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 {
onCompleteSetup: () => void
visible: boolean
bucket: Bucket
bucket?: Bucket
buckets: Bucket[]
startingType?: DataLoaderType
startingStep?: number
startingSubstep?: Substep
}
interface DispatchProps {
@ -116,24 +120,22 @@ class DataLoadersWizard extends PureComponent<Props> {
public stepSkippable = [true, true, true]
public componentDidMount() {
const {bucket} = this.props
if (bucket) {
const {organization, organizationID, name, id} = bucket
this.props.onSetBucketInfo(organization, organizationID, name, id)
}
this.handleSetBucketInfo()
this.handleSetStartingValues()
}
public componentDidUpdate(prevProps: Props) {
const {bucket} = this.props
const {bucket, buckets} = this.props
const prevID = _.get(prevProps.bucket, 'id', '')
const curID = _.get(bucket, 'id', '')
const prevBucket = prevProps.bucket || prevProps.buckets[0]
const curBucket = bucket || buckets[0]
const prevID = _.get(prevBucket, 'id', '')
const curID = _.get(curBucket, 'id', '')
const isDifferentBucket = prevID !== curID
if (isDifferentBucket && bucket) {
const {organization, organizationID, name, id} = bucket
this.props.onSetBucketInfo(organization, organizationID, name, id)
if (isDifferentBucket && curBucket) {
this.handleSetBucketInfo()
}
}
@ -156,6 +158,7 @@ class DataLoadersWizard extends PureComponent<Props> {
visible,
bucket,
username,
buckets,
} = this.props
return (
@ -194,6 +197,7 @@ class DataLoadersWizard extends PureComponent<Props> {
onSetConfigArrayValue={onSetConfigArrayValue}
org={_.get(bucket, 'organization', '')}
username={username}
buckets={buckets}
/>
</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 = () => {
this.props.onClearDataLoaders()
this.props.onClearSteps()
this.props.onCompleteSetup()
this.handleSetStartingValues()
}
private get progressHeader(): JSX.Element {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ import {OverlayTechnology, IndexList} from 'src/clockface'
// Types
import {OverlayState} from 'src/types/v2'
import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard'
import {DataLoaderStep, DataLoaderType} from 'src/types/v2/dataLoaders'
interface Props {
buckets: PrettyBucket[]
@ -68,6 +69,10 @@ export default class BucketList extends PureComponent<Props, State> {
visible={this.isDataLoadersWizardVisible}
onCompleteSetup={this.handleDismissDataLoaders}
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
}
export default class BucketList extends PureComponent<Props> {
export default class CollectorList extends PureComponent<Props> {
public render() {
const {emptyState} = this.props

View File

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

View File

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

View File

@ -14,40 +14,83 @@ import {
ComponentSize,
EmptyState,
} from 'src/clockface'
import DataLoadersWizard from 'src/dataLoaders/components/DataLoadersWizard'
// Decorators
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 {
scrapers: ScraperTargetResponses
onChange: () => void
orgName: string
buckets: Bucket[]
}
interface State {
overlayState: OverlayState
}
@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() {
const {scrapers} = this.props
const {scrapers, buckets} = this.props
return (
<>
<TabbedPageHeader>
<h1>Scrapers</h1>
<Button
text="Create Scraper"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
{this.createScraperButton}
</TabbedPageHeader>
<ScraperList
scrapers={scrapers}
emptyState={this.emptyState}
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 {
const {orgName} = this.props
return (
@ -55,11 +98,7 @@ export default class OrgOptions extends PureComponent<Props> {
<EmptyState.Text
text={`${orgName} does not own any scrapers, why not create one?`}
/>
<Button
text="Create Scraper"
icon={IconFont.Plus}
color={ComponentColor.Primary}
/>
{this.createScraperButton}
</EmptyState>
)
}

View File

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

View File

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