Skip to content

Commit 647fba3

Browse files
authored
feat: added ability to export tasks using v2 template (#3973)
* feat: added ability to export import in tasks * test: update create task test * chore: clean up * feat: make dropdown btn width dynamic * test: updated create task e2e test
1 parent ce2548e commit 647fba3

File tree

11 files changed

+179
-23
lines changed

11 files changed

+179
-23
lines changed

cypress/e2e/shared/tasks.test.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,8 @@ http.post(url: "https://foo.bar/baz", data: bytes(v: "body"))`
101101
})
102102

103103
it('can create a task with an option parameter', () => {
104-
cy.getByTestID('create-task--button')
105-
.first()
106-
.click()
104+
cy.getByTestID('add-resource-dropdown--button').click()
105+
cy.getByTestID('add-resource-dropdown--new').click()
107106

108107
cy.focused()
109108

@@ -633,9 +632,8 @@ from(bucket: "defbuck")
633632
]
634633

635634
tasks.forEach(task => {
636-
cy.getByTestID('create-task--button')
637-
.first()
638-
.click()
635+
cy.getByTestID('add-resource-dropdown--button').click()
636+
cy.getByTestID('add-resource-dropdown--new').click()
639637

640638
// Fill Task Form
641639
// focused() waits for monoco editor to get input focus

src/shared/components/AddResourceDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class AddResourceDropdown extends PureComponent<Props> {
4747
const {titleText, status} = this.props
4848
return (
4949
<Dropdown
50-
style={{width: '232px'}}
50+
style={{width: 'fit-content'}}
5151
testID="add-resource-dropdown"
5252
button={(active, onClick) => (
5353
<Dropdown.Button

src/shared/containers/SetOrg.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
ScrapersIndex,
3232
SecretsIndex,
3333
TaskEditPage,
34+
TaskImportOverlay,
3435
TaskPage,
3536
TaskRunsPage,
3637
TasksPage,
@@ -129,6 +130,10 @@ const SetOrg: FC = () => {
129130
<Route path={`${orgPath}/tasks/:id/runs`} component={TaskRunsPage} />
130131
<Route path={`${orgPath}/tasks/:id/edit`} component={TaskEditPage} />
131132
<Route path={`${orgPath}/tasks/new`} component={TaskPage} />
133+
<Route
134+
path={`${orgPath}/tasks/import`}
135+
component={TaskImportOverlay}
136+
/>
132137
<Route path={`${orgPath}/tasks`} component={TasksPage} />
133138
{/* Data Explorer */}
134139
<Route

src/shared/containers/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ export const TaskRunsPage = lazy(() =>
1616
export const TaskEditPage = lazy(() =>
1717
import('src/tasks/containers/TaskEditPage')
1818
)
19+
export const TaskImportOverlay = lazy(() =>
20+
import('src/tasks/components/TaskImportOverlay')
21+
)
1922
export const DashboardsIndex = lazy(() =>
2023
import('src/dashboards/components/dashboard_index/DashboardsIndex')
2124
)

src/tasks/actions/thunks.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {normalize} from 'normalizr'
55

66
// APIs
77
import * as api from 'src/client'
8+
import {createResourceFromPkgerTemplate} from 'src/templates/api'
89

910
// Schemas
1011
import {taskSchema, arrayOfTasks} from 'src/schemas/tasks'
@@ -52,6 +53,7 @@ import {checkTaskLimits} from 'src/cloud/actions/limits'
5253
import {getOrg} from 'src/organizations/selectors'
5354
import {getAll, getStatus} from 'src/resources/selectors'
5455
import {incrementCloneName} from 'src/utils/naming'
56+
import {event} from 'src/cloud/utils/reporting'
5557

5658
// Types
5759
import {TASK_LIMIT} from 'src/resources/constants'
@@ -499,6 +501,33 @@ export const cancel = () => (dispatch: Dispatch<Action | RouterAction>) => {
499501
dispatch(goBack())
500502
}
501503

504+
export const createTaskFromTemplate = (template: api.Template) => async (
505+
dispatch: Dispatch<Action>,
506+
getState: GetState
507+
) => {
508+
try {
509+
const state = getState()
510+
const org = getOrg(state)
511+
512+
const variableName = template[0].spec.name
513+
514+
await createResourceFromPkgerTemplate(template, org.id)
515+
await getTasks()(dispatch, getState)
516+
517+
event('task.create.from_template.success', {
518+
name: variableName,
519+
})
520+
521+
dispatch(notify(copy.taskImportSuccess()))
522+
} catch (error) {
523+
event('task.create.from_template.failure', {
524+
template: template[0].meta.name,
525+
})
526+
console.error(error)
527+
dispatch(notify(copy.taskImportFailed(error.message)))
528+
}
529+
}
530+
502531
export const updateScript = () => async (
503532
dispatch: Dispatch<Action>,
504533
getState: GetState

src/tasks/apis/index.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Utils
2+
import {downloadTextFile} from 'src/shared/utils/download'
3+
4+
import {postTemplatesExport} from 'src/client'
5+
6+
export const downloadTaskTemplate = async (task): Promise<void> => {
7+
const resp = await postTemplatesExport({
8+
data: {
9+
resources: [
10+
{
11+
kind: 'Task',
12+
id: task.id,
13+
},
14+
],
15+
},
16+
})
17+
18+
if (resp.status === 500) {
19+
throw new Error(resp.data.message)
20+
}
21+
22+
const data = await resp.data
23+
downloadTextFile(JSON.stringify(data), task.name, '.json', 'text/json')
24+
}

src/tasks/components/TaskCard.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
pinnedItemSuccess,
5252
} from 'src/shared/copy/notifications'
5353
import {notify} from 'src/shared/actions/notifications'
54+
import {downloadTaskTemplate} from 'src/tasks/apis'
5455

5556
interface PassedProps {
5657
task: Task
@@ -293,12 +294,8 @@ export class TaskCard extends PureComponent<
293294
}
294295

295296
private handleExport = () => {
296-
const {
297-
history,
298-
task,
299-
location: {pathname},
300-
} = this.props
301-
history.push(`${pathname}/${task.id}/export`)
297+
const {task} = this.props
298+
downloadTaskTemplate(task)
302299
}
303300

304301
private get labels(): JSX.Element {
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React, {PureComponent} from 'react'
2+
import {withRouter, RouteComponentProps} from 'react-router-dom'
3+
import {connect, ConnectedProps} from 'react-redux'
4+
5+
// Components
6+
import ImportOverlay from 'src/shared/components/ImportOverlay'
7+
8+
// Copy
9+
import {invalidJSON} from 'src/shared/copy/notifications'
10+
11+
// Actions
12+
import {createTaskFromTemplate as createTaskFromTemplateAction} from 'src/tasks/actions/thunks'
13+
import {notify as notifyAction} from 'src/shared/actions/notifications'
14+
15+
// Types
16+
import {ComponentStatus} from '@influxdata/clockface'
17+
18+
// Utils
19+
import jsonlint from 'jsonlint-mod'
20+
21+
interface State {
22+
status: ComponentStatus
23+
}
24+
25+
type ReduxProps = ConnectedProps<typeof connector>
26+
type Props = RouteComponentProps<{orgID: string}> & ReduxProps
27+
28+
class TaskImportOverlay extends PureComponent<Props> {
29+
public state: State = {
30+
status: ComponentStatus.Default,
31+
}
32+
33+
public render() {
34+
return (
35+
<ImportOverlay
36+
onDismissOverlay={this.onDismiss}
37+
resourceName="Task"
38+
onSubmit={this.handleImportTask}
39+
status={this.state.status}
40+
updateStatus={this.updateOverlayStatus}
41+
/>
42+
)
43+
}
44+
45+
private onDismiss = () => {
46+
const {history} = this.props
47+
48+
history.goBack()
49+
}
50+
51+
private updateOverlayStatus = (status: ComponentStatus) =>
52+
this.setState(() => ({status}))
53+
54+
private handleImportTask = (uploadContent: string) => {
55+
const {createTaskFromTemplate, notify} = this.props
56+
57+
let template
58+
this.updateOverlayStatus(ComponentStatus.Default)
59+
try {
60+
template = jsonlint.parse(uploadContent)
61+
} catch (error) {
62+
this.updateOverlayStatus(ComponentStatus.Error)
63+
notify(invalidJSON(error.message))
64+
return
65+
}
66+
67+
createTaskFromTemplate(template)
68+
69+
this.onDismiss()
70+
}
71+
}
72+
73+
const mdtp = {
74+
createTaskFromTemplate: createTaskFromTemplateAction,
75+
notify: notifyAction,
76+
}
77+
78+
const connector = connect(null, mdtp)
79+
80+
export default connector(withRouter(TaskImportOverlay))

src/tasks/components/TasksHeader.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import {Link} from 'react-router-dom'
44

55
// Components
66
import {
7-
Button,
8-
ComponentColor,
97
InputLabel,
108
SlideToggle,
119
ComponentSize,
@@ -16,6 +14,7 @@ import {
1614
Icon,
1715
IconFont,
1816
} from '@influxdata/clockface'
17+
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
1918
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
2019
import ResourceSortDropdown from 'src/shared/components/resource_sort_dropdown/ResourceSortDropdown'
2120
import RateLimitAlert from 'src/cloud/components/RateLimitAlert'
@@ -36,6 +35,7 @@ import 'src/shared/components/cta.scss'
3635

3736
interface Props {
3837
onCreateTask: () => void
38+
onImportTask: () => void
3939
setShowInactive: () => void
4040
showInactive: boolean
4141
searchTerm: string
@@ -59,6 +59,7 @@ const TasksHeader: FC<Props> = ({
5959
sortKey,
6060
sortType,
6161
sortDirection,
62+
onImportTask,
6263
onSort,
6364
}) => {
6465
const {flowsCTA, setFlowsCTA} = useContext(AppSettingContext)
@@ -67,6 +68,10 @@ const TasksHeader: FC<Props> = ({
6768
onCreateTask()
6869
}
6970

71+
const handleOpenImportOverlay = () => {
72+
onImportTask()
73+
}
74+
7075
const recordClick = () => {
7176
event('Tasks List Page - Clicked Notebooks CTA')
7277
}
@@ -121,13 +126,10 @@ const TasksHeader: FC<Props> = ({
121126
onChange={setShowInactive}
122127
/>
123128
</FlexBox>
124-
<Button
125-
icon={IconFont.Plus_New}
126-
color={ComponentColor.Primary}
127-
text="Create Task"
128-
titleText="Click to create a Task"
129-
onClick={creator}
130-
testID="create-task--button"
129+
<AddResourceDropdown
130+
resourceName="Task"
131+
onSelectNew={creator}
132+
onSelectImport={handleOpenImportOverlay}
131133
/>
132134
</Page.ControlBarRight>
133135
</Page.ControlBar>

src/tasks/containers/TasksPage.test.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,13 @@ describe('Tasks.Containers.TasksPage', () => {
218218

219219
describe('create tasks', () => {
220220
it('triggers create a new task', () => {
221-
fireEvent.click(screen.getByTestId('create-task--button'))
221+
const dropdownCreateButton = ui.getByTestId(
222+
'add-resource-dropdown--button'
223+
)
224+
fireEvent.click(dropdownCreateButton)
225+
226+
const newTaskButton = ui.getByTestId('add-resource-dropdown--new')
227+
fireEvent.click(newTaskButton)
222228

223229
expect(localHistory.entries).toEqual(
224230
expect.arrayContaining([

0 commit comments

Comments
 (0)