From 0c2b02c7fc884fcb58a58a1fbb7128989abfb5c5 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Thu, 25 May 2017 11:31:36 -0700 Subject: [PATCH] Better errors inside environment editors (#257) --- app/network/o-auth-2/grant-password.js | 12 ++- .../components/editors/environment-editor.js | 76 ++++++++++++++----- .../workspace-environments-edit-modal.js | 2 +- app/ui/components/request-pane.js | 4 +- app/ui/components/response-pane.js | 31 ++++---- app/ui/css/components/environment-editor.less | 5 ++ app/ui/css/index.less | 1 + 7 files changed, 91 insertions(+), 40 deletions(-) create mode 100644 app/ui/css/components/environment-editor.less diff --git a/app/network/o-auth-2/grant-password.js b/app/network/o-auth-2/grant-password.js index 96521ad17..4fe6a165b 100644 --- a/app/network/o-auth-2/grant-password.js +++ b/app/network/o-auth-2/grant-password.js @@ -1,5 +1,5 @@ import * as querystring from '../../common/querystring'; -import {getBasicAuthHeader} from '../../common/misc'; +import {getBasicAuthHeader, setDefaultProtocol} from '../../common/misc'; import * as c from './constants'; import {responseToObject} from './misc'; @@ -38,7 +38,15 @@ export default async function (accessTokenUrl, headers: headers }; - const response = await window.fetch(accessTokenUrl, config); + const url = setDefaultProtocol(accessTokenUrl); + + let response; + try { + response = await window.fetch(url, config); + } catch (err) { + throw new Error(`Failed to fetch access token at URL "${url}"`); + } + const body = await response.text(); const results = responseToObject(body, [ c.P_ACCESS_TOKEN, diff --git a/app/ui/components/editors/environment-editor.js b/app/ui/components/editors/environment-editor.js index a7da60862..7d5b73e51 100644 --- a/app/ui/components/editors/environment-editor.js +++ b/app/ui/components/editors/environment-editor.js @@ -5,7 +5,40 @@ import {DEBOUNCE_MILLIS} from '../../../common/constants'; @autobind class EnvironmentEditor extends PureComponent { + constructor (props) { + super(props); + this.state = { + error: null, + warning: null + }; + } + _handleChange () { + let error = null; + let warning = null; + let value = null; + + // Check for JSON parse errors + try { + value = this.getValue(); + } catch (err) { + error = err.message; + } + + // Check for invalid key names + if (value) { + for (const key of Object.keys(value)) { + if (!key.match(/^[a-zA-Z_$][0-9a-zA-Z_$]*$/)) { + warning = `"${key}" must only contain letters, numbers, and underscores`; + break; + } + } + } + + if (this.state.error !== error || this.state.warning !== warning) { + this.setState({error, warning}); + } + this.props.didChange(); } @@ -18,12 +51,7 @@ class EnvironmentEditor extends PureComponent { } isValid () { - try { - return this.getValue() !== undefined; - } catch (e) { - // Failed to parse JSON - return false; - } + return !this.state.error; } render () { @@ -38,22 +66,28 @@ class EnvironmentEditor extends PureComponent { ...props } = this.props; + const {error, warning} = this.state; + return ( - +
+ + {error &&

{error}

} + {(!error && warning) &&

{warning}

} +
); } } diff --git a/app/ui/components/modals/workspace-environments-edit-modal.js b/app/ui/components/modals/workspace-environments-edit-modal.js index a22a02c99..82da0d8ac 100644 --- a/app/ui/components/modals/workspace-environments-edit-modal.js +++ b/app/ui/components/modals/workspace-environments-edit-modal.js @@ -133,7 +133,7 @@ class WorkspaceEnvironmentsEditModal extends PureComponent { _didChange () { const isValid = this._envEditor.isValid(); - if (this.state.isValid === isValid) { + if (this.state.isValid !== isValid) { this.setState({isValid}); } diff --git a/app/ui/components/request-pane.js b/app/ui/components/request-pane.js index 5f18b888b..db5763194 100644 --- a/app/ui/components/request-pane.js +++ b/app/ui/components/request-pane.js @@ -241,12 +241,12 @@ class RequestPane extends PureComponent { diff --git a/app/ui/components/response-pane.js b/app/ui/components/response-pane.js index 50a5022b5..3b9c4d564 100644 --- a/app/ui/components/response-pane.js +++ b/app/ui/components/response-pane.js @@ -90,9 +90,9 @@ class ResponsePane extends PureComponent { const {body, timeline, encoding} = this.state.response; const headers = timeline - .filter(v => v.name === 'HEADER_IN') - .map(v => v.value) - .join(''); + .filter(v => v.name === 'HEADER_IN') + .map(v => v.value) + .join(''); const bodyBuffer = new Buffer(body, encoding); const fullResponse = `${headers}${bodyBuffer}`; @@ -249,8 +249,11 @@ class ResponsePane extends PureComponent { @@ -259,7 +262,7 @@ class ResponsePane extends PureComponent { {cookieHeaders.length}) : null} - {response.timeline && response.timeline.length && ( + {(response.timeline && response.timeline.length > 0) && ( @@ -305,15 +308,15 @@ class ResponsePane extends PureComponent { /> - {(response.timeline && response.timeline.length) && ( + {(response.timeline && response.timeline.length > 0) && ( - + )} diff --git a/app/ui/css/components/environment-editor.less b/app/ui/css/components/environment-editor.less new file mode 100644 index 000000000..beb522b7c --- /dev/null +++ b/app/ui/css/components/environment-editor.less @@ -0,0 +1,5 @@ +.environment-editor { + display: grid; + grid-template-rows: 1fr auto; + height: 100%; +} diff --git a/app/ui/css/index.less b/app/ui/css/index.less index 40e40abbb..4d4cf65d3 100644 --- a/app/ui/css/index.less +++ b/app/ui/css/index.less @@ -24,6 +24,7 @@ @import 'components/dropdown'; @import 'components/editable'; @import 'components/environment-modal'; +@import 'components/environment-editor'; @import 'components/header-editor'; @import 'components/key-value-editor'; @import 'components/method-dropdown';