From 42ad3818ca1e6563a3b1b70e901a20382dc87dac Mon Sep 17 00:00:00 2001 From: Ariel Salem Date: Thu, 19 Mar 2020 20:01:42 -0700 Subject: [PATCH] fix(ui): resolved telegraf bucket dropdown bug and undefined token issue (#17363) --- CHANGELOG.md | 2 + ui/src/buckets/selectors/index.ts | 14 ++- ui/src/dataLoaders/actions/dataLoaders.ts | 100 +++++++++++------- .../collectorsWizard/CollectorsWizard.tsx | 4 +- .../verifyStep/TelegrafInstructions.tsx | 11 +- ui/src/resources/selectors/index.ts | 3 + ui/src/shared/components/CodeSnippet.scss | 4 + ui/src/shared/components/TokenCodeSnippet.tsx | 92 ++++++++++++++++ ui/src/shared/copy/notifications.ts | 5 + .../TelegrafInstructionsOverlay.tsx | 64 ++++------- 10 files changed, 216 insertions(+), 83 deletions(-) create mode 100644 ui/src/shared/components/TokenCodeSnippet.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 1051d4ec033..1455c34311d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,12 @@ 1. [17232](https://github.com/influxdata/influxdb/pull/17232): Allow dashboards to optionally be displayed in light mode 1. [17273](https://github.com/influxdata/influxdb/pull/17273): Add shell completions command for the influx cli 1. [17353](https://github.com/influxdata/influxdb/pull/17353): Make all pkg resources unique by metadata.name field +1. [17363](https://github.com/influxdata/influxdb/pull/17363): Telegraf config tokens can no longer be retrieved after creation, but new tokens can be created after a telegraf has been setup ### Bug Fixes 1. [17240](https://github.com/influxdata/influxdb/pull/17240): NodeJS logo displays properly in Firefox +1. [17363](https://github.com/influxdata/influxdb/pull/17363): Fixed telegraf configuration bugs where system buckets were appearing in the buckets dropdown ### UI Improvements diff --git a/ui/src/buckets/selectors/index.ts b/ui/src/buckets/selectors/index.ts index 030c40c98d2..7189dd17702 100644 --- a/ui/src/buckets/selectors/index.ts +++ b/ui/src/buckets/selectors/index.ts @@ -1,8 +1,20 @@ // Types -import {Bucket} from 'src/types' +import {AppState, Bucket, ResourceType} from 'src/types' + +// Selectors +import {getAll} from 'src/resources/selectors' export const SYSTEM = 'system' +export const getBucketByName = ( + state: AppState, + bucketName: string +): Bucket => { + const buckets = getAll(state, ResourceType.Buckets) + const bucket = buckets.find(b => b.name === bucketName) + return bucket +} + export const isSystemBucket = (type: string): boolean => type === SYSTEM const sortFunc = (a: Bucket, b: Bucket) => { diff --git a/ui/src/dataLoaders/actions/dataLoaders.ts b/ui/src/dataLoaders/actions/dataLoaders.ts index 17f5c1cac25..021e479fcf9 100644 --- a/ui/src/dataLoaders/actions/dataLoaders.ts +++ b/ui/src/dataLoaders/actions/dataLoaders.ts @@ -6,7 +6,7 @@ import {normalize} from 'normalizr' import {client} from 'src/utils/api' import {ScraperTargetRequest, PermissionResource} from '@influxdata/influx' import {createAuthorization} from 'src/authorizations/apis' -import {postWrite as apiPostWrite, postLabel as apiPostLabel} from 'src/client' +import {postWrite as apiPostWrite} from 'src/client' // Schemas import {authSchema} from 'src/schemas' @@ -14,8 +14,9 @@ import {telegrafSchema} from 'src/schemas/telegrafs' // Utils import {createNewPlugin} from 'src/dataLoaders/utils/pluginConfigs' -import {addLabelDefaults} from 'src/labels/utils' import {getDataLoaders, getSteps} from 'src/dataLoaders/selectors' +import {getBucketByName} from 'src/buckets/selectors' +import {getByID} from 'src/resources/selectors' import {getOrg} from 'src/organizations/selectors' // Constants @@ -37,13 +38,12 @@ import { import { GetState, RemoteDataState, - LabelProperties, Authorization, AuthEntities, + ResourceType, TelegrafEntities, Telegraf, } from 'src/types' -import {ILabel} from '@influxdata/influx' import { WritePrecision, TelegrafRequest, @@ -57,9 +57,10 @@ import {addTelegraf, editTelegraf} from 'src/telegrafs/actions/creators' import {addAuthorization} from 'src/authorizations/actions/creators' import {notify} from 'src/shared/actions/notifications' import { + readWriteCardinalityLimitReached, TelegrafConfigCreationError, TelegrafConfigCreationSuccess, - readWriteCardinalityLimitReached, + TokenCreationError, } from 'src/shared/copy/notifications' const DEFAULT_COLLECTION_INTERVAL = 10000 @@ -401,6 +402,61 @@ export const createOrUpdateTelegrafConfigAsync = () => async ( createTelegraf(dispatch, getState, plugins) } +export const generateTelegrafToken = (configID: string) => async ( + dispatch, + getState: GetState +) => { + try { + const state = getState() + const orgID = getOrg(state).id + const telegraf = getByID(state, ResourceType.Telegrafs, configID) + const bucketName = get(telegraf, 'metadata.buckets[0]', '') + if (bucketName === '') { + throw new Error( + 'A token cannot be generated without a corresponding bucket' + ) + } + const bucket = getBucketByName(state, bucketName) + + const permissions = [ + { + action: Permission.ActionEnum.Write, + resource: { + type: PermissionResource.TypeEnum.Buckets, + id: bucket.id, + orgID, + }, + }, + { + action: Permission.ActionEnum.Read, + resource: { + type: PermissionResource.TypeEnum.Telegrafs, + id: configID, + orgID, + }, + }, + ] + + const token = { + name: `${telegraf.name} token`, + orgID, + description: `WRITE ${bucketName} bucket / READ ${ + telegraf.name + } telegraf config`, + permissions, + } + + // create token + const createdToken = await createAuthorization(token) + + // add token to data loader state + dispatch(setToken(createdToken.token)) + } catch (error) { + console.error(error) + dispatch(notify(TokenCreationError)) + } +} + const createTelegraf = async (dispatch, getState: GetState, plugins) => { try { const state = getState() @@ -461,40 +517,8 @@ const createTelegraf = async (dispatch, getState: GetState, plugins) => { // add token to authorizations state dispatch(addAuthorization(normAuth)) - // create token label - const properties = { - color: '#FFFFFF', - description: `token for telegraf config: ${telegrafConfigName}`, - tokenID: createdToken.id, - } as LabelProperties // hack to make compiler work - - const resp = await apiPostLabel({ - data: { - orgID: org.id, - name: `@influxdata.token-${new Date().getTime()}`, // fix for https://github.com/influxdata/influxdb/issues/15730 - properties, - }, - }) - - if (resp.status !== 201) { - throw new Error(resp.data.message) - } - - const createdLabel = addLabelDefaults(resp.data.label) - - // add label to telegraf config - const label = await client.telegrafConfigs.addLabel( - tc.id, - createdLabel as ILabel - ) - - const config = { - ...tc, - labels: [label], - } - const normTelegraf = normalize( - config, + tc, telegrafSchema ) diff --git a/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx b/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx index 71f933bac42..50267b2a7b7 100644 --- a/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx +++ b/ui/src/dataLoaders/components/collectorsWizard/CollectorsWizard.tsx @@ -50,7 +50,9 @@ import {AppState, Bucket, Organization, ResourceType} from 'src/types' // Selectors import {getAll} from 'src/resources/selectors' import {getOrg} from 'src/organizations/selectors' -import {isSystemBucket} from 'src/buckets/selectors' + +// Utils +import {isSystemBucket} from 'src/buckets/constants' export interface CollectorsStepProps { currentStepIndex: number diff --git a/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx b/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx index 762b4324da2..c29dccba98b 100644 --- a/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx +++ b/ui/src/dataLoaders/components/verifyStep/TelegrafInstructions.tsx @@ -1,5 +1,6 @@ // Libraries import React, {PureComponent} from 'react' +import {Alert, ComponentColor, IconFont} from '@influxdata/clockface' import _ from 'lodash' // Decorator @@ -7,6 +8,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors' // Components import CodeSnippet from 'src/shared/components/CodeSnippet' +import TokenCodeSnippet from 'src/shared/components/TokenCodeSnippet' export interface Props { token: string @@ -17,7 +19,6 @@ export interface Props { class TelegrafInstructions extends PureComponent { public render() { const {token, configID} = this.props - const exportToken = `export INFLUX_TOKEN=${token || ''}` const configScript = `telegraf --config ${ this.origin }/api/v2/telegrafs/${configID || ''}` @@ -42,7 +43,13 @@ class TelegrafInstructions extends PureComponent { copy the following command to your terminal window to set an environment variable with your token.

- + {token && ( + + Make sure to copy your new personal access token now. You won’t be + able to see it again! + + )} +
3. Start Telegraf

Finally, you can run the following command to start the Telegraf agent diff --git a/ui/src/resources/selectors/index.ts b/ui/src/resources/selectors/index.ts index 41d0fd7dc2c..3ac9430a0ce 100644 --- a/ui/src/resources/selectors/index.ts +++ b/ui/src/resources/selectors/index.ts @@ -20,6 +20,9 @@ export const getAll = ( return allIDs.map(id => byID[id]) } +export const getToken = (state: AppState): string => + get(state, 'dataLoading.dataLoaders.token', '') || '' + export const getByID = ( {resources}: AppState, type: ResourceType, diff --git a/ui/src/shared/components/CodeSnippet.scss b/ui/src/shared/components/CodeSnippet.scss index fc800f82366..b4ac2eb9d1f 100644 --- a/ui/src/shared/components/CodeSnippet.scss +++ b/ui/src/shared/components/CodeSnippet.scss @@ -34,6 +34,10 @@ } } +.new-token--btn { + margin: $ix-marg-a $ix-marg-c; +} + .code-snippet--footer { background-color: rgba($g4-onyx, 0.5); display: flex; diff --git a/ui/src/shared/components/TokenCodeSnippet.tsx b/ui/src/shared/components/TokenCodeSnippet.tsx new file mode 100644 index 00000000000..65805c62912 --- /dev/null +++ b/ui/src/shared/components/TokenCodeSnippet.tsx @@ -0,0 +1,92 @@ +// Libraries +import React, {FC} from 'react' +import {connect} from 'react-redux' +import { + Button, + ComponentColor, + ComponentSize, + ComponentStatus, + IconFont, +} from '@influxdata/clockface' + +// Decorator +import {Notification} from 'src/types' + +// Components +import FancyScrollbar from 'src/shared/components/fancy_scrollbar/FancyScrollbar' +import CopyButton from 'src/shared/components/CopyButton' + +// Actions +import {generateTelegrafToken} from 'src/dataLoaders/actions/dataLoaders' + +export interface Props { + configID: string + token: string + onCopyText?: (text: string, status: boolean) => Notification + testID?: string + label: string +} + +interface DispatchProps { + onGenerateTelegrafToken: typeof generateTelegrafToken +} + +const TokenCodeSnippet: FC = ({ + token, + onCopyText, + testID, + label = 'Code Snippet', +}) => { + const handleRefreshClick = () => { + const {configID, onGenerateTelegrafToken} = this.props + onGenerateTelegrafToken(configID) + } + + return ( +

+ +
+
+            export INFLUX_TOKEN={token || ''}
+          
+
+
+
+
+ +
+ +
+
+ ) +} + +const mdtp: DispatchProps = { + onGenerateTelegrafToken: generateTelegrafToken, +} + +export default connect<{}, DispatchProps>( + null, + mdtp +)(TokenCodeSnippet) diff --git a/ui/src/shared/copy/notifications.ts b/ui/src/shared/copy/notifications.ts index 94fe6c40b9b..5974828523a 100644 --- a/ui/src/shared/copy/notifications.ts +++ b/ui/src/shared/copy/notifications.ts @@ -121,6 +121,11 @@ export const TelegrafConfigCreationError: Notification = { message: `Failed to save configurations`, } +export const TokenCreationError: Notification = { + ...defaultErrorNotification, + message: `Failed to create a new Telegraf Token`, +} + // Task Notifications // ---------------------------------------------------------------------------- export const addTaskLabelFailed = (): Notification => ({ diff --git a/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx b/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx index 0ac3a9c15c2..b66b9ee7753 100644 --- a/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx +++ b/ui/src/telegrafs/components/TelegrafInstructionsOverlay.tsx @@ -10,29 +10,29 @@ import {Overlay} from '@influxdata/clockface' import TelegrafInstructions from 'src/dataLoaders/components/verifyStep/TelegrafInstructions' import GetResources from 'src/resources/components/GetResources' -// Constants -import {TOKEN_LABEL} from 'src/labels/constants' - // Types -import {Telegraf, AppState, ResourceType, Authorization} from 'src/types' +import {Telegraf, AppState, ResourceType} from 'src/types' // Selectors -import {getAll} from 'src/resources/selectors' - -const {Authorizations} = ResourceType +import {getAll, getToken} from 'src/resources/selectors' +import {clearDataLoaders} from 'src/dataLoaders/actions/dataLoaders' interface StateProps { username: string - telegrafs: Telegraf[] - tokens: Authorization[] + token: string collectors: Telegraf[] } +interface DispatchProps { + onClearDataLoaders: typeof clearDataLoaders +} + +type Props = StateProps & DispatchProps & WithRouterProps + @ErrorHandling -export class TelegrafInstructionsOverlay extends PureComponent< - StateProps & WithRouterProps -> { +export class TelegrafInstructionsOverlay extends PureComponent { public render() { + const {token} = this.props return ( @@ -43,7 +43,7 @@ export class TelegrafInstructionsOverlay extends PureComponent< @@ -53,28 +53,6 @@ export class TelegrafInstructionsOverlay extends PureComponent< ) } - private get token(): string { - const {telegrafs, tokens} = this.props - const config = - telegrafs.find(t => get(this.collector, 'id', '') === t.id) || - this.collector - - if (!config) { - return 'no config found' - } - - const labels = get(config, 'labels', []) - - const label = labels.find(l => l.name === TOKEN_LABEL) - const auth = tokens.find(t => t.id === get(label, 'properties.tokenID')) - - if (!label || !auth) { - return 'unknown token' - } - - return auth.token - } - private get collector() { const { params: {id}, @@ -87,11 +65,12 @@ export class TelegrafInstructionsOverlay extends PureComponent< const { router, params: {orgID}, + onClearDataLoaders, } = this.props this.setState({ collectorID: null, }) - + onClearDataLoaders() router.push(`/orgs/${orgID}/load-data/telegrafs/`) } } @@ -101,18 +80,21 @@ const mstp = (state: AppState): StateProps => { me: {name}, } = state - const tokens = getAll(state, Authorizations) + const token = getToken(state) const telegrafs = getAll(state, ResourceType.Telegrafs) return { username: name, - tokens, + token, collectors: telegrafs, - telegrafs: telegrafs, } } -export default connect( +const mdtp: DispatchProps = { + onClearDataLoaders: clearDataLoaders, +} + +export default connect( mstp, - null + mdtp )(withRouter(TelegrafInstructionsOverlay))