Skip to content

Commit

Permalink
fix(ui): retain user input when parsing invalid JSON during import
Browse files Browse the repository at this point in the history
  • Loading branch information
TCL735 committed Dec 18, 2019
1 parent 41e771a commit 6b4c1d3
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 18 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
### Bug Fixes

1. [16225](https://github.com/influxdata/influxdb/pull/16225): Ensures env vars are applied consistently across cmd, and fixes issue where INFLUX\_ env var prefix was not set globally.

1. [16235](https://github.com/influxdata/influxdb/pull/16235): Removed default frontend sorting when flux queries specify sorting
1. [16238](https://github.com/influxdata/influxdb/pull/16238): Store canceled task runs in the correct bucket
1. [16237](https://github.com/influxdata/influxdb/pull/16237): Updated Sortby functionality for table frontend sorts to sort numbers correctly
1. [16255](https://github.com/influxdata/influxdb/pull/16255): Retain user input when parsing invalid JSON during import

### UI Improvements

Expand Down
38 changes: 29 additions & 9 deletions ui/cypress/e2e/dashboardsIndex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,41 @@ describe('Dashboards', () => {
cy.createDashboardTemplate(id)
})

cy.getByTestID('empty-dashboards-list')
.getByTestID('add-resource-dropdown--button')
.click()

cy.getByTestID('add-resource-dropdown--template').click()

cy.getByTestID('empty-dashboards-list').within(() => {
cy.getByTestID('add-resource-dropdown--button').click()
cy.getByTestID('add-resource-dropdown--template').click()
})
cy.getByTestID('template--Bashboard-Template').click()

cy.getByTestID('template-panel').should('exist')

cy.getByTestID('create-dashboard-button').click()

cy.getByTestID('dashboard-card').should('have.length', 1)
})

it('keeps user input in text area when attempting to import invalid JSON', () => {
cy.getByTestID('page-header').within(() => {
cy.contains('Create').click()
})

cy.getByTestID('add-resource-dropdown--import').click()
cy.contains('Paste').click()
cy.getByTestID('textarea')
.click()
.type('this is invalid JSON')
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid JSON')
)
cy.getByTestID('textarea').type(
'{backspace}{backspace}{backspace}{backspace}{backspace}'
)
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid')
)
})

describe('Dashboard List', () => {
beforeEach(() => {
cy.get('@org').then(({id}: Organization) => {
Expand Down
25 changes: 25 additions & 0 deletions ui/cypress/e2e/tasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ http.post(
.and('contain', taskName)
})

it('keeps user input in text area when attempting to import invalid JSON', () => {
cy.getByTestID('page-header').within(() => {
cy.contains('Create').click()
})

cy.getByTestID('add-resource-dropdown--import').click()
cy.contains('Paste').click()
cy.getByTestID('textarea')
.click()
.type('this is invalid JSON')
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid JSON')
)
cy.getByTestID('textarea').type(
'{backspace}{backspace}{backspace}{backspace}{backspace}'
)
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid')
)
})

describe('When tasks already exist', () => {
beforeEach(() => {
cy.get('@org').then(({id}: Organization) => {
Expand Down
32 changes: 32 additions & 0 deletions ui/cypress/e2e/templates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
describe('Templates', () => {
beforeEach(() => {
cy.flush()

cy.signin().then(({body}) => {
cy.wrap(body.org).as('org')
cy.visit(`orgs/${body.org.id}/settings/templates`)
})
})

it('keeps user input in text area when attempting to import invalid JSON', () => {
cy.get('button[title*="Import"]').click()

cy.contains('Paste').click()
cy.getByTestID('textarea')
.click()
.type('this is invalid JSON')
cy.get('button[title*="Import JSON"').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid JSON')
)
cy.getByTestID('textarea').type(
'{backspace}{backspace}{backspace}{backspace}{backspace}'
)
cy.get('button[title*="Import JSON"').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid')
)
})
})
25 changes: 25 additions & 0 deletions ui/cypress/e2e/variables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ describe('Variables', () => {
cy.getByTestID('resource-card').should('have.length', 1)
})

it('keeps user input in text area when attempting to import invalid JSON', () => {
cy.get('.tabbed-page-section--header').within(() => {
cy.contains('Create').click()
})

cy.getByTestID('add-resource-dropdown--import').click()
cy.contains('Paste').click()
cy.getByTestID('textarea')
.click()
.type('this is invalid JSON')
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid JSON')
)
cy.getByTestID('textarea').type(
'{backspace}{backspace}{backspace}{backspace}{backspace}'
)
cy.get('button[title*="Import JSON"]').click()
cy.getByTestID('textarea--error').should('have.length', 1)
cy.getByTestID('textarea').should($s =>
expect($s).to.contain('this is invalid')
)
})

it.skip('can delete a variable', () => {
cy.get<Organization>('@org').then(({id}) => {
cy.createVariable(id)
Expand Down
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"honeybadger-js": "^1.0.2",
"immer": "^1.9.3",
"intersection-observer": "^0.7.0",
"jsonlint-mod": "^1.7.5",
"lodash": "^4.3.0",
"memoize-one": "^4.0.2",
"moment": "^2.13.0",
Expand Down
25 changes: 23 additions & 2 deletions ui/src/dashboards/components/DashboardImportOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import {withRouter, WithRouterProps} from 'react-router'
import _ from 'lodash'
import {connect} from 'react-redux'

// Components
import ImportOverlay from 'src/shared/components/ImportOverlay'

// Copy
import {invalidJSON} from 'src/shared/copy/notifications'

Expand All @@ -15,7 +18,14 @@ import {
import {notify as notifyAction} from 'src/shared/actions/notifications'

// Types
import ImportOverlay from 'src/shared/components/ImportOverlay'
import {ComponentStatus} from '@influxdata/clockface'

// Utils
import jsonlint from 'jsonlint-mod'

interface State {
status: ComponentStatus
}

interface DispatchProps {
createDashboardFromTemplate: typeof createDashboardFromTemplateAction
Expand All @@ -30,24 +40,35 @@ interface OwnProps extends WithRouterProps {
type Props = OwnProps & DispatchProps

class DashboardImportOverlay extends PureComponent<Props> {
public state: State = {
status: ComponentStatus.Default,
}

public render() {
return (
<ImportOverlay
isVisible={true}
onDismissOverlay={this.onDismiss}
resourceName="Dashboard"
onSubmit={this.handleImportDashboard}
status={this.state.status}
updateStatus={this.updateOverlayStatus}
/>
)
}

private updateOverlayStatus = (status: ComponentStatus) =>
this.setState(() => ({status}))

private handleImportDashboard = (uploadContent: string) => {
const {createDashboardFromTemplate, notify, populateDashboards} = this.props

let template
this.updateOverlayStatus(ComponentStatus.Default)
try {
template = JSON.parse(uploadContent)
template = jsonlint.parse(uploadContent)
} catch (error) {
this.updateOverlayStatus(ComponentStatus.Error)
notify(invalidJSON(error.message))
return
}
Expand Down
16 changes: 14 additions & 2 deletions ui/src/shared/components/ImportOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ interface OwnProps {
resourceName: string
onSubmit: (importString: string, orgID: string) => void
isVisible?: boolean
status?: ComponentStatus
updateStatus?: (status: ComponentStatus) => void
}

interface State {
Expand Down Expand Up @@ -96,6 +98,7 @@ class ImportOverlay extends PureComponent<Props, State> {

private get importBody(): JSX.Element {
const {selectedImportOption, importContent} = this.state
const {status = ComponentStatus.Default} = this.props

if (selectedImportOption === ImportOption.Upload) {
return (
Expand All @@ -110,16 +113,22 @@ class ImportOverlay extends PureComponent<Props, State> {
}
if (selectedImportOption === ImportOption.Paste) {
return (
<TextArea value={importContent} onChange={this.handleChangeTextArea} />
<TextArea
status={status}
value={importContent}
onChange={this.handleChangeTextArea}
/>
)
}
}

private handleChangeTextArea = (
e: ChangeEvent<HTMLTextAreaElement>
): void => {
const {updateStatus = () => {}} = this.props
const importContent = e.target.value
this.handleSetImportContent(importContent)
updateStatus(ComponentStatus.Default)
}

private get submitButton(): JSX.Element {
Expand Down Expand Up @@ -154,7 +163,10 @@ class ImportOverlay extends PureComponent<Props, State> {
}

private clearImportContent = () => {
this.setState({importContent: ''})
this.setState((state, props) => {
const {status = ComponentStatus.Default} = props
return status === ComponentStatus.Error ? {...state} : {importContent: ''}
})
}

private onDismiss = () => {
Expand Down
23 changes: 22 additions & 1 deletion ui/src/tasks/components/TaskImportOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import {invalidJSON} from 'src/shared/copy/notifications'
import {createTaskFromTemplate as createTaskFromTemplateAction} from 'src/tasks/actions/'
import {notify as notifyAction} from 'src/shared/actions/notifications'

// Types
import {ComponentStatus} from '@influxdata/clockface'

// Utils
import jsonlint from 'jsonlint-mod'

interface State {
status: ComponentStatus
}

interface DispatchProps {
createTaskFromTemplate: typeof createTaskFromTemplateAction
notify: typeof notifyAction
Expand All @@ -20,12 +30,18 @@ interface DispatchProps {
type Props = DispatchProps & WithRouterProps

class TaskImportOverlay extends PureComponent<Props> {
public state: State = {
status: ComponentStatus.Default,
}

public render() {
return (
<ImportOverlay
onDismissOverlay={this.onDismiss}
resourceName="Task"
onSubmit={this.handleImportTask}
status={this.state.status}
updateStatus={this.updateOverlayStatus}
/>
)
}
Expand All @@ -36,13 +52,18 @@ class TaskImportOverlay extends PureComponent<Props> {
router.goBack()
}

private updateOverlayStatus = (status: ComponentStatus) =>
this.setState(() => ({status}))

private handleImportTask = (importString: string) => {
const {createTaskFromTemplate, notify} = this.props

let template
this.updateOverlayStatus(ComponentStatus.Default)
try {
template = JSON.parse(importString)
template = jsonlint.parse(importString)
} catch (error) {
this.updateOverlayStatus(ComponentStatus.Error)
notify(invalidJSON(error.message))
return
}
Expand Down
21 changes: 20 additions & 1 deletion ui/src/templates/components/TemplateImportOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ import {notify as notifyAction} from 'src/shared/actions/notifications'

// Types
import {AppState, Organization} from 'src/types'
import {ComponentStatus} from '@influxdata/clockface'

// Utils
import jsonlint from 'jsonlint-mod'

interface State {
status: ComponentStatus
}

interface DispatchProps {
createTemplate: typeof createTemplateAction
Expand All @@ -35,12 +43,18 @@ interface OwnProps extends WithRouterProps {
type Props = DispatchProps & OwnProps & StateProps

class TemplateImportOverlay extends PureComponent<Props> {
public state: State = {
status: ComponentStatus.Default,
}

public render() {
return (
<ImportOverlay
onDismissOverlay={this.onDismiss}
resourceName="Template"
onSubmit={this.handleImportTemplate}
status={this.state.status}
updateStatus={this.updateOverlayStatus}
/>
)
}
Expand All @@ -51,13 +65,18 @@ class TemplateImportOverlay extends PureComponent<Props> {
router.goBack()
}

private updateOverlayStatus = (status: ComponentStatus) =>
this.setState(() => ({status}))

private handleImportTemplate = (importString: string) => {
const {createTemplate, getTemplates, notify} = this.props

let template
this.updateOverlayStatus(ComponentStatus.Default)
try {
template = JSON.parse(importString)
template = jsonlint.parse(importString)
} catch (error) {
this.updateOverlayStatus(ComponentStatus.Error)
notify(invalidJSON(error.message))
return
}
Expand Down
Loading

0 comments on commit 6b4c1d3

Please sign in to comment.