From: Lucas Di Pentima Date: Tue, 21 Jan 2020 15:11:24 +0000 (-0300) Subject: 15672: Merge branch 'master' into 15672-subprocess-list-v2 X-Git-Tag: 2.0.0~15^2 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/7be9038d988e4dd566536be646769f720b1ae04c?hp=ea35db0fe5387ed9cca2f8a8fabeaea10f89de67 15672: Merge branch 'master' into 15672-subprocess-list-v2 Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima --- diff --git a/src/common/config.ts b/src/common/config.ts index 4d4c1be7..f44dc168 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -54,6 +54,9 @@ export interface ClusterConfigJSON { Login: { LoginCluster: string; }; + Collections: { + ForwardSlashNameSubstitution: string; + }; } export class Config { @@ -164,6 +167,9 @@ export const mockClusterConfigJSON = (config: Partial): Clust Login: { LoginCluster: "", }, + Collections: { + ForwardSlashNameSubstitution: "", + }, ...config }); diff --git a/src/components/warning/warning.tsx b/src/components/warning/warning.tsx index 9a49ff0f..bd330361 100644 --- a/src/components/warning/warning.tsx +++ b/src/components/warning/warning.tsx @@ -4,8 +4,10 @@ import * as React from "react"; import { ErrorIcon } from "~/components/icon/icon"; -import { invalidNamingRules } from "~/validators/valid-name"; import { Tooltip } from "@material-ui/core"; +import { disallowSlash } from "~/validators/valid-name"; +import { connect } from "react-redux"; +import { RootState } from "~/store/store"; interface WarningComponentProps { text: string; @@ -15,16 +17,24 @@ interface WarningComponentProps { export const WarningComponent = ({ text, rules, message }: WarningComponentProps) => rules.find(aRule => text.match(aRule) !== null) - ? message - ? - : - : null; + ? message + ? + : + : null; interface IllegalNamingWarningProps { name: string; + validate: RegExp[]; } -export const IllegalNamingWarning = ({ name }: IllegalNamingWarningProps) => - ; \ No newline at end of file + +export const IllegalNamingWarning = connect( + (state: RootState) => { + return { + validate: (state.auth.config.clusterConfig.Collections.ForwardSlashNameSubstitution === "" ? + [disallowSlash] : []) + }; + })(({ name, validate }: IllegalNamingWarningProps) => + ); diff --git a/src/validators/valid-name.tsx b/src/validators/valid-name.tsx index 468811d8..da967123 100644 --- a/src/validators/valid-name.tsx +++ b/src/validators/valid-name.tsx @@ -2,13 +2,19 @@ // // SPDX-License-Identifier: AGPL-3.0 +export const disallowDotName = /^\.{1,2}$/; +export const disallowSlash = /\//; const ERROR_MESSAGE = "Name cannot be '.' or '..' or contain '/' characters"; -export const invalidNamingRules = [/\//, /^\.{1,2}$/]; - export const validName = (value: string) => { - return invalidNamingRules.find(aRule => value.match(aRule) !== null) + return [disallowDotName, disallowSlash].find(aRule => value.match(aRule) !== null) ? ERROR_MESSAGE : undefined; }; + +export const validNameAllowSlash = (value: string) => { + return [disallowDotName].find(aRule => value.match(aRule) !== null) + ? "Name cannot be '.' or '..'" + : undefined; +}; diff --git a/src/validators/validators.tsx b/src/validators/validators.tsx index 13ce4e6a..605d051c 100644 --- a/src/validators/validators.tsx +++ b/src/validators/validators.tsx @@ -6,14 +6,16 @@ import { require } from './require'; import { maxLength } from './max-length'; import { isRsaKey } from './is-rsa-key'; import { isRemoteHost } from "./is-remote-host"; -import { validName } from "./valid-name"; +import { validName, validNameAllowSlash } from "./valid-name"; export const TAG_KEY_VALIDATION = [require, maxLength(255)]; export const TAG_VALUE_VALIDATION = [require, maxLength(255)]; export const PROJECT_NAME_VALIDATION = [require, validName, maxLength(255)]; +export const PROJECT_NAME_VALIDATION_ALLOW_SLASH = [require, validNameAllowSlash, maxLength(255)]; export const COLLECTION_NAME_VALIDATION = [require, validName, maxLength(255)]; +export const COLLECTION_NAME_VALIDATION_ALLOW_SLASH = [require, validNameAllowSlash, maxLength(255)]; export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)]; export const COLLECTION_PROJECT_VALIDATION = [require]; diff --git a/src/views-components/form-fields/collection-form-fields.tsx b/src/views-components/form-fields/collection-form-fields.tsx index f6dc5d55..b3a3c224 100644 --- a/src/views-components/form-fields/collection-form-fields.tsx +++ b/src/views-components/form-fields/collection-form-fields.tsx @@ -3,19 +3,37 @@ // SPDX-License-Identifier: AGPL-3.0 import * as React from "react"; -import { Field } from "redux-form"; +import { Field, Validator } from "redux-form"; import { TextField } from "~/components/text-field/text-field"; -import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION } from "~/validators/validators"; +import { + COLLECTION_NAME_VALIDATION, COLLECTION_NAME_VALIDATION_ALLOW_SLASH, + COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION +} from "~/validators/validators"; import { ProjectTreePickerField, CollectionTreePickerField } from "~/views-components/projects-tree-picker/tree-picker-field"; import { PickerIdProp } from '~/store/tree-picker/picker-id'; +import { connect } from "react-redux"; +import { RootState } from "~/store/store"; -export const CollectionNameField = () => - ; +interface CollectionNameFieldProps { + validate: Validator[]; +} + +// See implementation note on declaration of ProjectNameField + +export const CollectionNameField = connect( + (state: RootState) => { + return { + validate: (state.auth.config.clusterConfig.Collections.ForwardSlashNameSubstitution === "" ? + COLLECTION_NAME_VALIDATION : COLLECTION_NAME_VALIDATION_ALLOW_SLASH) + }; + })((props: CollectionNameFieldProps) => + + ); export const CollectionDescriptionField = () => - ; +interface ProjectNameFieldProps { + validate: Validator[]; +} + +// Validation behavior depends on the value of ForwardSlashNameSubstitution. +// +// Redux form doesn't let you pass anonymous functions to 'validate' +// -- it fails with a very confusing recursive-update-exceeded error. +// So we can't construct the validation function on the fly. +// +// As a workaround, use ForwardSlashNameSubstitution to choose between one of two const-defined validators. + +export const ProjectNameField = connect( + (state: RootState) => { + return { + validate: (state.auth.config.clusterConfig.Collections.ForwardSlashNameSubstitution === "" ? + PROJECT_NAME_VALIDATION : PROJECT_NAME_VALIDATION_ALLOW_SLASH) + }; + })((props: ProjectNameFieldProps) => + + ); export const ProjectDescriptionField = () =>