diff --git a/ui/src/templates/actions/creators.ts b/ui/src/templates/actions/creators.ts index 691d868dca..97eebc27f1 100644 --- a/ui/src/templates/actions/creators.ts +++ b/ui/src/templates/actions/creators.ts @@ -20,10 +20,13 @@ export const SET_TEMPLATE_SUMMARY = 'SET_TEMPLATE_SUMMARY' export const SET_TEMPLATES_STATUS = 'SET_TEMPLATES_STATUS' export const TOGGLE_TEMPLATE_RESOURCE_INSTALL = 'TOGGLE_TEMPLATE_RESOURCE_INSTALL' +export const UPDATE_TEMPLATE_ENV_REF = 'UPDATE_TEMPLATE_ENV_REF' export const SET_STACKS = 'SET_STACKS' export const DELETE_STACKS = 'DELETE_STACKS' +export type EnvRefValue = string | number | boolean + export type Action = | ReturnType | ReturnType @@ -34,6 +37,7 @@ export type Action = | ReturnType | ReturnType | ReturnType + | ReturnType | ReturnType | ReturnType @@ -104,6 +108,20 @@ export const setStagedTemplateUrl = (templateUrl: string) => templateUrl, } as const) +export const updateTemplateEnvReferences = ( + envRefKey: string, + resourceField: string, + newValue: EnvRefValue, + valueType: string +) => + ({ + type: UPDATE_TEMPLATE_ENV_REF, + envRefKey, + resourceField, + newValue, + valueType, + } as const) + export const toggleTemplateResourceInstall = ( resourceType: string, templateMetaName: string, diff --git a/ui/src/templates/api/index.ts b/ui/src/templates/api/index.ts index ad6cccdc79..e3e71a7deb 100644 --- a/ui/src/templates/api/index.ts +++ b/ui/src/templates/api/index.ts @@ -489,7 +489,8 @@ export const reviewTemplate = async (orgID: string, templateUrl: string) => { export const installTemplate = async ( orgID: string, templateUrl: string, - resourcesToSkip + resourcesToSkip: {[key: string]: string}, + envRefs: {} = {} ) => { const data: TemplateApply = { dryRun: false, @@ -511,6 +512,10 @@ export const installTemplate = async ( data.actions = actions } + if (Object.keys(envRefs).length) { + data.envRefs = envRefs + } + const params = { data, } diff --git a/ui/src/templates/components/CommunityTemplateEnvReferences.tsx b/ui/src/templates/components/CommunityTemplateEnvReferences.tsx new file mode 100644 index 0000000000..b5f8a2d92d --- /dev/null +++ b/ui/src/templates/components/CommunityTemplateEnvReferences.tsx @@ -0,0 +1,108 @@ +import React, {PureComponent} from 'react' +import {connect, ConnectedProps} from 'react-redux' + +// Components +import {Input, InputType, Table} from '@influxdata/clockface' + +// Types +import {AppState} from 'src/types' + +import {updateTemplateEnvReferences} from 'src/templates/actions/creators' + +export type EnvRef = any + +interface OwnProps { + resource: any +} + +type ReduxProps = ConnectedProps +type Props = ReduxProps & OwnProps + +class CommunityTemplateEnvReferencesUnconnected extends PureComponent { + private handleChange = ref => { + return event => { + this.props.updateTemplateEnvReferences( + ref.envRefKey, + ref.resourceField, + event.target.value, + ref.valueType + ) + } + } + + private renderInputForEnvRef = (ref: EnvRef) => { + switch (ref.valueType) { + case 'float': + case 'number': + case 'integer': { + return ( + + ) + } + case 'string': + default: { + return ( + + ) + } + } + } + + private getFieldType(ref: EnvRef) { + return ref.resourceField.split('.').pop() + } + + render() { + return ( + + + + Parameter + Value + + + + {this.props.resource.envReferences.map(ref => { + return ( + + + {this.getFieldType(ref)} + + {this.renderInputForEnvRef(ref)} + + ) + })} + +
+ ) + } +} + +const mstp = (state: AppState) => { + return { + stagedTemplateEnvReferences: + state.resources.templates.stagedTemplateEnvReferences, + } +} + +const mdtp = { + updateTemplateEnvReferences, +} + +const connector = connect(mstp, mdtp) + +export const CommunityTemplateEnvReferences = connector( + CommunityTemplateEnvReferencesUnconnected +) diff --git a/ui/src/templates/components/CommunityTemplateImportOverlay.tsx b/ui/src/templates/components/CommunityTemplateImportOverlay.tsx index 3e21edd928..eb91e919aa 100644 --- a/ui/src/templates/components/CommunityTemplateImportOverlay.tsx +++ b/ui/src/templates/components/CommunityTemplateImportOverlay.tsx @@ -110,7 +110,8 @@ class UnconnectedTemplateImportOverlay extends PureComponent { summary = await installTemplate( this.props.org.id, this.props.stagedTemplateUrl, - this.props.resourcesToSkip + this.props.resourcesToSkip, + this.props.stagedTemplateEnvReferences ) } catch (err) { this.props.notify(communityTemplateInstallFailed(err.message)) @@ -143,8 +144,36 @@ const mstp = (state: AppState, props: RouterProps) => { props.match.params.orgID ) + // convert the env references into a format pkger is happy with + const stagedTemplateEnvReferences = {} + for (const [refKey, refObject] of Object.entries( + state.resources.templates.stagedTemplateEnvReferences + )) { + switch (refObject.valueType) { + case 'string': + case 'time': + case 'duration': { + stagedTemplateEnvReferences[refKey] = refObject.value + continue + } + case 'number': + case 'float': { + stagedTemplateEnvReferences[refKey] = parseFloat(refObject.value as any) + continue + } + case 'integer': { + stagedTemplateEnvReferences[refKey] = parseInt( + refObject.value as any, + 10 + ) + continue + } + } + } + return { org, + stagedTemplateEnvReferences, flags: state.flags.original, resourceCount: getTotalResourceCount( state.resources.templates.stagedCommunityTemplate.summary diff --git a/ui/src/templates/components/CommunityTemplateOverlayContents.tsx b/ui/src/templates/components/CommunityTemplateOverlayContents.tsx index 93f35c63d9..9ab12a2302 100644 --- a/ui/src/templates/components/CommunityTemplateOverlayContents.tsx +++ b/ui/src/templates/components/CommunityTemplateOverlayContents.tsx @@ -17,6 +17,8 @@ import { import CommunityTemplateListItem from 'src/templates/components/CommunityTemplateListItem' import CommunityTemplateListGroup from 'src/templates/components/CommunityTemplateListGroup' +import {CommunityTemplateParameters} from 'src/templates/components/CommunityTemplateParameters' + import {toggleTemplateResourceInstall} from 'src/templates/actions/creators' import {getResourceInstallCount} from 'src/templates/selectors' @@ -50,24 +52,38 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.dashboards) && summary.dashboards.map(dashboard => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'dashboards', - }) - this.props.toggleTemplateResourceInstall( - 'dashboards', - dashboard.templateMetaName, - !dashboard.shouldInstall - ) - }} + - Charts: {dashboard.charts.length} - + + { + event('template_resource_uncheck', { + templateResourceType: 'dashboards', + }) + this.props.toggleTemplateResourceInstall( + 'dashboards', + dashboard.templateMetaName, + !dashboard.shouldInstall + ) + }} + title={dashboard.name} + description={dashboard.description} + > + Charts: {dashboard.charts.length} + + + + {resourceHasEnvRefs(dashboard) && ( + + + + )} + ) })} @@ -78,22 +94,35 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.telegrafConfigs) && summary.telegrafConfigs.map(telegrafConfig => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'telegraf', - }) - this.props.toggleTemplateResourceInstall( - 'telegrafConfigs', - telegrafConfig.templateMetaName, - !telegrafConfig.shouldInstall - ) - }} + + > + + { + event('template_resource_uncheck', { + templateResourceType: 'telegraf', + }) + this.props.toggleTemplateResourceInstall( + 'telegrafConfigs', + telegrafConfig.templateMetaName, + !telegrafConfig.shouldInstall + ) + }} + title={telegrafConfig.templateMetaName} + description={telegrafConfig.description} + /> + + {resourceHasEnvRefs(telegrafConfig) && ( + + + + )} + ) })} @@ -104,26 +133,82 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.buckets) && summary.buckets.map(bucket => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'buckets', - }) - this.props.toggleTemplateResourceInstall( - 'buckets', - bucket.templateMetaName, - !bucket.shouldInstall - ) - }} + + > + + { + event('template_resource_uncheck', { + templateResourceType: 'buckets', + }) + this.props.toggleTemplateResourceInstall( + 'buckets', + bucket.templateMetaName, + !bucket.shouldInstall + ) + }} + key={bucket.templateMetaName} + title={bucket.name} + description={bucket.description} + /> + + {resourceHasEnvRefs(bucket) && ( + + + + )} + ) })} + + + {Array.isArray(summary.summaryTask) && + summary.summaryTask.map(task => { + return ( + + + { + event('template_resource_uncheck', { + templateResourceType: 'tasks', + }) + this.props.toggleTemplateResourceInstall( + 'tasks', + task.templateMetaName, + !task.shouldInstall + ) + }} + key={task.templateMetaName} + title={task.name} + description={task.description} + /> + + {resourceHasEnvRefs(task) && ( + + + + )} + + ) + })} + + { {Array.isArray(summary.checks) && summary.checks.map(check => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'checks', - }) - this.props.toggleTemplateResourceInstall( - 'checks', - check.templateMetaName, - !check.shouldInstall - ) - }} + + > + + { + event('template_resource_uncheck', { + templateResourceType: 'checks', + }) + this.props.toggleTemplateResourceInstall( + 'checks', + check.templateMetaName, + !check.shouldInstall + ) + }} + key={check.templateMetaName} + title={check.check.name} + description={check.description} + /> + + {resourceHasEnvRefs(check) && ( + + + + )} + ) })} @@ -157,25 +256,39 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.variables) && summary.variables.map(variable => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'variables', - }) - this.props.toggleTemplateResourceInstall( - 'variables', - variable.templateMetaName, - !variable.shouldInstall - ) - }} + - Type: {variable.arguments.type} - + + { + event('template_resource_uncheck', { + templateResourceType: 'variables', + }) + this.props.toggleTemplateResourceInstall( + 'variables', + variable.templateMetaName, + !variable.shouldInstall + ) + }} + key={variable.templateMetaName} + title={variable.name} + description={variable.description} + > + Type: {variable.arguments.type} + + + {resourceHasEnvRefs(variable) && ( + + + + )} + ) })} @@ -186,22 +299,36 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.notificationRules) && summary.notificationRules.map(notificationRule => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'notification rules', - }) - this.props.toggleTemplateResourceInstall( - 'notificationRules', - notificationRule.templateMetaName, - !notificationRule.shouldInstall - ) - }} + + > + + { + event('template_resource_uncheck', { + templateResourceType: 'notification rules', + }) + this.props.toggleTemplateResourceInstall( + 'notificationRules', + notificationRule.templateMetaName, + !notificationRule.shouldInstall + ) + }} + key={notificationRule.templateMetaName} + title={notificationRule.name} + description={notificationRule.description} + /> + + {resourceHasEnvRefs(summary) && ( + + + + )} + ) })} @@ -212,27 +339,41 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { {Array.isArray(summary.labels) && summary.labels.map(label => { return ( - { - event('template_resource_uncheck', { - templateResourceType: 'labels', - }) - this.props.toggleTemplateResourceInstall( - 'labels', - label.templateMetaName, - !label.shouldInstall - ) - }} + - + + { + event('template_resource_uncheck', { + templateResourceType: 'labels', + }) + this.props.toggleTemplateResourceInstall( + 'labels', + label.templateMetaName, + !label.shouldInstall + ) + }} + key={label.templateMetaName} + > + + + {resourceHasEnvRefs(label) && ( + + + + )} + ) })} @@ -241,6 +382,12 @@ class CommunityTemplateOverlayContentsUnconnected extends PureComponent { } } +const resourceHasEnvRefs = (resource: any): boolean => { + return ( + resource.envReferences && Object.keys(resource.envReferences).length > 0 + ) +} + const mstp = (state: AppState) => { return {summary: state.resources.templates.stagedCommunityTemplate.summary} } diff --git a/ui/src/templates/components/CommunityTemplateParameters.tsx b/ui/src/templates/components/CommunityTemplateParameters.tsx new file mode 100644 index 0000000000..86ba00a17b --- /dev/null +++ b/ui/src/templates/components/CommunityTemplateParameters.tsx @@ -0,0 +1,45 @@ +// Libraries +import React, {PureComponent} from 'react' + +import { + AlignItems, + ComponentSize, + FlexBox, + FlexDirection, + Panel, +} from '@influxdata/clockface' + +import {CommunityTemplateEnvReferences} from 'src/templates/components/CommunityTemplateEnvReferences' + +interface OwnProps { + resource: any +} + +type Props = OwnProps + +export class CommunityTemplateParameters extends PureComponent { + render() { + if (Object.keys(this.props.resource.envReferences).length < 1) { + return null + } + + return ( + + + + + + + + ) + } +} diff --git a/ui/src/templates/reducers/index.test.ts b/ui/src/templates/reducers/index.test.ts index f9aa2a7369..4296e6053a 100644 --- a/ui/src/templates/reducers/index.test.ts +++ b/ui/src/templates/reducers/index.test.ts @@ -46,6 +46,7 @@ const stagedCommunityTemplate: CommunityTemplate = {} const initialState = () => ({ stagedCommunityTemplate, + stagedTemplateEnvReferences: {}, stagedTemplateUrl: '', status, byID: { @@ -100,6 +101,7 @@ describe('templates reducer', () => { allIDs, exportTemplate, stagedCommunityTemplate, + stagedTemplateEnvReferences: {}, stagedTemplateUrl: '', stacks: [], } diff --git a/ui/src/templates/reducers/index.ts b/ui/src/templates/reducers/index.ts index 5ba2092f21..03916aa445 100644 --- a/ui/src/templates/reducers/index.ts +++ b/ui/src/templates/reducers/index.ts @@ -11,6 +11,7 @@ import { SET_TEMPLATES_STATUS, SET_TEMPLATE_SUMMARY, TOGGLE_TEMPLATE_RESOURCE_INSTALL, + UPDATE_TEMPLATE_ENV_REF, } from 'src/templates/actions/creators' import { CommunityTemplate, @@ -38,6 +39,7 @@ const defaultCommunityTemplate = (): CommunityTemplate => { export const defaultState = (): TemplatesState => ({ stagedCommunityTemplate: defaultCommunityTemplate(), + stagedTemplateEnvReferences: {}, stagedTemplateUrl: '', status: RemoteDataState.NotStarted, byID: {}, @@ -87,6 +89,8 @@ export const templatesReducer = ( case SET_STAGED_TEMPLATE: { const {template} = action + const envReferences = {} + const stagedCommunityTemplate = { ...defaultCommunityTemplate(), ...template, @@ -98,6 +102,16 @@ export const templatesReducer = ( if (!dashboard.hasOwnProperty('shouldInstall')) { dashboard.shouldInstall = true } + if (dashboard.envReferences.length) { + dashboard.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return dashboard }) @@ -107,6 +121,16 @@ export const templatesReducer = ( if (!telegrafConfig.hasOwnProperty('shouldInstall')) { telegrafConfig.shouldInstall = true } + if (telegrafConfig.envReferences.length) { + telegrafConfig.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return telegrafConfig }) @@ -116,6 +140,16 @@ export const templatesReducer = ( if (!bucket.hasOwnProperty('shouldInstall')) { bucket.shouldInstall = true } + if (bucket.envReferences.length) { + bucket.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return bucket }) @@ -125,6 +159,16 @@ export const templatesReducer = ( if (!check.hasOwnProperty('shouldInstall')) { check.shouldInstall = true } + if (check.envReferences.length) { + check.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return check }) @@ -134,6 +178,16 @@ export const templatesReducer = ( if (!variable.hasOwnProperty('shouldInstall')) { variable.shouldInstall = true } + if (variable.envReferences.length) { + variable.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return variable }) @@ -143,6 +197,16 @@ export const templatesReducer = ( if (!notificationRule.hasOwnProperty('shouldInstall')) { notificationRule.shouldInstall = true } + if (notificationRule.envReferences.length) { + notificationRule.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return notificationRule }) @@ -152,6 +216,16 @@ export const templatesReducer = ( if (!notificationEndpoint.hasOwnProperty('shouldInstall')) { notificationEndpoint.shouldInstall = true } + if (notificationEndpoint.envReferences.length) { + notificationEndpoint.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return notificationEndpoint }) @@ -161,6 +235,16 @@ export const templatesReducer = ( if (!label.hasOwnProperty('shouldInstall')) { label.shouldInstall = true } + if (label.envReferences.length) { + label.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return label }) @@ -170,9 +254,20 @@ export const templatesReducer = ( if (!summaryTask.hasOwnProperty('shouldInstall')) { summaryTask.shouldInstall = true } + if (summaryTask.envReferences.length) { + summaryTask.envReferences.forEach(ref => { + envReferences[ref.envRefKey] = { + envRefKey: ref.envRefKey, + resourceField: ref.resourceField, + value: ref.defaultValue, + valueType: ref.valueType, + } + }) + } return summaryTask }) + draftState.stagedTemplateEnvReferences = envReferences draftState.stagedCommunityTemplate = stagedCommunityTemplate return } @@ -233,6 +328,17 @@ export const templatesReducer = ( draftState.stacks = stacks return } + + case UPDATE_TEMPLATE_ENV_REF: { + const {envRefKey, newValue, resourceField, valueType} = action + draftState.stagedTemplateEnvReferences[envRefKey] = { + envRefKey, + resourceField, + value: newValue, + valueType, + } + return + } } }) diff --git a/ui/src/types/templates.ts b/ui/src/types/templates.ts index 64d4df2874..a1e27790f1 100644 --- a/ui/src/types/templates.ts +++ b/ui/src/types/templates.ts @@ -33,9 +33,18 @@ export interface TemplateSummary extends Omit { export type CommunityTemplate = any +export interface EnvReference { + resourceField: string + envRefKey: string + value?: string | number | boolean + defaultValue?: string | number | boolean + valueType: string +} + export interface TemplatesState extends NormalizedState { exportTemplate: {status: RemoteDataState; item: DocumentCreate} stagedCommunityTemplate: CommunityTemplate + stagedTemplateEnvReferences: {[key: string]: EnvReference} stagedTemplateUrl: string stacks: InstalledStack[] }