From cd242d7505abd59b2b609b919971ac55ac7445d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Ramos?= Date: Thu, 1 Mar 2018 01:15:16 +0000 Subject: [PATCH] feat: improved validation of attrs --- packages/icons/package.json | 2 +- packages/logos/package.json | 2 +- packages/my-joy-images/package.json | 5 +- .../src/containers/create-image/details.js | 44 ++-- .../src/containers/create-image/tags.js | 9 + packages/my-joy-images/src/containers/tags.js | 14 +- .../my-joy-images/src/state/validators.js | 67 ++++++ packages/my-joy-instances/package.json | 8 +- ...ui-js-add-service-form-pristine-1-snap.png | Bin 16054 -> 16297 bytes .../__tests__/__snapshots__/cns.spec.js.snap | 24 +- .../my-joy-instances/src/components/cns.js | 2 +- .../affinity-ui-js-header-1-snap.png | Bin 17205 -> 17257 bytes .../affinity-ui-js-header-tag-1-snap.png | Bin 17205 -> 17257 bytes .../__snapshots__/affinity.spec.js.snap | 132 ++++++----- .../__tests__/affinity.spec.js | 42 ++-- .../create-instance/__tests__/affinity.ui.js | 42 ++-- .../components/create-instance/affinity.js | 72 +++--- packages/my-joy-instances/src/constants.js | 2 - .../containers/create-instance/affinity.js | 216 ++++++++---------- .../src/containers/create-instance/cns.js | 25 +- .../src/containers/create-instance/index.js | 11 +- .../containers/create-instance/metadata.js | 15 +- .../src/containers/create-instance/name.js | 56 +---- .../src/containers/create-instance/tags.js | 20 +- ...networks-ui-js-networks-loading-1-snap.png | Bin 38667 -> 38655 bytes ...ary-ui-js-summary-loading-error-1-snap.png | Bin 9282 -> 9292 bytes ...ry-ui-js-summary-mutation-error-1-snap.png | Bin 13130 -> 13118 bytes ...ing-stopping-rebooting-removing-1-snap.png | Bin 9292 -> 9282 bytes .../src/containers/instances/cns.js | 16 +- .../src/containers/instances/metadata.js | 27 +-- .../src/containers/instances/snapshots.js | 59 ++--- .../src/containers/instances/tags.js | 24 +- .../src/graphql/get-snapshot.gql | 6 + .../my-joy-instances/src/state/validators.js | 112 +++++++++ packages/my-joy-navigation/package.json | 2 +- packages/ui-toolkit/package.json | 2 +- packages/ui-toolkit/src/form/meta.js | 4 + yarn.lock | 44 +++- 38 files changed, 580 insertions(+), 526 deletions(-) create mode 100644 packages/my-joy-images/src/state/validators.js delete mode 100644 packages/my-joy-instances/src/constants.js create mode 100644 packages/my-joy-instances/src/graphql/get-snapshot.gql create mode 100644 packages/my-joy-instances/src/state/validators.js diff --git a/packages/icons/package.json b/packages/icons/package.json index df407b21..7d497a0a 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -13,7 +13,7 @@ "build:lib": "NODE_ENV=production redrun -p build:es build:umd", "build:bundle": "echo 0", "prepublish": "NODE_ENV=production redrun build:lib", - "lint": "redrun lint:ci -- -- --fix", + "lint": "redrun lint:ci -- --fix", "lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md", "test": "NODE_ENV=test joyent-react-scripts test --env=jsdom", "test:ci": "redrun test", diff --git a/packages/logos/package.json b/packages/logos/package.json index af8021ce..43aee564 100644 --- a/packages/logos/package.json +++ b/packages/logos/package.json @@ -13,7 +13,7 @@ "build:lib": "NODE_ENV=production redrun -p build:es build:umd", "build:bundle": "echo 0", "prepublish": "NODE_ENV=production redrun build:lib", - "lint": "redrun lint:ci -- -- --fix", + "lint": "redrun lint:ci -- --fix", "lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md", "test": "echo 0", "test:ci": "redrun test", diff --git a/packages/my-joy-images/package.json b/packages/my-joy-images/package.json index 267e64bc..697f5708 100644 --- a/packages/my-joy-images/package.json +++ b/packages/my-joy-images/package.json @@ -11,9 +11,9 @@ "build:lib": "echo 0", "build:bundle": "NODE_ENV=production redrun -p build:frontend build:ssr", "prepublish": "NODE_ENV=production redrun build:bundle", - "lint": "redrun lint:ci -- -- --fix", + "lint": "redrun lint:ci -- --fix", "lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md", - "test": "redrun test:ci", + "test": "echo 0", "test:ci": "echo 0", "build:frontend": "joyent-react-scripts build", "build:ssr": "SSR=1 UMD=1 babel src --out-dir lib/app --copy-files" @@ -42,7 +42,6 @@ "lodash.uniqby": "^4.7.0", "lunr": "^2.1.6", "param-case": "^2.1.1", - "punycode": "^2.1.0", "react": "^16.2.0", "react-apollo": "^2.0.4", "react-dom": "^16.2.0", diff --git a/packages/my-joy-images/src/containers/create-image/details.js b/packages/my-joy-images/src/containers/create-image/details.js index aec1e634..5e48d5d2 100644 --- a/packages/my-joy-images/src/containers/create-image/details.js +++ b/packages/my-joy-images/src/containers/create-image/details.js @@ -2,13 +2,12 @@ import React, { Fragment } from 'react'; import { compose, graphql } from 'react-apollo'; import { set } from 'react-redux-values'; import ReduxForm from 'declarative-redux-form'; +import { Row, Col } from 'joyent-react-styled-flexboxgrid'; import { Margin } from 'styled-components-spacing'; import { change } from 'redux-form'; import { connect } from 'react-redux'; import intercept from 'apr-intercept'; import get from 'lodash.get'; -import punycode from 'punycode'; -import { Row, Col } from 'joyent-react-styled-flexboxgrid'; import { NameIcon, H3, Button, H4, P } from 'joyent-ui-toolkit'; @@ -16,7 +15,8 @@ import Title from '@components/create-image/title'; import Details from '@components/create-image/details'; import Description from '@components/description'; import GetRandomName from '@graphql/get-random-name.gql'; -import createStore from '@state/apollo-client'; +import createClient from '@state/apollo-client'; +import { instanceName as validateName } from '@state/validators'; import { Forms } from '@root/constants'; const NameContainer = ({ @@ -27,7 +27,7 @@ const NameContainer = ({ description, placeholderName, randomizing, - handleAsyncValidation, + handleAsyncValidate, shouldAsyncValidate, handleNext, handleRandomize, @@ -54,9 +54,9 @@ const NameContainer = ({ form={Forms.FORM_DETAILS} destroyOnUnmount={false} forceUnregisterOnUnmount={true} - onSubmit={handleNext} - asyncValidate={handleAsyncValidation} + asyncValidate={handleAsyncValidate} shouldAsyncValidate={shouldAsyncValidate} + onSubmit={handleNext} > {props => expanded ? ( @@ -121,6 +121,7 @@ export default compose( ({ form, values }, ownProps) => { const name = get(form, `${Forms.FORM_DETAILS}.values.name`, ''); const version = get(form, `${Forms.FORM_DETAILS}.values.version`, ''); + const description = get( form, `${Forms.FORM_DETAILS}.values.description`, @@ -128,7 +129,6 @@ export default compose( ); const proceeded = get(values, `${Forms.FORM_DETAILS}-proceeded`, false); - const randomizing = get(values, 'create-image-name-randomizing', false); return { @@ -141,35 +141,23 @@ export default compose( }; }, (dispatch, { history, match }) => ({ - shouldAsyncValidate: ({ trigger }) => trigger === 'submit', - handleAsyncValidation: async ({ name }) => { - const sanitized = punycode.encode(name).replace(/-$/, ''); - - if (sanitized !== name) { - // eslint-disable-next-line no-throw-literal - throw { - name: 'Special characters are not accepted' - }; - } - - if (!/^[a-zA-Z0-9][a-zA-Z0-9\\_\\.\\-]*$/.test(name)) { - // eslint-disable-next-line no-throw-literal - throw { - name: 'Invalid name' - }; - } - }, handleNext: () => { dispatch(set({ name: `${Forms.FORM_DETAILS}-proceeded`, value: true })); - return history.push(`/~create/${match.params.instance}/tag`); }, - handleEdit: () => history.push(`/~create/${match.params.instance}/name`), + handleEdit: () => { + dispatch(set({ name: `${Forms.FORM_DETAILS}-proceeded`, value: true })); + return history.push(`/~create/${match.params.instance}/name`); + }, + shouldAsyncValidate: ({ trigger }) => { + return trigger === 'change'; + }, + handleAsyncValidate: validateName, handleRandomize: async () => { dispatch(set({ name: 'create-image-name-randomizing', value: true })); const [err, res] = await intercept( - createStore().query({ + createClient().query({ fetchPolicy: 'network-only', query: GetRandomName }) diff --git a/packages/my-joy-images/src/containers/create-image/tags.js b/packages/my-joy-images/src/containers/create-image/tags.js index 25d7f640..9f346f3a 100644 --- a/packages/my-joy-images/src/containers/create-image/tags.js +++ b/packages/my-joy-images/src/containers/create-image/tags.js @@ -21,6 +21,7 @@ import { import Title from '@components/create-image/title'; import Description from '@components/description'; import Tag from '@components/tags'; +import { addTag as validateTag } from '@state/validators'; import { Forms } from '@root/constants'; export const Tags = ({ @@ -34,6 +35,8 @@ export const Tags = ({ handleToggleExpanded, handleCancelEdit, handleChangeAddOpen, + handleAsyncValidate, + shouldAsyncValidate, handleNext, step, handleEdit, @@ -84,6 +87,8 @@ export const Tags = ({ form={Forms.FORM_TAGS_CREATE} destroyOnUnmount={false} forceUnregisterOnUnmount={true} + asyncValidate={handleAsyncValidate} + shouldAsyncValidate={shouldAsyncValidate} onSubmit={handleAddTag} > {props => @@ -141,6 +146,10 @@ export default compose( handleEdit: () => { return history.push(`/~create/${match.params.instance}/tag`); }, + shouldAsyncValidate: ({ trigger }) => { + return trigger === 'submit'; + }, + handleAsyncValidate: validateTag, handleAddTag: value => { const toggleToClosed = set({ name: `${Forms.CREATE_TAGS}-add-open`, diff --git a/packages/my-joy-images/src/containers/tags.js b/packages/my-joy-images/src/containers/tags.js index b29b1e58..2d0d5ada 100644 --- a/packages/my-joy-images/src/containers/tags.js +++ b/packages/my-joy-images/src/containers/tags.js @@ -27,6 +27,7 @@ import Tag, { AddForm } from '@components/tags'; import ToolbarForm from '@components/toolbar'; import UpdateImageTags from '@graphql/update-image-tags.gql'; import GetTags from '@graphql/get-tags.gql'; +import { addTag as validateTag } from '@state/validators'; import parseError from '@state/parse-error'; const { TAGS_TOOLBAR_FORM, TAGS_ADD_FORM } = Forms; @@ -38,6 +39,8 @@ export const Tags = ({ error = null, mutationError = null, mutating = false, + handleAsyncValidate, + shouldAsyncValidate, handleToggleAddOpen, handleRemoveTag, handleAddTag @@ -76,7 +79,12 @@ export const Tags = ({ ) : null} - + {props => addOpen ? ( @@ -162,6 +170,10 @@ export default compose( }; }, (dispatch, { image, tags = [], updateTags, refetch }) => ({ + shouldAsyncValidate: ({ trigger }) => { + return trigger === 'submit'; + }, + handleAsyncValidate: validateTag, handleToggleAddOpen: addOpen => { dispatch(set({ name: `${image.id}-add-open`, value: addOpen })); }, diff --git a/packages/my-joy-images/src/state/validators.js b/packages/my-joy-images/src/state/validators.js new file mode 100644 index 00000000..d5f04f1c --- /dev/null +++ b/packages/my-joy-images/src/state/validators.js @@ -0,0 +1,67 @@ +import intercept from 'apr-intercept'; +import keys from 'lodash.keys'; +import reduce from 'apr-reduce'; +import assign from 'lodash.assign'; +import yup from 'yup'; + +/*****************************************************************************/ + +const validateField = async (field, value) => { + const [err] = await intercept(field.validate(value)); + return err ? err.errors.shift() : ''; +}; + +const validateSchema = async (schema, value) => { + const errors = await reduce( + keys(schema), + async (errors, name) => + assign(errors, { + [name]: await validateField(schema[name], value[name]) + }), + {} + ); + + throw errors; +}; + +/*****************************************************************************/ + +const matches = { + nameStart: /^[a-zA-Z]|\d/, + nameBody: /^([a-zA-Z]|\d|\.|_|-)+$/ +}; + +const msgs = { + required: prefix => `${prefix} must be defined.`, + nameStart: prefix => `${prefix} can only start with letters and numbers.`, + nameBody: prefix => + `${prefix} cannot contain spaces and can only contain letters, numbers, periods (.), underscores (_), and hyphens (-).` +}; + +const Schemas = { + tag: { + name: yup + .string() + .required(msgs.required('Key')) + .matches(matches.nameStart, msgs.nameStart('Key')) + .matches(matches.nameBody, msgs.nameBody('Key')), + value: yup + .string() + .required(msgs.required('Value')) + .matches(matches.nameStart, msgs.nameStart('Value')) + .matches(matches.nameBody, msgs.nameBody('Value')) + }, + instanceName: { + name: yup + .string() + .matches(matches.nameStart, msgs.nameStart('Instance name')) + .matches(matches.nameBody, msgs.nameBody('Instance Name')) + } +}; + +/*****************************************************************************/ + +export const addTag = tag => validateSchema(Schemas.tag, tag); + +export const instanceName = ({ name }) => + !name ? null : validateSchema(Schemas.instanceName, { name }); diff --git a/packages/my-joy-instances/package.json b/packages/my-joy-instances/package.json index 96bd9647..e3f39a12 100644 --- a/packages/my-joy-instances/package.json +++ b/packages/my-joy-instances/package.json @@ -11,7 +11,7 @@ "build:lib": "echo 0", "build:bundle": "NODE_ENV=production redrun -p build:frontend build:ssr", "prepublish": "NODE_ENV=production redrun build:bundle", - "lint": "redrun lint:ci -- -- --fix", + "lint": "redrun lint:ci -- --fix", "lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md", "test": "DEFAULT_TIMEOUT_INTERVAL=100000 NODE_ENV=test joyent-react-scripts test --env=jsdom", "test:ci": "NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'", @@ -22,6 +22,7 @@ "@manaflair/redux-batch": "^0.1.0", "apollo": "^0.2.2", "apr-intercept": "^3.0.3", + "apr-reduce": "^3.0.3", "bytes": "^3.0.0", "clipboard-copy": "^1.4.2", "constant-case": "^2.0.0", @@ -47,13 +48,13 @@ "lodash.isfunction": "^3.0.9", "lodash.isinteger": "^4.0.4", "lodash.omit": "^4.5.0", + "lodash.reduce": "^4.6.0", "lodash.reverse": "^4.0.1", "lodash.some": "^4.6.0", "lodash.sortby": "^4.7.0", "lodash.uniqby": "^4.7.0", "lodash.values": "^4.3.0", "param-case": "^2.1.1", - "punycode": "^2.1.0", "query-string": "^5.1.0", "react": "^16.2.0", "react-apollo": "^2.0.4", @@ -68,7 +69,8 @@ "styled-components": "^3.1.6", "styled-components-spacing": "^2.1.3", "styled-flex-component": "^2.2.1", - "title-case": "^2.1.1" + "title-case": "^2.1.1", + "yup": "^0.24.1" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/packages/my-joy-instances/src/components/__tests__/__image_snapshots__/cns-ui-js-add-service-form-pristine-1-snap.png b/packages/my-joy-instances/src/components/__tests__/__image_snapshots__/cns-ui-js-add-service-form-pristine-1-snap.png index f11153e343959bcd4770cbc17d70dd783b1ad6db..e7bb037d9ab5c862dabe9d9ab52bf1d9ffbd8828 100644 GIT binary patch literal 16297 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 literal 16054 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 q*oO^owf^PL4gVMp without throwing 1`] = ` background-color: rgb(59,70,204); border-radius: 0.25rem; border: solid 0.0625rem rgb(45,56,132); - cursor: not-allowed; - pointer-events: none; - color: rgb(216,216,216); - background-color: rgb(250,250,250); - border-color: rgb(216,216,216); } .c7:focus { @@ -500,23 +495,6 @@ exports[`renders without throwing 1`] = ` pointer-events: none; } -.c7:focus { - background-color: rgb(250,250,250); - border-color: rgb(216,216,216); -} - -.c7:hover { - background-color: rgb(250,250,250); - border-color: rgb(250,250,250); -} - -.c7:active, -.c7:active:hover, -.c7:active:focus { - background-color: rgb(250,250,250); - border-color: rgb(250,250,250); -} - .c3 { font-size: 0.9375rem; line-height: 1.125rem; @@ -678,7 +656,7 @@ exports[`renders without throwing 1`] = ` >