Merge pull request #11259 from influxdata/feat/add-collector-wizard

feat(ui/dataLoaders): Add new collector from collectors tab
pull/11208/head
Iris Scholten 2019-01-17 17:40:23 -08:00 committed by GitHub
commit 51b3248303
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 213 additions and 68 deletions

View File

@ -44,6 +44,7 @@ import {
DataLoadersState,
DataLoaderType,
Substep,
DataLoaderStep,
} from 'src/types/v2/dataLoaders'
import {Notification, NotificationFunc} from 'src/types'
import {AppState} from 'src/types/v2'
@ -105,6 +106,7 @@ interface StateProps {
currentStepIndex: number
substep: Substep
username: string
selectedBucket: string
}
type Props = OwnProps & StateProps & DispatchProps
@ -158,7 +160,9 @@ class DataLoadersWizard extends PureComponent<Props> {
visible,
bucket,
username,
onSetBucketInfo,
buckets,
selectedBucket,
} = this.props
return (
@ -184,6 +188,7 @@ class DataLoadersWizard extends PureComponent<Props> {
currentStepIndex={currentStepIndex}
onboardingStepProps={this.stepProps}
bucketName={_.get(bucket, 'name', '')}
selectedBucket={selectedBucket}
dataLoaders={dataLoaders}
onSetDataLoadersType={onSetDataLoadersType}
onUpdateTelegrafPluginConfig={onUpdateTelegrafPluginConfig}
@ -195,6 +200,7 @@ class DataLoadersWizard extends PureComponent<Props> {
onAddPluginBundle={onAddPluginBundle}
onRemovePluginBundle={onRemovePluginBundle}
onSetConfigArrayValue={onSetConfigArrayValue}
onSetBucketInfo={onSetBucketInfo}
org={_.get(bucket, 'organization', '')}
username={username}
buckets={buckets}
@ -274,7 +280,7 @@ class DataLoadersWizard extends PureComponent<Props> {
const {onSetSubstepIndex, onSetActiveTelegrafPlugin} = this.props
onSetActiveTelegrafPlugin('')
onSetSubstepIndex(0, 'streaming')
onSetSubstepIndex(DataLoaderStep.Select, 'streaming')
}
private handleClickSideBarTab = (telegrafPluginID: string) => {
@ -291,7 +297,7 @@ class DataLoadersWizard extends PureComponent<Props> {
0
)
onSetSubstepIndex(1, index)
onSetSubstepIndex(DataLoaderStep.Configure, index)
onSetActiveTelegrafPlugin(telegrafPluginID)
}
@ -332,7 +338,7 @@ const mstp = ({
links,
dataLoading: {
dataLoaders,
steps: {stepStatuses, currentStep, substep},
steps: {stepStatuses, currentStep, substep, bucket},
},
me: {name},
}: AppState): StateProps => ({
@ -342,6 +348,7 @@ const mstp = ({
currentStepIndex: currentStep,
substep,
username: name,
selectedBucket: bucket,
})
const mdtp: DispatchProps = {

View File

@ -21,6 +21,7 @@ import {
setPluginConfiguration,
setConfigArrayValue,
} from 'src/onboarding/actions/dataLoaders'
import {setBucketInfo} from 'src/onboarding/actions/steps'
// Types
import {DataLoadersState, DataLoaderStep} from 'src/types/v2/dataLoaders'
@ -43,8 +44,10 @@ interface Props {
onAddPluginBundle: typeof addPluginBundleWithPlugins
onRemovePluginBundle: typeof removePluginBundleWithPlugins
onSetConfigArrayValue: typeof setConfigArrayValue
onSetBucketInfo: typeof setBucketInfo
org: string
username: string
selectedBucket: string
}
@ErrorHandling
@ -68,6 +71,8 @@ class StepSwitcher extends PureComponent<Props> {
username,
org,
buckets,
onSetBucketInfo,
selectedBucket,
} = this.props
switch (currentStepIndex) {
@ -77,10 +82,13 @@ class StepSwitcher extends PureComponent<Props> {
{...onboardingStepProps}
{...dataLoaders}
onSetDataLoadersType={onSetDataLoadersType}
buckets={buckets}
bucket={bucketName}
selectedBucket={selectedBucket}
onSetActiveTelegrafPlugin={onSetActiveTelegrafPlugin}
onAddPluginBundle={onAddPluginBundle}
onRemovePluginBundle={onRemovePluginBundle}
onSetBucketInfo={onSetBucketInfo}
/>
)
case DataLoaderStep.Configure:

View File

@ -70,6 +70,11 @@
flex: 1 0 calc(100% - 240px);
}
.wizard-step--group {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.onboarding-step {
min-width: 100%;
@ -122,8 +127,8 @@
}
.wizard-step--filter {
float:right;
padding-bottom: 15px;
align-self: flex-end;
margin: 20px;
}
/* Buttons */
@ -351,4 +356,4 @@
.wizard--bookend-step {
width: 70%;
}
}
}

View File

@ -5,10 +5,10 @@ import _ from 'lodash'
import {
writeLineProtocol,
createTelegrafConfig,
getTelegrafConfigs,
updateTelegrafConfig,
createScraperTarget,
updateScraperTarget,
getTelegrafConfig,
} from 'src/onboarding/apis/index'
// Utils
@ -284,13 +284,11 @@ export const createOrUpdateTelegrafConfigAsync = (authToken: string) => async (
) => {
const {
dataLoading: {
dataLoaders: {telegrafPlugins},
dataLoaders: {telegrafPlugins, telegrafConfigID},
steps: {org, bucket, orgID},
},
} = getState()
const telegrafConfigsFromServer = await getTelegrafConfigs(orgID)
const influxDB2Out = {
name: TelegrafPluginOutputInfluxDBV2.NameEnum.InfluxdbV2,
type: TelegrafPluginOutputInfluxDBV2.TypeEnum.Output,
@ -309,6 +307,15 @@ export const createOrUpdateTelegrafConfigAsync = (authToken: string) => async (
}
})
if (telegrafConfigID) {
const telegrafConfig = await getTelegrafConfig(telegrafConfigID)
const id = _.get(telegrafConfig, '0.id', '')
await updateTelegrafConfig(id, {...telegrafConfig, plugins})
dispatch(setTelegrafConfigID(id))
return
}
const telegrafRequest: TelegrafRequest = {
name: 'new config',
agent: {collectionInterval: DEFAULT_COLLECTION_INTERVAL},
@ -316,14 +323,6 @@ export const createOrUpdateTelegrafConfigAsync = (authToken: string) => async (
plugins,
}
if (telegrafConfigsFromServer.length) {
const id = _.get(telegrafConfigsFromServer, '0.id', '')
await updateTelegrafConfig(id, telegrafRequest)
dispatch(setTelegrafConfigID(id))
return
}
const created = await createTelegrafConfig(telegrafRequest)
dispatch(setTelegrafConfigID(created.id))
}

View File

@ -91,6 +91,18 @@ export const trySources = async (): Promise<boolean> => {
}
}
export const getTelegrafConfig = async (
telegrafConfigID
): Promise<Telegraf> => {
try {
const response = await telegrafsAPI.telegrafsTelegrafIDGet(telegrafConfigID)
return response.data
} catch (error) {
console.error(error)
return null
}
}
export const getTelegrafConfigs = async (org: string): Promise<Telegraf[]> => {
try {
const data = await telegrafsAPI.telegrafsGet(org)

View File

@ -36,6 +36,9 @@ const setup = (override = {}) => {
location: null,
router: null,
routes: [],
selectedBucket: '',
onSetBucketInfo: jest.fn(),
buckets: [],
...override,
}

View File

@ -17,6 +17,7 @@ import {
addPluginBundleWithPlugins,
removePluginBundleWithPlugins,
} from 'src/onboarding/actions/dataLoaders'
import {setBucketInfo} from 'src/onboarding/actions/steps'
// Constants
import {StepStatus} from 'src/clockface/constants/wizard'
@ -28,8 +29,9 @@ import {
DataLoaderType,
BundleName,
} from 'src/types/v2/dataLoaders'
import {Bucket} from 'src/api'
export interface OwnProps extends DataLoaderStepProps {
export interface Props extends DataLoaderStepProps {
bucket: string
telegrafPlugins: TelegrafPlugin[]
pluginBundles: BundleName[]
@ -39,22 +41,13 @@ export interface OwnProps extends DataLoaderStepProps {
onSetDataLoadersType: (type: DataLoaderType) => void
onSetActiveTelegrafPlugin: typeof setActiveTelegrafPlugin
onSetStepStatus: (index: number, status: StepStatus) => void
}
type Props = OwnProps
interface State {
showStreamingSources: boolean
onSetBucketInfo: typeof setBucketInfo
buckets: Bucket[]
selectedBucket: string
}
@ErrorHandling
export class SelectDataSourceStep extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {showStreamingSources: false}
}
export class SelectDataSourceStep extends PureComponent<Props> {
public componentDidMount() {
if (this.isStreaming && this.props.type !== DataLoaderType.Streaming) {
this.props.onSetDataLoadersType(DataLoaderType.Streaming)
@ -121,6 +114,10 @@ export class SelectDataSourceStep extends PureComponent<Props, State> {
pluginBundles={this.props.pluginBundles}
telegrafPlugins={this.props.telegrafPlugins}
onTogglePluginBundle={this.handleTogglePluginBundle}
buckets={this.props.buckets}
bucket={this.props.bucket}
selectedBucket={this.props.selectedBucket}
onSelectBucket={this.handleSelectBucket}
/>
)
}
@ -132,6 +129,12 @@ export class SelectDataSourceStep extends PureComponent<Props, State> {
)
}
private handleSelectBucket = (bucket: Bucket) => {
const {organization, organizationID, id, name} = bucket
this.props.onSetBucketInfo(organization, organizationID, name, id)
}
private get showSkip(): boolean {
const {telegrafPlugins} = this.props
if (telegrafPlugins.length < 1) {

View File

@ -15,6 +15,10 @@ const setup = (override = {}) => {
telegrafPlugins: [],
pluginBundles: [],
onTogglePluginBundle: jest.fn(),
buckets: [],
bucket: '',
selectedBucket: '',
onSelectBucket: jest.fn(),
...override,
}

View File

@ -1,11 +1,21 @@
// Libraries
import React, {PureComponent, ChangeEvent} from 'react'
import uuid from 'uuid'
import _ from 'lodash'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import CardSelectCard from 'src/clockface/components/card_select/CardSelectCard'
import {GridSizer, Input, IconFont, ComponentSize} from 'src/clockface'
import {
GridSizer,
Input,
IconFont,
ComponentSize,
Dropdown,
FormElement,
Grid,
Columns,
} from 'src/clockface'
// Constants
import {
@ -15,11 +25,16 @@ import {
// Types
import {TelegrafPlugin, BundleName} from 'src/types/v2/dataLoaders'
import {Bucket} from 'src/api'
export interface Props {
buckets: Bucket[]
bucket: string
selectedBucket: string
pluginBundles: BundleName[]
telegrafPlugins: TelegrafPlugin[]
onTogglePluginBundle: (telegrafPlugin: string, isSelected: boolean) => void
onSelectBucket: (bucket: Bucket) => void
}
interface State {
@ -59,17 +74,31 @@ class StreamingSelector extends PureComponent<Props, State> {
return (
<div className="wizard-step--grid-container">
<div className="wizard-step--filter">
<Input
size={ComponentSize.Medium}
icon={IconFont.Search}
widthPixels={290}
value={searchTerm}
onBlur={this.handleFilterBlur}
onChange={this.handleFilterChange}
placeholder="Filter Plugins..."
/>
</div>
<Grid.Row>
<Grid.Column widthSM={Columns.Five}>
<FormElement label="Bucket">
<Dropdown
selectedID={this.selectedBucketID}
onChange={this.handleSelectBucket}
>
{this.dropdownBuckets}
</Dropdown>
</FormElement>
</Grid.Column>
<Grid.Column widthSM={Columns.Five} offsetSM={Columns.Two}>
<FormElement label="">
<Input
customClass={'wizard-step--filter'}
size={ComponentSize.Small}
icon={IconFont.Search}
value={searchTerm}
onBlur={this.handleFilterBlur}
onChange={this.handleFilterChange}
placeholder="Filter Plugins..."
/>
</FormElement>
</Grid.Column>
</Grid.Row>
<GridSizer
wait={ANIMATION_LENGTH}
recalculateFlag={gridSizerUpdateFlag}
@ -92,6 +121,28 @@ class StreamingSelector extends PureComponent<Props, State> {
)
}
private handleSelectBucket = (bucketName: string) => {
const bucket = this.props.buckets.find(b => b.name === bucketName)
this.props.onSelectBucket(bucket)
}
private get selectedBucketID(): string {
const {bucket, selectedBucket, buckets} = this.props
return selectedBucket || bucket || _.get(buckets, '0.name', '')
}
private get dropdownBuckets(): JSX.Element[] {
const {buckets} = this.props
return buckets.map(b => (
<Dropdown.Item key={b.name} value={b.name} id={b.name}>
{b.name}
</Dropdown.Item>
))
}
private get filteredBundles(): BundleName[] {
const {searchTerm} = this.state

View File

@ -17,42 +17,56 @@ import {
Grid,
Columns,
} from 'src/clockface'
import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard'
// APIS
import {
getTelegrafConfigTOML,
deleteTelegrafConfig,
} from 'src/organizations/apis/index'
// Actions
import * as NotificationsActions from 'src/types/actions/notifications'
import {notify} from 'src/shared/actions/notifications'
// Constants
import {getTelegrafConfigFailed} from 'src/shared/copy/v2/notifications'
// Decorators
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Telegraf} from 'src/api'
import {
getTelegrafConfigTOML,
deleteTelegrafConfig,
} from 'src/organizations/apis/index'
import {notify} from 'src/shared/actions/notifications'
// Types
import {Telegraf, Bucket} from 'src/api'
import {DataLoaderType, DataLoaderStep} from 'src/types/v2/dataLoaders'
import {OverlayState} from 'src/types/v2'
interface Props {
collectors: Telegraf[]
onChange: () => void
notify: NotificationsActions.PublishNotificationActionCreator
orgName: string
buckets: Bucket[]
}
interface State {
overlayState: OverlayState
}
@ErrorHandling
export default class Collectors extends PureComponent<Props> {
export default class Collectors extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {overlayState: OverlayState.Closed}
}
public render() {
const {collectors} = this.props
const {collectors, buckets} = this.props
return (
<>
<TabbedPageHeader>
<h1>Telegraf Configurations</h1>
<Button
text="Create Configuration"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
{this.createButton}
</TabbedPageHeader>
<Grid>
<Grid.Row>
@ -74,9 +88,42 @@ export default class Collectors extends PureComponent<Props> {
</Grid.Column>
</Grid.Row>
</Grid>
<DataLoadersWizard
visible={this.isOverlayVisible}
onCompleteSetup={this.handleDismissDataLoaders}
startingType={DataLoaderType.Streaming}
startingStep={DataLoaderStep.Select}
startingSubstep={'streaming'}
buckets={buckets}
/>
</>
)
}
private get isOverlayVisible(): boolean {
return this.state.overlayState === OverlayState.Open
}
private get createButton(): JSX.Element {
return (
<Button
text="Create Configuration"
icon={IconFont.Plus}
color={ComponentColor.Primary}
onClick={this.handleAddScraper}
/>
)
}
private handleAddScraper = () => {
this.setState({overlayState: OverlayState.Open})
}
private handleDismissDataLoaders = () => {
this.setState({overlayState: OverlayState.Closed})
this.props.onChange()
}
private get emptyState(): JSX.Element {
const {orgName} = this.props
@ -86,11 +133,7 @@ export default class Collectors extends PureComponent<Props> {
text={`${orgName} does not own any Telegraf Configurations , why not create one?`}
highlightWords={['Telegraf', 'Configurations']}
/>
<Button
text="Create Configuration"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
{this.createButton}
</EmptyState>
)
}

View File

@ -165,12 +165,22 @@ class OrganizationView extends PureComponent<Props> {
>
{(collectors, loading, fetch) => (
<Spinner loading={loading}>
<Collectors
collectors={collectors}
onChange={fetch}
notify={notify}
orgName={org.name}
/>
<GetOrgResources<Bucket[]>
organization={org}
fetcher={getBuckets}
>
{(buckets, loading) => (
<Spinner loading={loading}>
<Collectors
collectors={collectors}
onChange={fetch}
notify={notify}
buckets={buckets}
orgName={org.name}
/>
</Spinner>
)}
</GetOrgResources>
</Spinner>
)}
</GetOrgResources>