From 71e3797af8ea335dbf6415226cb53b800e807cc1 Mon Sep 17 00:00:00 2001 From: Etheryte Date: Wed, 2 Oct 2024 15:22:48 +0300 Subject: [PATCH] Clean up validation logic --- web/html/src/components/input/InputBase.tsx | 56 ++++++++------ .../src/components/input/range/Range.test.tsx | 2 +- .../input/validation/async.stories.tsx | 2 +- .../validation/multiple-messages.stories.tsx | 2 +- .../input/validation/validation.test.ts | 76 ++++++++++++++++--- .../components/input/validation/validation.ts | 64 ++++++++++++++-- .../src/manager/images/image-profile-edit.tsx | 16 ++-- .../src/manager/images/image-store-edit.tsx | 10 ++- .../nets/network-properties.tsx | 39 ++++++---- .../nets/properties/IpConfig.tsx | 11 ++- .../nets/properties/NetworkAddress.tsx | 13 ++-- .../virtualization/nets/properties/utils.ts | 6 +- .../src/vendors/npm.licenses.structured.js | 2 +- web/html/src/vendors/npm.licenses.txt | 2 +- 14 files changed, 219 insertions(+), 82 deletions(-) diff --git a/web/html/src/components/input/InputBase.tsx b/web/html/src/components/input/InputBase.tsx index bba9188e9fdc..488ada216487 100644 --- a/web/html/src/components/input/InputBase.tsx +++ b/web/html/src/components/input/InputBase.tsx @@ -168,18 +168,7 @@ export class InputBase extends React.Component { - if (name.includes(key)) { - filtered[key] = this.context.model[key]; - } - return filtered; - }, {}); - this.validate(values); - } else if (typeof name !== "undefined") { - this.validate(this.context.model[name]); - } + this.validate(this.getModelValue()); } } @@ -200,6 +189,21 @@ export class InputBase extends React.Component { + if (name.includes(key)) { + filtered[key] = this.context.model[key]; + } + return filtered; + }, {} as ValueType); + return values; + } else if (typeof name !== "undefined") { + return this.context.model[name]; + } + } + isEmptyValue(input: unknown) { if (typeof input === "string") { return input.trim() === ""; @@ -207,11 +211,10 @@ export class InputBase extends React.Component(value: T) => { + requiredHint = () => { + const value = this.getModelValue(); const hasNoValue = this.isEmptyValue(value) || - // TODO: Fix types (Array.isArray(this.props.name) && Object.values(value).filter((v) => !this.isEmptyValue(v)).length === 0); if (hasNoValue) { @@ -219,7 +222,7 @@ export class InputBase extends React.Component extends React.Component(value: InferredValueType): Promise => { const validators = Array.isArray(this.props.validate) ? this.props.validate : [this.props.validate] ?? []; - // TODO: Move this into render so it's always sync and up to date instantly - if (!this.props.disabled && this.props.required) { - validators.push(this.requiredValidator); - } - /** * Each validator sets its own result independently, this way we can mix and match different speed async * validators without having to wait all of them to finish @@ -309,10 +307,14 @@ export class InputBase extends React.Component this.pushHint(hints, item)); return; @@ -330,7 +332,13 @@ export class InputBase extends React.Component this.pushHint(hints, error)); - this.state.validationErrors.forEach((error) => this.pushHint(hints, error)); + if (this.state.isTouched) { + this.state.validationErrors.forEach((error) => this.pushHint(hints, error)); + + if (this.props.required && !this.props.disabled) { + this.pushHint(hints, this.requiredHint()); + } + } const hasError = this.state.isTouched && !this.isValid(); diff --git a/web/html/src/components/input/range/Range.test.tsx b/web/html/src/components/input/range/Range.test.tsx index 65d81b0ca595..57b34d8d5493 100644 --- a/web/html/src/components/input/range/Range.test.tsx +++ b/web/html/src/components/input/range/Range.test.tsx @@ -74,7 +74,7 @@ describe("Range", () => { if (!hasValues) { return message; } - // TODO: Replace this with Validate.isInteger or similar + const isInteger = Object.values(value).every((item) => typeof item === "string" && item.match(/^[0-9]+$/)); if (!isInteger) { return message; diff --git a/web/html/src/components/input/validation/async.stories.tsx b/web/html/src/components/input/validation/async.stories.tsx index 313fde4b0348..e4c2df58b6d5 100644 --- a/web/html/src/components/input/validation/async.stories.tsx +++ b/web/html/src/components/input/validation/async.stories.tsx @@ -18,7 +18,7 @@ export default () => { return (

Async validation with debounce:

- + ); }; diff --git a/web/html/src/components/input/validation/multiple-messages.stories.tsx b/web/html/src/components/input/validation/multiple-messages.stories.tsx index 43a2518c1943..856c922d1a60 100644 --- a/web/html/src/components/input/validation/multiple-messages.stories.tsx +++ b/web/html/src/components/input/validation/multiple-messages.stories.tsx @@ -14,7 +14,7 @@ export default () => { return (

You can return multiple validation errors:

- + ); }; diff --git a/web/html/src/components/input/validation/validation.test.ts b/web/html/src/components/input/validation/validation.test.ts index c0d5c28374dc..6c84056962f6 100644 --- a/web/html/src/components/input/validation/validation.test.ts +++ b/web/html/src/components/input/validation/validation.test.ts @@ -3,9 +3,21 @@ import { Validation } from "./validation"; const errorMessage = "error message"; describe("validation", () => { + test("matches", () => { + const validator = Validation.matches(/foo/, errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("-")).toEqual(errorMessage); + expect(validator("-foo")).toEqual(undefined); + expect(validator("-foo-")).toEqual(undefined); + expect(validator("-fo-")).toEqual(errorMessage); + }); + test("minLength string", () => { const validator = Validation.minLength(3, errorMessage); + // Here and elsewhere, if you want the value to be required, set the `required` flag instead + expect(validator("")).toEqual(undefined); expect(validator("foo")).toEqual(undefined); expect(validator("fo")).toEqual(errorMessage); }); @@ -33,6 +45,7 @@ describe("validation", () => { test("maxLength string", () => { const validator = Validation.maxLength(3, errorMessage); + expect(validator("")).toEqual(undefined); expect(validator("foo")).toEqual(undefined); expect(validator("fooo")).toEqual(errorMessage); }); @@ -58,19 +71,58 @@ describe("validation", () => { }); test("isInt", () => { - const validator = Validation.isInt(); - - // If you want the value to be required, set the `required` flag instead - expect(validator("")).toEqual(true); - expect(validator("0")).toEqual(true); - expect(validator("42")).toEqual(true); - expect(validator("42.")).toEqual(false); - expect(validator("4.2")).toEqual(false); - expect(validator("0x1")).toEqual(false); - expect(validator("foo")).toEqual(false); + const validator = Validation.isInt(errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("0")).toEqual(undefined); + expect(validator("42")).toEqual(undefined); + expect(validator("42.")).toEqual(errorMessage); + expect(validator("4.2")).toEqual(errorMessage); + expect(validator("0x1")).toEqual(errorMessage); + expect(validator("foo")).toEqual(errorMessage); }); - test("matches", () => { - // TODO: Implement + test("min", () => { + const validator = Validation.min(7, errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("6")).toEqual(errorMessage); + expect(validator("7")).toEqual(undefined); + expect(validator("8")).toEqual(undefined); + }); + + test("max", () => { + const validator = Validation.max(7, errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("6")).toEqual(undefined); + expect(validator("7")).toEqual(undefined); + expect(validator("8")).toEqual(errorMessage); + }); + + test("intRange", () => { + const validator = Validation.intRange(3, 5, errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("1.5")).toEqual(errorMessage); + expect(validator("2")).toEqual(errorMessage); + expect(validator("3")).toEqual(undefined); + expect(validator("4")).toEqual(undefined); + expect(validator("4.5")).toEqual(errorMessage); + expect(validator("5")).toEqual(undefined); + expect(validator("6")).toEqual(errorMessage); + }); + + test("floatRange", () => { + const validator = Validation.floatRange(3, 5, errorMessage); + + expect(validator("")).toEqual(undefined); + expect(validator("1.5")).toEqual(errorMessage); + expect(validator("2")).toEqual(errorMessage); + expect(validator("3")).toEqual(undefined); + expect(validator("4")).toEqual(undefined); + expect(validator("4.5")).toEqual(undefined); + expect(validator("5")).toEqual(undefined); + expect(validator("6")).toEqual(errorMessage); }); }); diff --git a/web/html/src/components/input/validation/validation.ts b/web/html/src/components/input/validation/validation.ts index 01cb9017c662..e293ba49e23b 100644 --- a/web/html/src/components/input/validation/validation.ts +++ b/web/html/src/components/input/validation/validation.ts @@ -1,15 +1,18 @@ -// TODO: Add 100% test coverage to this whole file - type OneOrMany = T | T[]; type SyncOrAsync = T | Promise; -export type ValidationResult = OneOrMany | undefined>; +export type ValidationResult = OneOrMany | undefined>; export type Validator = (...args: any[]) => SyncOrAsync; /** String must match `regex` */ const matches = (regex: RegExp, message?: string): Validator => (value: string) => { + // Here and elsewhere, if you want the value to be required, set the `required` flag instead + if (value === "") { + return; + } + if (!regex.test(value)) { return message ?? t("Doesn't match expected format"); } @@ -22,11 +25,11 @@ const minLength = (value: Record | string) => { const defaultMessage = t(`Must be at least ${length} characters long`); if (typeof value === "object") { - const isInvalid = Object.values(value).some((item) => item.length < length); + const isInvalid = Object.values(value).some((item) => item.length !== 0 && item.length < length); if (isInvalid) { return message ?? defaultMessage; } - } else if (value.length < length) { + } else if (value.length !== 0 && value.length < length) { return message ?? defaultMessage; } }; @@ -37,15 +40,16 @@ const maxLength = (value: Record | string) => { const defaultMessage = t(`Must be no more than ${length} characters long`); if (typeof value === "object") { - const isInvalid = Object.values(value).some((item) => item.length > length); + const isInvalid = Object.values(value).some((item) => item.length !== 0 && item.length > length); if (isInvalid) { return message ?? defaultMessage; } - } else if (value.length > length) { + } else if (value.length !== 0 && value.length > length) { return message ?? defaultMessage; } }; +/** String is integer */ const isInt = (message?: string): Validator => (value: string) => { @@ -59,9 +63,55 @@ const isInt = } }; +/** Value is no smaller than `minValue` */ +const min = + (minValue: number, message?: string): Validator => + (value: string) => { + if (value === "") { + return; + } + + const parsed = parseFloat(value); + if (isNaN(parsed) || parsed < minValue) { + return message ?? t(`Must be larger than ${minValue}`); + } + }; + +/** Value is no larger than `maxValue` */ +const max = + (maxValue: number, message?: string): Validator => + (value: string) => { + if (value === "") { + return; + } + + const parsed = parseFloat(value); + if (isNaN(parsed) || parsed > maxValue) { + return message ?? t(`Must be smaller than ${maxValue}`); + } + }; + +/** Value is an integer that is no smaller than `minValue` and no larger than `maxValue` */ +const intRange = + (minValue: number, maxValue: number, message?: string): Validator => + (value: string) => { + return isInt(message)(value) || min(minValue, message)(value) || max(maxValue, message)(value); + }; + +/** Value is a number that is no smaller than `minValue` and no larger than `maxValue` */ +const floatRange = + (minValue: number, maxValue: number, message?: string): Validator => + (value: string) => { + return min(minValue, message)(value) || max(maxValue, message)(value); + }; + export const Validation = { matches, minLength, maxLength, + min, + max, isInt, + intRange, + floatRange, }; diff --git a/web/html/src/manager/images/image-profile-edit.tsx b/web/html/src/manager/images/image-profile-edit.tsx index 3ee7fb03501b..de6a913a50a1 100644 --- a/web/html/src/manager/images/image-profile-edit.tsx +++ b/web/html/src/manager/images/image-profile-edit.tsx @@ -168,19 +168,22 @@ class CreateImageProfile extends React.Component { } } - isLabelValid = (label) => { + isLabelValid = async (label: string) => { if (this.state.initLabel && this.state.initLabel === label) { // Initial state (on edit), always valid. - return true; + return; } // Should not contain ':' character - const isValid = label.indexOf(":") === -1; + const matchesFormat = label.indexOf(":") === -1; // Check for uniqueness - return Network.get("/rhn/manager/api/cm/imageprofiles/find/" + label) - .then((res) => !res.success && isValid) + const isValid = Network.get("/rhn/manager/api/cm/imageprofiles/find/" + label) + .then((res) => !res.success && matchesFormat) .catch(() => false); + if (!isValid) { + return t("Label is required and must be a unique string and it cannot include any colons (:)."); + } }; onUpdate = (model) => { @@ -527,8 +530,7 @@ class CreateImageProfile extends React.Component { name="label" label={t("Label")} required - validate={[this.isLabelValid]} - invalidHint={t("Label is required and must be a unique string and it cannot include any colons (:).")} + validate={this.isLabelValid} labelClass="col-md-3" divClass="col-md-6" /> diff --git a/web/html/src/manager/images/image-store-edit.tsx b/web/html/src/manager/images/image-store-edit.tsx index 311e1cff4e7a..77406b1eccc5 100644 --- a/web/html/src/manager/images/image-store-edit.tsx +++ b/web/html/src/manager/images/image-store-edit.tsx @@ -79,14 +79,17 @@ class CreateImageStore extends React.Component { }); }; - isLabelUnique = (label) => { + isLabelUnique = async (label: string) => { if (this.state.initLabel && this.state.initLabel === label) { - return true; + return; } - return Network.get("/rhn/manager/api/cm/imagestores/find/" + label) + const isUnique = await Network.get("/rhn/manager/api/cm/imagestores/find/" + label) .then((res) => !res.success) .catch(() => false); + if (!isUnique) { + return t("Label is required and must be unique."); + } }; onUpdate = (model) => { @@ -259,7 +262,6 @@ class CreateImageStore extends React.Component { label={t("Label")} required validate={this.isLabelUnique} - invalidHint={t("Label is required and must be unique.")} labelClass="col-md-3" divClass="col-md-6" /> diff --git a/web/html/src/manager/virtualization/nets/network-properties.tsx b/web/html/src/manager/virtualization/nets/network-properties.tsx index e28f4d0b5a59..b9c14b488479 100644 --- a/web/html/src/manager/virtualization/nets/network-properties.tsx +++ b/web/html/src/manager/virtualization/nets/network-properties.tsx @@ -403,13 +403,16 @@ export function NetworkProperties(props: Props) { divClass="col-md-6" validate={[ utils.allOrNone, - (value) => - Object.values(value).every( + (value) => { + const isValid = Object.values(value).every( (item) => typeof item === "string" && (item === "" || item.match(utils.ipv4Pattern) != null) - ), + ); + if (!isValid) { + return t("Both values has to be IPv4 addresses"); + } + }, ]} - invalidHint={t("Both values has to be IPv4 addresses")} /> - Object.values(value).every( - (item) => typeof item === "string" && (item === "" || item.match(/^[0-9]+$/)) - ), - ({ nat_port_start, nat_port_end }) => - (nat_port_start === "" && nat_port_end === "") || - parseInt(nat_port_start, 10) <= parseInt(nat_port_end, 10), + (value) => { + const message = t("Both values need to be positive integers"); + const hasValues = Object.values(value).every((item) => item != null); + if (!hasValues) { + return message; + } + + const isInteger = Object.values(value).every( + (item) => typeof item === "string" && item.match(/^[0-9]+$/) + ); + if (!isInteger) { + return message; + } + const { port_start, port_end } = value; + const isOrdered = parseInt(port_start, 10) <= parseInt(port_end, 10); + if (!isOrdered) { + return message; + } + }, ]} - invalidHint={t("Both values has to be positive integers")} /> )} diff --git a/web/html/src/manager/virtualization/nets/properties/IpConfig.tsx b/web/html/src/manager/virtualization/nets/properties/IpConfig.tsx index 8955c4eca9b9..ff45252e40ac 100644 --- a/web/html/src/manager/virtualization/nets/properties/IpConfig.tsx +++ b/web/html/src/manager/virtualization/nets/properties/IpConfig.tsx @@ -58,12 +58,15 @@ export function IpConfig(props: Props) { required validate={[ utils.allOrNone, - (value) => - Object.values(value).every((item) => { + (value) => { + const isValid = Object.values(value).every((item) => { return typeof item === "string" && (item === "" || !_isNil(item.match(address_pattern))); - }), + }); + if (!isValid) { + return t(`Both values need to be IPv${ip_version} addresses`); + } + }, ]} - invalidHint={t(`Both values need to be IPv${ip_version} addresses`)} /> )} diff --git a/web/html/src/manager/virtualization/nets/properties/NetworkAddress.tsx b/web/html/src/manager/virtualization/nets/properties/NetworkAddress.tsx index cc68569a83ef..e9ca32bc9714 100644 --- a/web/html/src/manager/virtualization/nets/properties/NetworkAddress.tsx +++ b/web/html/src/manager/virtualization/nets/properties/NetworkAddress.tsx @@ -1,8 +1,8 @@ import * as React from "react"; +import { Validation } from "components/input"; import { FormContext } from "components/input/form/Form"; import { InputBase } from "components/input/InputBase"; -import Validation from "components/validation"; import * as utils from "./utils"; @@ -50,14 +50,17 @@ export const NetworkAddress = (props: Props) => { (values) => { const address = values[`${props.prefix}_address`] || ""; const prefix = values[`${props.prefix}_prefix`] || ""; + if (address === "" && prefix === "") { + return; + } + + const errorMessage = t(`Value needs to be a valid IPv${ipVersion} address with prefix`); return ( - (address === "" && prefix === "") || - (Validation.matches(ipv6 ? utils.ipv6Pattern : utils.ipv4Pattern)(address) && - Validation.isInt({ min: 0, max: ipv6 ? 128 : 32 })(prefix)) + Validation.matches(ipv6 ? utils.ipv6Pattern : utils.ipv4Pattern, errorMessage)(address) || + Validation.intRange(0, ipv6 ? 128 : 32, errorMessage)(prefix) ); }, ]} - invalidHint={t(`Value needs to be a valid IPv${ipVersion} address with prefix`)} {...propsToPass} > {({ setValue, onBlur }) => { diff --git a/web/html/src/manager/virtualization/nets/properties/utils.ts b/web/html/src/manager/virtualization/nets/properties/utils.ts index 49fb68948412..5592574a763d 100644 --- a/web/html/src/manager/virtualization/nets/properties/utils.ts +++ b/web/html/src/manager/virtualization/nets/properties/utils.ts @@ -11,5 +11,9 @@ export const macPattern = new RegExp(`^${hexDigit}{2}(?::${hexDigit}{2}){5}$`); export const uuidPattern = new RegExp(`^(?:${hexDigit}{8}-(${hexDigit}{4}-){3}${hexDigit}{12})|(?:${hexDigit}{32})$`); export const allOrNone = (value: unknown[]) => { - return Object.values(value).every((item) => item != null) || Object.values(value).every((item) => item == null); + const isValid = + Object.values(value).every((item) => item != null) || Object.values(value).every((item) => item == null); + if (!isValid) { + return t("Please provide missing values"); + } }; diff --git a/web/html/src/vendors/npm.licenses.structured.js b/web/html/src/vendors/npm.licenses.structured.js index 653346175e31..c08d337c584e 100644 --- a/web/html/src/vendors/npm.licenses.structured.js +++ b/web/html/src/vendors/npm.licenses.structured.js @@ -1 +1 @@ -const npmLicensesArray=["MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MPL-2.0","MIT","ISC","LGPL-3.0-or-later","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","(MPL-2.0 OR Apache-2.0)","BSD-3-Clause","BSD-3-Clause","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","MIT","MIT","BSD-3-Clause","BSD","BSD","BSD","BSD","BSD","BSD","BSD","BSD","BSD","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","0BSD","MIT","MIT","MIT"],npmDependencies=[{name:"@babel/polyfill",license:"MIT",version:"7.7.0"},{name:"@babel/runtime",license:"MIT",version:"7.16.3"},{name:"@emotion/cache",license:"MIT",version:"11.11.0"},{name:"@emotion/hash",license:"MIT",version:"0.9.1"},{name:"@emotion/memoize",license:"MIT",version:"0.8.1"},{name:"@emotion/react",license:"MIT",version:"11.11.1"},{name:"@emotion/serialize",license:"MIT",version:"1.1.2"},{name:"@emotion/sheet",license:"MIT",version:"1.2.2"},{name:"@emotion/unitless",license:"MIT",version:"0.8.1"},{name:"@emotion/use-insertion-effect-with-fallbacks",license:"MIT",version:"1.0.1"},{name:"@emotion/utils",license:"MIT",version:"1.2.1"},{name:"@emotion/weak-memoize",license:"MIT",version:"0.3.1"},{name:"@formatjs/ecma402-abstract",license:"MIT",version:"1.17.0"},{name:"@formatjs/fast-memoize",license:"MIT",version:"2.2.0"},{name:"@formatjs/icu-messageformat-parser",license:"MIT",version:"2.6.0"},{name:"@formatjs/icu-skeleton-parser",license:"MIT",version:"1.6.0"},{name:"@formatjs/intl",license:"MIT",version:"2.9.0"},{name:"@formatjs/intl-localematcher",license:"MIT",version:"0.4.0"},{name:"@fullcalendar/common",license:"MIT",version:"5.5.1"},{name:"@fullcalendar/core",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/daygrid",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/interaction",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/react",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/timegrid",license:"MIT",version:"5.5.0"},{name:"@hot-loader/react-dom",license:"MIT",version:"17.0.1+4.13.0"},{name:"@novnc/novnc",license:"MPL-2.0",version:"1.1.0"},{name:"@popperjs/core",license:"MIT",version:"2.11.6"},{name:"@seznam/compose-react-refs",license:"ISC",version:"1.0.6"},{name:"@spice-project/spice-html5",license:"LGPL-3.0-or-later",version:"0.2.1"},{name:"@virtuoso.dev/react-urx",license:"MIT",version:"0.2.12"},{name:"@virtuoso.dev/urx",license:"MIT",version:"0.2.12"},{name:"babel-loader",license:"MIT",version:"8.2.2"},{name:"bootstrap",license:"MIT",version:"3.4.1"},{name:"classnames",license:"MIT",version:"2.3.1"},{name:"core-js",license:"MIT",version:"3.8.1"},{name:"css-loader",license:"MIT",version:"6.10.0"},{name:"date-fns",license:"MIT",version:"2.29.3"},{name:"dom-helpers",license:"MIT",version:"5.1.4"},{name:"dompurify",license:"(MPL-2.0 OR Apache-2.0)",version:"3.0.3"},{name:"exenv",license:"BSD-3-Clause",version:"1.2.2"},{name:"hoist-non-react-statics",license:"BSD-3-Clause",version:"3.3.2"},{name:"html-dom-parser",license:"MIT",version:"0.2.3"},{name:"html-react-parser",license:"MIT",version:"0.10.0"},{name:"immer",license:"MIT",version:"9.0.6"},{name:"inline-style-parser",license:"MIT",version:"0.1.1"},{name:"intl-messageformat",license:"BSD-3-Clause",version:"10.5.0"},{name:"ip-regex",license:"MIT",version:"4.3.0"},{name:"jexl",license:"MIT",version:"2.2.2"},{name:"less-loader",license:"MIT",version:"10.2.0"},{name:"lodash",license:"MIT",version:"4.17.21"},{name:"lodash.get",license:"MIT",version:"4.4.2"},{name:"memoize-one",license:"MIT",version:"5.2.1"},{name:"metal",license:"BSD-3-Clause",version:"2.16.8"},{name:"metal-ajax",license:"BSD",version:"2.1.1"},{name:"metal-debounce",license:"BSD",version:"2.0.2"},{name:"metal-dom",license:"BSD",version:"2.16.8"},{name:"metal-events",license:"BSD",version:"2.16.8"},{name:"metal-path-parser",license:"BSD",version:"1.0.4"},{name:"metal-promise",license:"BSD",version:"2.0.1"},{name:"metal-structs",license:"BSD",version:"1.0.2"},{name:"metal-uri",license:"BSD",version:"2.4.0"},{name:"metal-useragent",license:"BSD",version:"3.0.1"},{name:"mini-css-extract-plugin",license:"MIT",version:"2.6.0"},{name:"moment",license:"MIT",version:"2.29.4"},{name:"moment-timezone",license:"MIT",version:"0.5.35"},{name:"node-gettext",license:"MIT",version:"3.0.0"},{name:"object-assign",license:"MIT",version:"4.1.1"},{name:"postcss-loader",license:"MIT",version:"8.1.0"},{name:"prop-types",license:"MIT",version:"15.7.2"},{name:"react",license:"MIT",version:"16.14.0"},{name:"react-datepicker",license:"MIT",version:"4.5.0"},{name:"react-fast-compare",license:"MIT",version:"3.2.0"},{name:"react-hot-loader",license:"MIT",version:"4.13.0"},{name:"react-input-autosize",license:"MIT",version:"3.0.0"},{name:"react-is",license:"MIT",version:"16.12.0"},{name:"react-is-mounted-hook",license:"MIT",version:"1.1.2"},{name:"react-lifecycles-compat",license:"MIT",version:"3.0.4"},{name:"react-modal",license:"MIT",version:"3.13.1"},{name:"react-onclickoutside",license:"MIT",version:"6.12.2"},{name:"react-popper",license:"MIT",version:"2.3.0"},{name:"react-property",license:"MIT",version:"1.0.1"},{name:"react-select",license:"MIT",version:"4.3.1"},{name:"react-select-async-paginate",license:"MIT",version:"0.6.0"},{name:"react-toastify",license:"MIT",version:"6.0.5"},{name:"react-transition-group",license:"BSD-3-Clause",version:"4.4.2"},{name:"react-virtuoso",license:"MIT",version:"2.5.1"},{name:"regenerator-runtime",license:"MIT",version:"0.13.5"},{name:"sass-loader",license:"MIT",version:"14.1.0"},{name:"scheduler",license:"MIT",version:"0.20.1"},{name:"senna",license:"BSD-3-Clause",version:"2.7.9"},{name:"sleep-promise",license:"MIT",version:"9.1.0"},{name:"style-loader",license:"MIT",version:"3.3.4"},{name:"style-to-object",license:"MIT",version:"0.3.0"},{name:"stylis",license:"MIT",version:"4.2.0"},{name:"tslib",license:"0BSD",version:"2.1.0"},{name:"use-immer",license:"MIT",version:"0.3.5"},{name:"validator",license:"MIT",version:"13.7.0"},{name:"warning",license:"MIT",version:"4.0.3"}];module.exports={npmLicensesArray,npmDependencies}; \ No newline at end of file +const npmLicensesArray=["MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MPL-2.0","MIT","ISC","LGPL-3.0-or-later","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","(MPL-2.0 OR Apache-2.0)","BSD-3-Clause","BSD-3-Clause","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","MIT","MIT","BSD-3-Clause","BSD","BSD","BSD","BSD","BSD","BSD","BSD","BSD","BSD","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","BSD-3-Clause","MIT","MIT","MIT","MIT","0BSD","MIT","MIT","MIT"],npmDependencies=[{name:"@babel/polyfill",license:"MIT",version:"7.7.0"},{name:"@babel/runtime",license:"MIT",version:"7.16.3"},{name:"@emotion/cache",license:"MIT",version:"11.11.0"},{name:"@emotion/hash",license:"MIT",version:"0.9.1"},{name:"@emotion/memoize",license:"MIT",version:"0.8.1"},{name:"@emotion/react",license:"MIT",version:"11.11.1"},{name:"@emotion/serialize",license:"MIT",version:"1.1.2"},{name:"@emotion/sheet",license:"MIT",version:"1.2.2"},{name:"@emotion/unitless",license:"MIT",version:"0.8.1"},{name:"@emotion/use-insertion-effect-with-fallbacks",license:"MIT",version:"1.0.1"},{name:"@emotion/utils",license:"MIT",version:"1.2.1"},{name:"@emotion/weak-memoize",license:"MIT",version:"0.3.1"},{name:"@formatjs/ecma402-abstract",license:"MIT",version:"1.17.0"},{name:"@formatjs/fast-memoize",license:"MIT",version:"2.2.0"},{name:"@formatjs/icu-messageformat-parser",license:"MIT",version:"2.6.0"},{name:"@formatjs/icu-skeleton-parser",license:"MIT",version:"1.6.0"},{name:"@formatjs/intl",license:"MIT",version:"2.9.0"},{name:"@formatjs/intl-localematcher",license:"MIT",version:"0.4.0"},{name:"@fullcalendar/common",license:"MIT",version:"5.5.1"},{name:"@fullcalendar/core",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/daygrid",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/interaction",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/react",license:"MIT",version:"5.5.0"},{name:"@fullcalendar/timegrid",license:"MIT",version:"5.5.0"},{name:"@hot-loader/react-dom",license:"MIT",version:"17.0.1+4.13.0"},{name:"@novnc/novnc",license:"MPL-2.0",version:"1.1.0"},{name:"@popperjs/core",license:"MIT",version:"2.11.6"},{name:"@seznam/compose-react-refs",license:"ISC",version:"1.0.6"},{name:"@spice-project/spice-html5",license:"LGPL-3.0-or-later",version:"0.2.1"},{name:"@virtuoso.dev/react-urx",license:"MIT",version:"0.2.12"},{name:"@virtuoso.dev/urx",license:"MIT",version:"0.2.12"},{name:"babel-loader",license:"MIT",version:"8.2.2"},{name:"bootstrap",license:"MIT",version:"3.4.1"},{name:"classnames",license:"MIT",version:"2.3.1"},{name:"core-js",license:"MIT",version:"3.8.1"},{name:"css-loader",license:"MIT",version:"6.10.0"},{name:"date-fns",license:"MIT",version:"2.29.3"},{name:"dom-helpers",license:"MIT",version:"5.1.4"},{name:"dompurify",license:"(MPL-2.0 OR Apache-2.0)",version:"3.0.3"},{name:"exenv",license:"BSD-3-Clause",version:"1.2.2"},{name:"hoist-non-react-statics",license:"BSD-3-Clause",version:"3.3.2"},{name:"html-dom-parser",license:"MIT",version:"0.2.3"},{name:"html-react-parser",license:"MIT",version:"0.10.0"},{name:"immer",license:"MIT",version:"9.0.6"},{name:"inline-style-parser",license:"MIT",version:"0.1.1"},{name:"intl-messageformat",license:"BSD-3-Clause",version:"10.5.0"},{name:"ip-regex",license:"MIT",version:"4.3.0"},{name:"jexl",license:"MIT",version:"2.2.2"},{name:"less-loader",license:"MIT",version:"10.2.0"},{name:"lodash",license:"MIT",version:"4.17.21"},{name:"lodash.get",license:"MIT",version:"4.4.2"},{name:"memoize-one",license:"MIT",version:"5.2.1"},{name:"metal",license:"BSD-3-Clause",version:"2.16.8"},{name:"metal-ajax",license:"BSD",version:"2.1.1"},{name:"metal-debounce",license:"BSD",version:"2.0.2"},{name:"metal-dom",license:"BSD",version:"2.16.8"},{name:"metal-events",license:"BSD",version:"2.16.8"},{name:"metal-path-parser",license:"BSD",version:"1.0.4"},{name:"metal-promise",license:"BSD",version:"2.0.1"},{name:"metal-structs",license:"BSD",version:"1.0.2"},{name:"metal-uri",license:"BSD",version:"2.4.0"},{name:"metal-useragent",license:"BSD",version:"3.0.1"},{name:"mini-css-extract-plugin",license:"MIT",version:"2.6.0"},{name:"moment",license:"MIT",version:"2.29.4"},{name:"moment-timezone",license:"MIT",version:"0.5.35"},{name:"node-gettext",license:"MIT",version:"3.0.0"},{name:"object-assign",license:"MIT",version:"4.1.1"},{name:"postcss-loader",license:"MIT",version:"8.1.0"},{name:"prop-types",license:"MIT",version:"15.8.1"},{name:"react",license:"MIT",version:"16.14.0"},{name:"react-datepicker",license:"MIT",version:"4.5.0"},{name:"react-fast-compare",license:"MIT",version:"3.2.0"},{name:"react-hot-loader",license:"MIT",version:"4.13.0"},{name:"react-input-autosize",license:"MIT",version:"3.0.0"},{name:"react-is",license:"MIT",version:"16.12.0"},{name:"react-is-mounted-hook",license:"MIT",version:"1.1.2"},{name:"react-lifecycles-compat",license:"MIT",version:"3.0.4"},{name:"react-modal",license:"MIT",version:"3.13.1"},{name:"react-onclickoutside",license:"MIT",version:"6.12.2"},{name:"react-popper",license:"MIT",version:"2.3.0"},{name:"react-property",license:"MIT",version:"1.0.1"},{name:"react-select",license:"MIT",version:"4.3.1"},{name:"react-select-async-paginate",license:"MIT",version:"0.6.0"},{name:"react-toastify",license:"MIT",version:"6.0.5"},{name:"react-transition-group",license:"BSD-3-Clause",version:"4.4.2"},{name:"react-virtuoso",license:"MIT",version:"2.5.1"},{name:"regenerator-runtime",license:"MIT",version:"0.13.5"},{name:"sass-loader",license:"MIT",version:"14.1.0"},{name:"scheduler",license:"MIT",version:"0.20.1"},{name:"senna",license:"BSD-3-Clause",version:"2.7.9"},{name:"sleep-promise",license:"MIT",version:"9.1.0"},{name:"style-loader",license:"MIT",version:"3.3.4"},{name:"style-to-object",license:"MIT",version:"0.3.0"},{name:"stylis",license:"MIT",version:"4.2.0"},{name:"tslib",license:"0BSD",version:"2.1.0"},{name:"use-immer",license:"MIT",version:"0.3.5"},{name:"validator",license:"MIT",version:"13.7.0"},{name:"warning",license:"MIT",version:"4.0.3"}];module.exports={npmLicensesArray,npmDependencies}; \ No newline at end of file diff --git a/web/html/src/vendors/npm.licenses.txt b/web/html/src/vendors/npm.licenses.txt index 11e411f1c1ea..8b2e3d016bd4 100644 --- a/web/html/src/vendors/npm.licenses.txt +++ b/web/html/src/vendors/npm.licenses.txt @@ -2450,7 +2450,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- -prop-types v15.7.2 +prop-types v15.8.1 facebook/prop-types --------------------------------------------------------------------------------