feat(ui/dataLoaders): Add new scraper from scrapers tab

pull/11235/head
Iris Scholten 2019-01-17 10:48:37 -08:00
parent b031e22003
commit d7f18beaee
14 changed files with 169 additions and 49 deletions

View File

@ -69,7 +69,11 @@ export interface DataLoaderStepProps {
interface OwnProps { interface OwnProps {
onCompleteSetup: () => void onCompleteSetup: () => void
visible: boolean visible: boolean
bucket: Bucket bucket?: Bucket
buckets: Bucket[]
startingType?: DataLoaderType
startingStep?: number
startingSubstep?: Substep
} }
interface DispatchProps { interface DispatchProps {
@ -116,24 +120,22 @@ class DataLoadersWizard extends PureComponent<Props> {
public stepSkippable = [true, true, true] public stepSkippable = [true, true, true]
public componentDidMount() { public componentDidMount() {
const {bucket} = this.props this.handleSetBucketInfo()
if (bucket) { this.handleSetStartingValues()
const {organization, organizationID, name, id} = bucket
this.props.onSetBucketInfo(organization, organizationID, name, id)
}
} }
public componentDidUpdate(prevProps: Props) { public componentDidUpdate(prevProps: Props) {
const {bucket} = this.props const {bucket, buckets} = this.props
const prevID = _.get(prevProps.bucket, 'id', '') const prevBucket = prevProps.bucket || prevProps.buckets[0]
const curID = _.get(bucket, 'id', '') const curBucket = bucket || buckets[0]
const prevID = _.get(prevBucket, 'id', '')
const curID = _.get(curBucket, 'id', '')
const isDifferentBucket = prevID !== curID const isDifferentBucket = prevID !== curID
if (isDifferentBucket && bucket) { if (isDifferentBucket && curBucket) {
const {organization, organizationID, name, id} = bucket this.handleSetBucketInfo()
this.props.onSetBucketInfo(organization, organizationID, name, id)
} }
} }
@ -156,6 +158,7 @@ class DataLoadersWizard extends PureComponent<Props> {
visible, visible,
bucket, bucket,
username, username,
buckets,
} = this.props } = this.props
return ( return (
@ -194,6 +197,7 @@ class DataLoadersWizard extends PureComponent<Props> {
onSetConfigArrayValue={onSetConfigArrayValue} onSetConfigArrayValue={onSetConfigArrayValue}
org={_.get(bucket, 'organization', '')} org={_.get(bucket, 'organization', '')}
username={username} username={username}
buckets={buckets}
/> />
</div> </div>
</div> </div>
@ -201,10 +205,43 @@ class DataLoadersWizard extends PureComponent<Props> {
) )
} }
private handleSetBucketInfo = () => {
const {bucket, buckets} = this.props
if (bucket || buckets.length) {
const b = bucket || buckets[0]
const {organization, organizationID, name, id} = b
this.props.onSetBucketInfo(organization, organizationID, name, id)
}
}
private handleSetStartingValues = () => {
const {startingStep, startingType, startingSubstep} = this.props
const hasStartingStep = startingStep || startingStep === 0
const hasStartingSubstep = startingSubstep || startingSubstep === 0
const hasStartingType =
startingType || startingType === DataLoaderType.Empty
if (hasStartingType) {
this.props.onSetDataLoadersType(startingType)
}
if (hasStartingSubstep) {
this.props.onSetSubstepIndex(
hasStartingStep ? startingStep : 0,
startingSubstep
)
} else if (hasStartingStep) {
this.props.onSetCurrentStepIndex(startingStep)
}
}
private handleDismiss = () => { private handleDismiss = () => {
this.props.onClearDataLoaders() this.props.onClearDataLoaders()
this.props.onClearSteps() this.props.onClearSteps()
this.props.onCompleteSetup() this.props.onCompleteSetup()
this.handleSetStartingValues()
} }
private get progressHeader(): JSX.Element { private get progressHeader(): JSX.Element {

View File

@ -23,8 +23,9 @@ import {
} from 'src/onboarding/actions/dataLoaders' } from 'src/onboarding/actions/dataLoaders'
// Types // Types
import {DataLoadersState} from 'src/types/v2/dataLoaders' import {DataLoadersState, DataLoaderStep} from 'src/types/v2/dataLoaders'
import {DataLoaderStepProps} from 'src/dataLoaders/components/DataLoadersWizard' import {DataLoaderStepProps} from 'src/dataLoaders/components/DataLoadersWizard'
import {Bucket} from 'src/api'
interface Props { interface Props {
onboardingStepProps: DataLoaderStepProps onboardingStepProps: DataLoaderStepProps
@ -35,6 +36,7 @@ interface Props {
onSetActiveTelegrafPlugin: typeof setActiveTelegrafPlugin onSetActiveTelegrafPlugin: typeof setActiveTelegrafPlugin
onSetPluginConfiguration: typeof setPluginConfiguration onSetPluginConfiguration: typeof setPluginConfiguration
bucketName: string bucketName: string
buckets: Bucket[]
dataLoaders: DataLoadersState dataLoaders: DataLoadersState
currentStepIndex: number currentStepIndex: number
onSaveTelegrafConfig: typeof createOrUpdateTelegrafConfigAsync onSaveTelegrafConfig: typeof createOrUpdateTelegrafConfigAsync
@ -65,10 +67,11 @@ class StepSwitcher extends PureComponent<Props> {
bucketName, bucketName,
username, username,
org, org,
buckets,
} = this.props } = this.props
switch (currentStepIndex) { switch (currentStepIndex) {
case 0: case DataLoaderStep.Select:
return ( return (
<SelectDataSourceStep <SelectDataSourceStep
{...onboardingStepProps} {...onboardingStepProps}
@ -80,11 +83,12 @@ class StepSwitcher extends PureComponent<Props> {
onRemovePluginBundle={onRemovePluginBundle} onRemovePluginBundle={onRemovePluginBundle}
/> />
) )
case 1: case DataLoaderStep.Configure:
return ( return (
<ConfigureDataSourceStep <ConfigureDataSourceStep
{...onboardingStepProps} {...onboardingStepProps}
{...dataLoaders} {...dataLoaders}
buckets={buckets}
bucket={bucketName} bucket={bucketName}
username={username} username={username}
org={org} org={org}
@ -96,7 +100,7 @@ class StepSwitcher extends PureComponent<Props> {
onSetConfigArrayValue={onSetConfigArrayValue} onSetConfigArrayValue={onSetConfigArrayValue}
/> />
) )
case 2: case DataLoaderStep.Verify:
return ( return (
<VerifyDataStep <VerifyDataStep
{...onboardingStepProps} {...onboardingStepProps}

View File

@ -416,11 +416,11 @@ export const saveScraperTarget = () => async (
getState: GetState getState: GetState
) => { ) => {
const { const {
onboarding: {bucketID, orgID},
dataLoading: { dataLoading: {
dataLoaders: { dataLoaders: {
scraperTarget: {url, id}, scraperTarget: {url, id},
}, },
steps: {bucketID, orgID},
}, },
} = getState() } = getState()

View File

@ -33,6 +33,7 @@ const setup = (override = {}) => {
bucket: '', bucket: '',
org: '', org: '',
username: '', username: '',
buckets: [],
...override, ...override,
} }

View File

@ -26,6 +26,7 @@ import {
DataLoaderType, DataLoaderType,
ConfigurationState, ConfigurationState,
} from 'src/types/v2/dataLoaders' } from 'src/types/v2/dataLoaders'
import {Bucket} from 'src/api'
export interface OwnProps extends DataLoaderStepProps { export interface OwnProps extends DataLoaderStepProps {
telegrafPlugins: TelegrafPlugin[] telegrafPlugins: TelegrafPlugin[]
@ -39,6 +40,7 @@ export interface OwnProps extends DataLoaderStepProps {
bucket: string bucket: string
org: string org: string
username: string username: string
buckets: Bucket[]
} }
type Props = OwnProps type Props = OwnProps
@ -61,11 +63,13 @@ export class ConfigureDataSourceStep extends PureComponent<Props> {
bucket, bucket,
org, org,
username, username,
buckets,
} = this.props } = this.props
return ( return (
<div className="onboarding-step wizard--skippable"> <div className="onboarding-step wizard--skippable">
<ConfigureDataSourceSwitcher <ConfigureDataSourceSwitcher
buckets={buckets}
bucket={bucket} bucket={bucket}
org={org} org={org}
username={username} username={username}

View File

@ -19,6 +19,7 @@ import {
// Types // Types
import {TelegrafPlugin, DataLoaderType} from 'src/types/v2/dataLoaders' import {TelegrafPlugin, DataLoaderType} from 'src/types/v2/dataLoaders'
import {Bucket} from 'src/api'
export interface Props { export interface Props {
telegrafPlugins: TelegrafPlugin[] telegrafPlugins: TelegrafPlugin[]
@ -27,6 +28,7 @@ export interface Props {
onAddConfigValue: typeof addConfigValue onAddConfigValue: typeof addConfigValue
onRemoveConfigValue: typeof removeConfigValue onRemoveConfigValue: typeof removeConfigValue
dataLoaderType: DataLoaderType dataLoaderType: DataLoaderType
buckets: Bucket[]
bucket: string bucket: string
org: string org: string
username: string username: string
@ -52,6 +54,7 @@ class ConfigureDataSourceSwitcher extends PureComponent<Props> {
onClickNext, onClickNext,
onClickPrevious, onClickPrevious,
onClickSkip, onClickSkip,
buckets,
} = this.props } = this.props
switch (dataLoaderType) { switch (dataLoaderType) {
@ -85,6 +88,7 @@ class ConfigureDataSourceSwitcher extends PureComponent<Props> {
onClickNext={onClickNext} onClickNext={onClickNext}
onClickBack={onClickPrevious} onClickBack={onClickPrevious}
onClickSkip={onClickSkip} onClickSkip={onClickSkip}
buckets={buckets}
/> />
) )
case DataLoaderType.CSV: case DataLoaderType.CSV:

View File

@ -6,6 +6,7 @@ import {connect} from 'react-redux'
import {Form} from 'src/clockface' import {Form} from 'src/clockface'
import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar'
import OnboardingButtons from 'src/onboarding/components/OnboardingButtons' import OnboardingButtons from 'src/onboarding/components/OnboardingButtons'
import ScraperTarget from 'src/onboarding/components/configureStep/ScraperTarget'
// Actions // Actions
import { import {
@ -13,13 +14,16 @@ import {
setScraperTargetURL, setScraperTargetURL,
saveScraperTarget, saveScraperTarget,
} from 'src/onboarding/actions/dataLoaders' } from 'src/onboarding/actions/dataLoaders'
// Types
import {Bucket} from 'src/api'
import {AppState} from 'src/types/v2/index' import {AppState} from 'src/types/v2/index'
import ScraperTarget from 'src/onboarding/components/configureStep/ScraperTarget'
interface OwnProps { interface OwnProps {
onClickNext: () => void onClickNext: () => void
onClickBack: () => void onClickBack: () => void
onClickSkip: () => void onClickSkip: () => void
buckets: Bucket[]
} }
interface DispatchProps { interface DispatchProps {
@ -29,7 +33,7 @@ interface DispatchProps {
} }
interface StateProps { interface StateProps {
bucket: string scraperBucket: string
url: string url: string
currentBucket: string currentBucket: string
} }
@ -38,15 +42,21 @@ type Props = OwnProps & DispatchProps & StateProps
export class Scraping extends PureComponent<Props> { export class Scraping extends PureComponent<Props> {
public componentDidMount() { public componentDidMount() {
const {bucket, currentBucket, onSetScraperTargetBucket} = this.props const {
if (!bucket) { buckets,
onSetScraperTargetBucket(currentBucket) scraperBucket,
currentBucket,
onSetScraperTargetBucket,
} = this.props
if (!scraperBucket) {
onSetScraperTargetBucket(currentBucket || buckets[0].name)
} }
} }
public render() { public render() {
const { const {
bucket, scraperBucket,
onClickBack, onClickBack,
onClickSkip, onClickSkip,
onSetScraperTargetURL, onSetScraperTargetURL,
@ -64,7 +74,7 @@ export class Scraping extends PureComponent<Props> {
and to write to a bucket and to write to a bucket
</h5> </h5>
<ScraperTarget <ScraperTarget
bucket={bucket} bucket={scraperBucket}
buckets={this.buckets} buckets={this.buckets}
onSelectBucket={this.handleSelectBucket} onSelectBucket={this.handleSelectBucket}
onChangeURL={onSetScraperTargetURL} onChangeURL={onSetScraperTargetURL}
@ -85,9 +95,9 @@ export class Scraping extends PureComponent<Props> {
} }
private get buckets(): string[] { private get buckets(): string[] {
const {currentBucket} = this.props const {buckets} = this.props
return currentBucket ? [currentBucket] : [] return buckets.map(b => b.name)
} }
private handleSelectBucket = (bucket: string) => { private handleSelectBucket = (bucket: string) => {
@ -109,7 +119,7 @@ const mstp = ({
}: AppState): StateProps => { }: AppState): StateProps => {
return { return {
currentBucket: bucket, currentBucket: bucket,
bucket: scraperTarget.bucket, scraperBucket: scraperTarget.bucket,
url: scraperTarget.url, url: scraperTarget.url,
} }
} }

View File

@ -9,6 +9,7 @@ import {OverlayTechnology, IndexList} from 'src/clockface'
// Types // Types
import {OverlayState} from 'src/types/v2' import {OverlayState} from 'src/types/v2'
import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard' import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard'
import {DataLoaderStep, DataLoaderType} from 'src/types/v2/dataLoaders'
interface Props { interface Props {
buckets: PrettyBucket[] buckets: PrettyBucket[]
@ -68,6 +69,10 @@ export default class BucketList extends PureComponent<Props, State> {
visible={this.isDataLoadersWizardVisible} visible={this.isDataLoadersWizardVisible}
onCompleteSetup={this.handleDismissDataLoaders} onCompleteSetup={this.handleDismissDataLoaders}
bucket={this.bucket} bucket={this.bucket}
buckets={buckets}
startingStep={DataLoaderStep.Select}
startingSubstep={0}
startingType={DataLoaderType.Empty}
/> />
</> </>
) )

View File

@ -17,7 +17,7 @@ interface Props {
onDelete: (telegrafID: string) => void onDelete: (telegrafID: string) => void
} }
export default class BucketList extends PureComponent<Props> { export default class CollectorList extends PureComponent<Props> {
public render() { public render() {
const {emptyState} = this.props const {emptyState} = this.props

View File

@ -25,7 +25,7 @@ export default class ScraperList extends PureComponent<Props> {
<> <>
<IndexList> <IndexList>
<IndexList.Header> <IndexList.Header>
<IndexList.HeaderCell columnName="Name" width="50%" /> <IndexList.HeaderCell columnName="URL" width="50%" />
<IndexList.HeaderCell columnName="Bucket" width="50%" /> <IndexList.HeaderCell columnName="Bucket" width="50%" />
</IndexList.Header> </IndexList.Header>
<IndexList.Body columnCount={3} emptyState={emptyState}> <IndexList.Body columnCount={3} emptyState={emptyState}>

View File

@ -21,7 +21,7 @@ export default class ScraperRow extends PureComponent<Props> {
return ( return (
<> <>
<IndexList.Row> <IndexList.Row>
<IndexList.Cell>{scraper.name}</IndexList.Cell> <IndexList.Cell>{scraper.url}</IndexList.Cell>
<IndexList.Cell>{scraper.bucket}</IndexList.Cell> <IndexList.Cell>{scraper.bucket}</IndexList.Cell>
<IndexList.Cell revealOnHover={true} alignment={Alignment.Right}> <IndexList.Cell revealOnHover={true} alignment={Alignment.Right}>
<ConfirmationButton <ConfirmationButton

View File

@ -14,40 +14,83 @@ import {
ComponentSize, ComponentSize,
EmptyState, EmptyState,
} from 'src/clockface' } from 'src/clockface'
import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard'
// Decorators // Decorators
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import {ScraperTargetResponses, ScraperTargetResponse} from 'src/api'
// Types
import {ScraperTargetResponses, ScraperTargetResponse, Bucket} from 'src/api'
import {OverlayState} from 'src/types/v2'
import {DataLoaderType, DataLoaderStep} from 'src/types/v2/dataLoaders'
interface Props { interface Props {
scrapers: ScraperTargetResponses scrapers: ScraperTargetResponses
onChange: () => void onChange: () => void
orgName: string orgName: string
buckets: Bucket[]
}
interface State {
overlayState: OverlayState
} }
@ErrorHandling @ErrorHandling
export default class OrgOptions extends PureComponent<Props> { export default class Scrapers extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {overlayState: OverlayState.Closed}
}
public render() { public render() {
const {scrapers} = this.props const {scrapers, buckets} = this.props
return ( return (
<> <>
<TabbedPageHeader> <TabbedPageHeader>
<h1>Scrapers</h1> <h1>Scrapers</h1>
<Button {this.createScraperButton}
text="Create Scraper"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
</TabbedPageHeader> </TabbedPageHeader>
<ScraperList <ScraperList
scrapers={scrapers} scrapers={scrapers}
emptyState={this.emptyState} emptyState={this.emptyState}
onDeleteScraper={this.handleDeleteScraper} onDeleteScraper={this.handleDeleteScraper}
/> />
<DataLoadersWizard
visible={this.isOverlayVisible}
onCompleteSetup={this.handleDismissDataLoaders}
startingType={DataLoaderType.Scraping}
startingStep={DataLoaderStep.Configure}
buckets={buckets}
/>
</> </>
) )
} }
private get isOverlayVisible(): boolean {
return this.state.overlayState === OverlayState.Open
}
private get createScraperButton(): JSX.Element {
return (
<Button
text="Create Scraper"
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 { private get emptyState(): JSX.Element {
const {orgName} = this.props const {orgName} = this.props
return ( return (
@ -55,11 +98,7 @@ export default class OrgOptions extends PureComponent<Props> {
<EmptyState.Text <EmptyState.Text
text={`${orgName} does not own any scrapers, why not create one?`} text={`${orgName} does not own any scrapers, why not create one?`}
/> />
<Button {this.createScraperButton}
text="Create Scraper"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
</EmptyState> </EmptyState>
) )
} }

View File

@ -180,11 +180,21 @@ class OrganizationView extends PureComponent<Props> {
> >
{(scrapers, loading, fetch) => ( {(scrapers, loading, fetch) => (
<Spinner loading={loading}> <Spinner loading={loading}>
<Scrapers <GetOrgResources<Bucket[]>
scrapers={scrapers} organization={org}
onChange={fetch} fetcher={getBuckets}
orgName={org.name} >
/> {(buckets, loading) => (
<Spinner loading={loading}>
<Scrapers
scrapers={scrapers}
onChange={fetch}
orgName={org.name}
buckets={buckets}
/>
</Spinner>
)}
</GetOrgResources>
</Spinner> </Spinner>
)} )}
</GetOrgResources> </GetOrgResources>

View File

@ -37,6 +37,12 @@ import {
import {RemoteDataState} from 'src/types' import {RemoteDataState} from 'src/types'
import {WritePrecision} from 'src/api' import {WritePrecision} from 'src/api'
export enum DataLoaderStep {
'Select',
'Configure',
'Verify',
}
interface ScraperTarget { interface ScraperTarget {
bucket: string bucket: string
url: string url: string