diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ec67c7c46..4286d0fb9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Features 1. [14130](https://github.com/influxdata/influxdb/pull/14130): Add static templates for system, docker, redis, kubernetes +1. [14189] (https://github.com/influxdata/influxdb/pull/14189): Add option to select a token when creating a task ## v2.0.0-alpha.12 [2019-06-13] diff --git a/ui/src/authorizations/actions/index.ts b/ui/src/authorizations/actions/index.ts index 17c724547d..b840fe82dc 100644 --- a/ui/src/authorizations/actions/index.ts +++ b/ui/src/authorizations/actions/index.ts @@ -107,6 +107,15 @@ export const getAuthorizations = () => async ( } } +export const getAuthorization = async (authorizationID: string) => { + try { + const authorization = await client.authorizations.get(authorizationID) + return authorization + } catch (e) { + console.error(e) + } +} + export const createAuthorization = (auth: Authorization) => async ( dispatch: Dispatch ) => { diff --git a/ui/src/dataExplorer/components/SaveAsTaskForm.tsx b/ui/src/dataExplorer/components/SaveAsTaskForm.tsx index 274b6d10db..e25f3836f0 100644 --- a/ui/src/dataExplorer/components/SaveAsTaskForm.tsx +++ b/ui/src/dataExplorer/components/SaveAsTaskForm.tsx @@ -13,7 +13,9 @@ import { setTaskOption, clearTask, setNewScript, + setTaskToken, } from 'src/tasks/actions' +import {getAuthorizations} from 'src/authorizations/actions' // Utils import {getActiveTimeMachine} from 'src/timeMachine/selectors' @@ -26,13 +28,15 @@ import { } from 'src/utils/taskOptionsToFluxScript' // Types -import {AppState, TimeRange} from 'src/types' +import {AppState, TimeRange, RemoteDataState} from 'src/types' import { TaskSchedule, TaskOptions, TaskOptionKeys, } from 'src/utils/taskOptionsToFluxScript' import {DashboardDraftQuery} from 'src/types/dashboards' +import {Authorization} from '@influxdata/influx' +import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface' interface OwnProps { dismiss: () => void @@ -43,6 +47,8 @@ interface DispatchProps { setTaskOption: typeof setTaskOption clearTask: typeof clearTask setNewScript: typeof setNewScript + getTokens: typeof getAuthorizations + setTaskToken: typeof setTaskToken } interface StateProps { @@ -51,12 +57,15 @@ interface StateProps { activeQueryIndex: number newScript: string timeRange: TimeRange + tokens: Authorization[] + tokenStatus: RemoteDataState + selectedToken: Authorization } type Props = StateProps & OwnProps & DispatchProps class SaveAsTaskForm extends PureComponent { - public componentDidMount() { + public async componentDidMount() { const {setTaskOption, setNewScript} = this.props setTaskOption({ @@ -65,6 +74,10 @@ class SaveAsTaskForm extends PureComponent { }) setNewScript(this.activeScript) + await this.props.getTokens() + if (this.props.tokens.length > 0) { + this.props.setTaskToken(this.props.tokens[0]) + } } public componentWillUnmount() { @@ -74,19 +87,33 @@ class SaveAsTaskForm extends PureComponent { } public render() { - const {taskOptions, dismiss} = this.props + const { + taskOptions, + dismiss, + tokens, + tokenStatus, + selectedToken, + } = this.props return ( - + } + > + + ) } @@ -106,7 +133,13 @@ class SaveAsTaskForm extends PureComponent { } private handleSubmit = async () => { - const {saveNewScript, newScript, taskOptions, timeRange} = this.props + const { + saveNewScript, + newScript, + taskOptions, + timeRange, + selectedToken, + } = this.props // When a task runs, it does not have access to variables that we typically // inject into the script via the front end. So any variables that are used @@ -127,7 +160,7 @@ class SaveAsTaskForm extends PureComponent { const preamble = `${varOption}\n\n${taskOption}` const script = addDestinationToFluxScript(newScript, taskOptions) - saveNewScript(script, preamble) + saveNewScript(script, preamble, selectedToken.token) } private handleChangeToBucketName = (bucketName: string) => { @@ -150,11 +183,16 @@ class SaveAsTaskForm extends PureComponent { setTaskOption({key, value}) } + + private handleTokenChange = (selectedToken: Authorization) => { + this.props.setTaskToken(selectedToken) + } } const mstp = (state: AppState): StateProps => { const { - tasks: {newScript, taskOptions}, + tasks: {newScript, taskOptions, taskToken}, + tokens, orgs: {org}, } = state @@ -168,6 +206,9 @@ const mstp = (state: AppState): StateProps => { timeRange, draftQueries, activeQueryIndex, + tokens: tokens.list, + tokenStatus: tokens.status, + selectedToken: taskToken, } } @@ -176,6 +217,8 @@ const mdtp: DispatchProps = { setTaskOption, clearTask, setNewScript, + getTokens: getAuthorizations, + setTaskToken, } export default connect( diff --git a/ui/src/tasks/actions/index.ts b/ui/src/tasks/actions/index.ts index 56eeb04b3c..56a0a3a12c 100644 --- a/ui/src/tasks/actions/index.ts +++ b/ui/src/tasks/actions/index.ts @@ -3,7 +3,7 @@ import {push, goBack} from 'react-router-redux' import _ from 'lodash' // APIs -import {LogEvent, ITask as Task} from '@influxdata/influx' +import {LogEvent, ITask as Task, Authorization} from '@influxdata/influx' import {client} from 'src/utils/api' import {notify} from 'src/shared/actions/notifications' import { @@ -12,8 +12,6 @@ import { taskDeleteFailed, taskNotFound, taskUpdateFailed, - taskImportFailed, - taskImportSuccess, taskUpdateSuccess, taskCreatedSuccess, taskDeleteSuccess, @@ -46,6 +44,7 @@ import {TaskOptionKeys, TaskSchedule} from 'src/utils/taskOptionsToFluxScript' import {taskToTemplate} from 'src/shared/utils/resourceToTemplate' import {isLimitError} from 'src/cloud/utils/limits' import {checkTaskLimits} from 'src/cloud/actions/limits' +import {getAuthorization} from 'src/authorizations/actions' export type Action = | SetNewScript @@ -63,6 +62,7 @@ export type Action = | SetLogs | UpdateTask | SetTaskStatus + | SetTaskToken type GetStateFunc = () => AppState @@ -163,6 +163,12 @@ export interface UpdateTask { task: Task } } +export interface SetTaskToken { + type: 'SET_TASK_TOKEN' + payload: { + token: Authorization + } +} export const setTaskOption = (taskOption: { key: TaskOptionKeys @@ -231,6 +237,11 @@ export const updateTask = (task: Task): UpdateTask => ({ payload: {task}, }) +export const setTaskToken = (token: Authorization): SetTaskToken => ({ + type: 'SET_TASK_TOKEN', + payload: {token}, +}) + // Thunks export const getTasks = () => async ( dispatch, @@ -345,7 +356,8 @@ export const selectTaskByID = (id: string) => async ( ): Promise => { try { const task = await client.tasks.get(id) - + const token = await getAuthorization(task.authorizationID) + dispatch(setTaskToken(token)) dispatch(setCurrentTask(task)) } catch (e) { console.error(e) @@ -411,18 +423,18 @@ export const updateScript = () => async (dispatch, getState: GetStateFunc) => { } } -export const saveNewScript = (script: string, preamble: string) => async ( - dispatch, - getState: GetStateFunc -): Promise => { +export const saveNewScript = ( + script: string, + preamble: string, + token: string +) => async (dispatch, getState: GetStateFunc): Promise => { try { const fluxScript = await insertPreambleInScript(script, preamble) const { orgs: {org}, } = getState() - - await client.tasks.createByOrgID(org.id, fluxScript, null) + await client.tasks.createByOrgID(org.id, fluxScript, token) dispatch(setNewScript('')) dispatch(clearTask()) @@ -441,32 +453,6 @@ export const saveNewScript = (script: string, preamble: string) => async ( } } -export const importTask = (script: string) => async ( - dispatch, - getState: GetStateFunc -): Promise => { - try { - if (_.isEmpty(script)) { - dispatch(notify(taskImportFailed('File is empty'))) - return - } - - const { - orgs: {org}, - } = await getState() - - await client.tasks.createByOrgID(org.id, script, null) - - dispatch(getTasks()) - - dispatch(notify(taskImportSuccess())) - } catch (error) { - console.error(error) - const message = getErrorMessage(error) - dispatch(notify(taskImportFailed(message))) - } -} - export const getRuns = (taskID: string) => async (dispatch): Promise => { try { dispatch(setRuns([], RemoteDataState.Loading)) diff --git a/ui/src/tasks/components/TaskForm.test.tsx b/ui/src/tasks/components/TaskForm.test.tsx index 623cb83a54..2d85a2ab7e 100644 --- a/ui/src/tasks/components/TaskForm.test.tsx +++ b/ui/src/tasks/components/TaskForm.test.tsx @@ -16,6 +16,9 @@ const setup = (override?) => { onChangeScheduleType: jest.fn(), onChangeInput: jest.fn(), onChangeTaskOrgID: jest.fn(), + tokens: [], + selectedToken: '', + onTokenChange: jest.fn(), ...override, } diff --git a/ui/src/tasks/components/TaskForm.tsx b/ui/src/tasks/components/TaskForm.tsx index af6aa668ba..556714b0c9 100644 --- a/ui/src/tasks/components/TaskForm.tsx +++ b/ui/src/tasks/components/TaskForm.tsx @@ -27,6 +27,8 @@ import { ComponentSize, } from '@influxdata/clockface' import {TaskOptions, TaskSchedule} from 'src/utils/taskOptionsToFluxScript' +import {Authorization} from '@influxdata/influx' +import TaskTokenDropdown from './TaskTokenDropdown' interface Props { taskOptions: TaskOptions @@ -37,6 +39,9 @@ interface Props { onChangeScheduleType: (schedule: TaskSchedule) => void onChangeInput: (e: ChangeEvent) => void onChangeToBucketName: (bucketName: string) => void + tokens: Authorization[] + selectedToken: Authorization + onTokenChange: (token: Authorization) => void } interface State { @@ -73,6 +78,9 @@ export default class TaskForm extends PureComponent { toBucketName, }, isInOverlay, + tokens, + onTokenChange, + selectedToken, } = this.props return ( @@ -127,6 +135,15 @@ export default class TaskForm extends PureComponent { offset={offset} cron={cron} /> + + + + + {isInOverlay && ( diff --git a/ui/src/tasks/components/TaskTokenDropdown.tsx b/ui/src/tasks/components/TaskTokenDropdown.tsx new file mode 100644 index 0000000000..a540567d71 --- /dev/null +++ b/ui/src/tasks/components/TaskTokenDropdown.tsx @@ -0,0 +1,35 @@ +// Libraries +import _ from 'lodash' +import React, {PureComponent} from 'react' + +// Types +import {ComponentColor, ComponentSize} from '@influxdata/clockface' +import {Authorization} from '@influxdata/influx' +import {Dropdown} from 'src/clockface' + +interface Props { + tokens: Authorization[] + selectedToken: Authorization + onTokenChange: (token: Authorization) => void +} + +export default class TaskTokenDropdown extends PureComponent { + public render() { + const {tokens, selectedToken, onTokenChange} = this.props + + return ( + + {tokens.map(t => ( + + {t.description} + + ))} + + ) + } +} diff --git a/ui/src/tasks/components/__snapshots__/TaskForm.test.tsx.snap b/ui/src/tasks/components/__snapshots__/TaskForm.test.tsx.snap index 3f2b5c2fbe..f08791fa29 100644 --- a/ui/src/tasks/components/__snapshots__/TaskForm.test.tsx.snap +++ b/ui/src/tasks/components/__snapshots__/TaskForm.test.tsx.snap @@ -89,6 +89,21 @@ exports[`TaskForm rendering renders 1`] = ` + + + + + diff --git a/ui/src/tasks/containers/TaskEditPage.tsx b/ui/src/tasks/containers/TaskEditPage.tsx index b41ccba5d1..5e47671300 100644 --- a/ui/src/tasks/containers/TaskEditPage.tsx +++ b/ui/src/tasks/containers/TaskEditPage.tsx @@ -19,6 +19,7 @@ import { setTaskOption, clearTask, setAllTaskOptions, + setTaskToken, } from 'src/tasks/actions' // Types @@ -27,7 +28,10 @@ import { TaskOptionKeys, TaskSchedule, } from 'src/utils/taskOptionsToFluxScript' -import {AppState, Task} from 'src/types' +import {AppState, Task, RemoteDataState} from 'src/types' +import {Authorization} from '@influxdata/influx' +import {getAuthorizations} from 'src/authorizations/actions' +import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface' interface PassedInProps { router: InjectedRouter @@ -38,6 +42,9 @@ interface ConnectStateProps { taskOptions: TaskOptions currentTask: Task currentScript: string + tokens: Authorization[] + tokenStatus: RemoteDataState + selectedToken: Authorization } interface ConnectDispatchProps { @@ -48,6 +55,8 @@ interface ConnectDispatchProps { selectTaskByID: typeof selectTaskByID clearTask: typeof clearTask setAllTaskOptions: typeof setAllTaskOptions + getTokens: typeof getAuthorizations + setTaskToken: typeof setTaskToken } class TaskEditPage extends PureComponent< @@ -66,6 +75,10 @@ class TaskEditPage extends PureComponent< const {currentTask} = this.props this.props.setAllTaskOptions(currentTask) + + const {selectedToken, getTokens} = this.props + this.props.setTaskToken(selectedToken) + await getTokens() } public componentWillUnmount() { @@ -73,7 +86,13 @@ class TaskEditPage extends PureComponent< } public render(): JSX.Element { - const {currentScript, taskOptions} = this.props + const { + currentScript, + taskOptions, + tokens, + tokenStatus, + selectedToken, + } = this.props return ( @@ -86,12 +105,20 @@ class TaskEditPage extends PureComponent<
- + } + > + +
{ + this.props.setTaskToken(selectedToken) + } } -const mstp = ({tasks}: AppState): ConnectStateProps => { +const mstp = ({tasks, tokens}: AppState): ConnectStateProps => { return { taskOptions: tasks.taskOptions, currentScript: tasks.currentScript, currentTask: tasks.currentTask, + tokens: tokens.list, + tokenStatus: tokens.status, + selectedToken: tasks.taskToken, } } @@ -157,6 +190,8 @@ const mdtp: ConnectDispatchProps = { selectTaskByID, setAllTaskOptions, clearTask, + getTokens: getAuthorizations, + setTaskToken, } export default connect( diff --git a/ui/src/tasks/containers/TaskPage.tsx b/ui/src/tasks/containers/TaskPage.tsx index 865af8df4d..6176b922c2 100644 --- a/ui/src/tasks/containers/TaskPage.tsx +++ b/ui/src/tasks/containers/TaskPage.tsx @@ -17,6 +17,7 @@ import { setTaskOption, clearTask, cancel, + setTaskToken, } from 'src/tasks/actions' // Utils @@ -31,6 +32,13 @@ import { TaskOptionKeys, TaskSchedule, } from 'src/utils/taskOptionsToFluxScript' +import {getAuthorizations} from 'src/authorizations/actions' +import {Authorization} from '@influxdata/influx' +import { + RemoteDataState, + SpinnerContainer, + TechnoSpinner, +} from '@influxdata/clockface' interface PassedInProps { router: InjectedRouter @@ -39,6 +47,9 @@ interface PassedInProps { interface ConnectStateProps { taskOptions: TaskOptions newScript: string + tokens: Authorization[] + tokenStatus: RemoteDataState + selectedToken: Authorization } interface ConnectDispatchProps { @@ -47,16 +58,25 @@ interface ConnectDispatchProps { setTaskOption: typeof setTaskOption clearTask: typeof clearTask cancel: typeof cancel + getTokens: typeof getAuthorizations + setTaskToken: typeof setTaskToken } class TaskPage extends PureComponent< PassedInProps & ConnectStateProps & ConnectDispatchProps > { - public componentDidMount() { + constructor(props) { + super(props) + } + public async componentDidMount() { this.props.setTaskOption({ key: 'taskScheduleType', value: TaskSchedule.interval, }) + await this.props.getTokens() + if (this.props.tokens.length > 0) { + this.props.setTaskToken(this.props.tokens[0]) + } } public componentWillUnmount() { @@ -64,7 +84,13 @@ class TaskPage extends PureComponent< } public render(): JSX.Element { - const {newScript, taskOptions} = this.props + const { + newScript, + taskOptions, + tokens, + tokenStatus, + selectedToken, + } = this.props return ( @@ -77,12 +103,20 @@ class TaskPage extends PureComponent<
- + } + > + +
{ - const {newScript, taskOptions} = this.props + const {newScript, taskOptions, selectedToken} = this.props const taskOption: string = taskOptionsToFluxScript(taskOptions) const script: string = addDestinationToFluxScript(newScript, taskOptions) const preamble = `${taskOption}` - this.props.saveNewScript(script, preamble) + this.props.saveNewScript(script, preamble, selectedToken.token) } private handleCancel = () => { @@ -136,12 +170,19 @@ class TaskPage extends PureComponent< this.props.setTaskOption({key, value}) } + + private handleTokenChange = (selectedToken: Authorization) => { + this.props.setTaskToken(selectedToken) + } } -const mstp = ({tasks}: AppState): ConnectStateProps => { +const mstp = ({tasks, tokens}: AppState): ConnectStateProps => { return { taskOptions: tasks.taskOptions, newScript: tasks.newScript, + tokens: tokens.list, + tokenStatus: tokens.status, + selectedToken: tasks.taskToken, } } @@ -151,6 +192,8 @@ const mdtp: ConnectDispatchProps = { setTaskOption, clearTask, cancel, + getTokens: getAuthorizations, + setTaskToken, } export default connect( diff --git a/ui/src/tasks/reducers/index.ts b/ui/src/tasks/reducers/index.ts index 6494a6442b..cd4aad3b2a 100644 --- a/ui/src/tasks/reducers/index.ts +++ b/ui/src/tasks/reducers/index.ts @@ -1,5 +1,5 @@ import {TaskOptions, TaskSchedule} from 'src/utils/taskOptionsToFluxScript' -import {LogEvent} from '@influxdata/influx' +import {LogEvent, Authorization} from '@influxdata/influx' //Types import {Action} from 'src/tasks/actions' @@ -19,6 +19,7 @@ export interface TasksState { runs: Run[] runStatus: RemoteDataState logs: LogEvent[] + taskToken: Authorization } export const defaultTaskOptions: TaskOptions = { @@ -44,6 +45,7 @@ export const defaultState: TasksState = { runs: [], runStatus: RemoteDataState.NotStarted, logs: [], + taskToken: {}, } export default ( @@ -121,7 +123,6 @@ export default ( currentScript = task.flux } return {...state, currentScript, currentTask: task} - case 'SET_SEARCH_TERM': const {searchTerm} = action.payload return {...state, searchTerm} @@ -139,6 +140,9 @@ export default ( case 'SET_LOGS': const {logs} = action.payload return {...state, logs} + case 'SET_TASK_TOKEN': + const {token} = action.payload + return {...state, taskToken: token} default: return state }