From bd86b5341262d4b1f8e399e7d33e8116886da0a7 Mon Sep 17 00:00:00 2001 From: Panchajanya Mysarla <106101699+mpanchajanya@users.noreply.github.com> Date: Mon, 6 Jun 2022 14:55:30 -0500 Subject: [PATCH] Kickstart UI: Refactors Labels Component to Shared TKG Labels (#2515) * Issue #1620 - TKG Labels * update lock * add spaces on imports --- .gitignore | 2 +- pkg/v1/tkg/web/package-lock.json | 369 ++++++++++++++---- pkg/v1/tkg/web/package.json | 1 + pkg/v1/tkg/web/src/app/app.module.ts | 12 +- .../shared/service/user-data-form.service.ts | 41 +- .../aws-wizard/aws-wizard.component.html | 20 +- .../aws-wizard/aws-wizard.component.spec.ts | 13 +- .../aws-wizard/aws-wizard.component.ts | 20 +- .../load-balancer-step.component.html | 150 ++----- .../load-balancer-step.component.spec.ts | 30 +- .../load-balancer-step.component.ts | 244 ++++++------ .../load-balancer-step.fieldmapping.ts | 60 ++- .../metadata-step.component.html | 97 +---- .../metadata-step.component.spec.ts | 34 +- .../metadata-step/metadata-step.component.ts | 189 ++------- .../metadata-step.fieldmapping.ts | 48 ++- .../interfaces/tkg-labels.interface.ts | 16 + .../tkg-labels/tkg-labels.component.html | 95 +++++ .../tkg-labels/tkg-labels.component.scss | 20 + .../tkg-labels/tkg-labels.component.spec.ts | 106 +++++ .../tkg-labels/tkg-labels.component.ts | 69 ++++ .../shared/constants/validation.constants.ts | 8 +- .../shared/field-mapping/FieldMapping.ts | 12 + .../landing/wizard/shared/utils/form-utils.ts | 50 ++- .../shared/validation/validation.service.ts | 121 +++--- .../wizard/shared/wizard-base/wizard-base.ts | 217 +++++++--- .../wizard/shared/wizard-shared.module.ts | 19 +- .../tkg/web/src/contextualHelpDocs/index.json | 2 +- 28 files changed, 1257 insertions(+), 808 deletions(-) create mode 100644 pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/interfaces/tkg-labels.interface.ts create mode 100644 pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.html create mode 100644 pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.scss create mode 100644 pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.spec.ts create mode 100644 pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.ts diff --git a/.gitignore b/.gitignore index 6cc5dd2c2c..56a12276d8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,4 @@ hack/providers-sync-tools/**/build # Build artifacts related to packages /build /packages/package-values-sha256.yaml -/packages/**/.imgpkg \ No newline at end of file +/packages/**/.imgpkg diff --git a/pkg/v1/tkg/web/package-lock.json b/pkg/v1/tkg/web/package-lock.json index 0adfa3500f..15c8e878e2 100644 --- a/pkg/v1/tkg/web/package-lock.json +++ b/pkg/v1/tkg/web/package-lock.json @@ -20,6 +20,7 @@ "@clr/icons": "^12.0.7", "@clr/ui": "^12.0.7", "@ctrl/ngx-codemirror": "2.2.1", + "@rxweb/reactive-form-validators": "^2.1.6", "@types/lodash": "^4.14.170", "@webcomponents/custom-elements": "1.3.2", "@webcomponents/webcomponentsjs": "^2.6.0", @@ -1003,6 +1004,7 @@ "version": "12.2.14", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.14.tgz", "integrity": "sha512-EktEOF2xnuMsUyanXjZw3hyn7w97NX9h8LJ3O9l27secbjYXhyrao5bmrMILdDTEJNeZSC/OuCga1pvdaJTYmg==", + "dev": true, "dependencies": { "@babel/core": "^7.8.6", "@babel/types": "^7.8.6", @@ -1037,6 +1039,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.16.0" }, @@ -1048,6 +1051,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -1077,6 +1081,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1089,6 +1094,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -1097,6 +1103,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -1105,6 +1112,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -1118,6 +1126,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1130,6 +1139,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -1138,6 +1148,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -1151,6 +1162,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1163,6 +1175,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -1174,6 +1187,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1186,6 +1200,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -1197,6 +1212,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1209,6 +1225,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -1220,6 +1237,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1232,6 +1250,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1240,6 +1259,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -1253,6 +1273,7 @@ "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1264,6 +1285,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -1277,6 +1299,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1289,6 +1312,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -1308,6 +1332,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -1320,6 +1345,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -1334,6 +1360,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -1342,6 +1369,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -1355,6 +1383,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -1365,12 +1394,14 @@ "node_modules/@angular/compiler-cli/node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true }, "node_modules/@angular/compiler-cli/node_modules/yargs": { "version": "17.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -1388,6 +1419,7 @@ "version": "21.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true, "engines": { "node": ">=12" } @@ -1629,6 +1661,7 @@ "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1726,6 +1759,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", @@ -1743,6 +1777,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, "bin": { "semver": "bin/semver.js" } @@ -2042,6 +2077,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2053,6 +2089,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2061,6 +2098,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2073,6 +2111,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2084,6 +2123,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2092,6 +2132,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2104,6 +2145,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.16.0", "@babel/helper-replace-supers": "^7.16.0", @@ -2122,6 +2164,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.16.0" }, @@ -2133,6 +2176,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -2146,6 +2190,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -2159,6 +2204,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2170,6 +2216,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2181,6 +2228,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2192,6 +2240,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2200,6 +2249,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -2213,6 +2263,7 @@ "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -2224,6 +2275,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -2237,6 +2289,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -2256,6 +2309,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2268,6 +2322,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2279,6 +2334,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2287,6 +2343,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2356,6 +2413,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.16.0", "@babel/helper-optimise-call-expression": "^7.16.0", @@ -2370,6 +2428,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.16.0" }, @@ -2381,6 +2440,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -2394,6 +2454,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -2407,6 +2468,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2418,6 +2480,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2429,6 +2492,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2440,6 +2504,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2448,6 +2513,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -2461,6 +2527,7 @@ "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -2472,6 +2539,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -2485,6 +2553,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -2504,6 +2573,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2516,6 +2586,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, "dependencies": { "@babel/types": "^7.16.0" }, @@ -2527,6 +2598,7 @@ "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -2535,6 +2607,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -2600,6 +2673,7 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -4749,6 +4823,19 @@ "read-package-json-fast": "^2.0.1" } }, + "node_modules/@rxweb/reactive-form-validators": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@rxweb/reactive-form-validators/-/reactive-form-validators-2.1.6.tgz", + "integrity": "sha512-16NSt644vKppUGDiTgO9ij7b/oW1BTNjTfCPWnx9Qa6VbKOrggGMLZNMewSmoXFBoErBYSCQLEwY4wrLfix25A==", + "dependencies": { + "tslib": "2.2.0" + } + }, + "node_modules/@rxweb/reactive-form-validators/node_modules/tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + }, "node_modules/@schematics/angular": { "version": "12.2.14", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.14.tgz", @@ -5435,6 +5522,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -6018,6 +6106,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, "engines": { "node": ">=8" } @@ -6162,6 +6251,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -6173,6 +6263,7 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "dev": true, "dependencies": { "caniuse-lite": "^1.0.30001280", "electron-to-chromium": "^1.3.896", @@ -6404,6 +6495,7 @@ "version": "1.0.30001286", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/browserslist" @@ -6412,7 +6504,8 @@ "node_modules/canonical-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==" + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true }, "node_modules/caseless": { "version": "0.12.0", @@ -6443,6 +6536,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -6463,6 +6557,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -8067,6 +8162,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, "engines": { "node": ">= 0.6.0" } @@ -8234,7 +8330,8 @@ "node_modules/electron-to-chromium": { "version": "1.4.16", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.16.tgz", - "integrity": "sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA==" + "integrity": "sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -9269,6 +9366,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -9664,6 +9762,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -10639,6 +10738,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -10735,6 +10835,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10763,6 +10864,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -10810,6 +10912,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -11944,6 +12047,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -11955,6 +12059,7 @@ "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, "dependencies": { "sourcemap-codec": "^1.4.4" } @@ -12719,7 +12824,8 @@ "node_modules/node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true }, "node_modules/nopt": { "version": "5.0.0", @@ -12758,6 +12864,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -13662,12 +13769,14 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -16589,6 +16698,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -16624,7 +16734,8 @@ "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true }, "node_modules/regenerate": { "version": "1.4.2", @@ -18168,7 +18279,8 @@ "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "node_modules/spdx-correct": { "version": "3.1.1", @@ -18933,6 +19045,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -19112,6 +19225,7 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20449,7 +20563,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -21261,6 +21376,7 @@ "version": "12.2.14", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-12.2.14.tgz", "integrity": "sha512-EktEOF2xnuMsUyanXjZw3hyn7w97NX9h8LJ3O9l27secbjYXhyrao5bmrMILdDTEJNeZSC/OuCga1pvdaJTYmg==", + "dev": true, "requires": { "@babel/core": "^7.8.6", "@babel/types": "^7.8.6", @@ -21282,6 +21398,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "requires": { "@babel/highlight": "^7.16.0" } @@ -21290,6 +21407,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.16.0.tgz", "integrity": "sha512-mYZEvshBRHGsIAiyH5PzCFTCfbWfoYbO/jcSdXQSUQu1/pW0xDZAUP7KEc32heqWTAfAHhV9j1vH8Sav7l+JNQ==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -21312,6 +21430,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21320,12 +21439,14 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -21333,6 +21454,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "requires": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -21343,6 +21465,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21351,7 +21474,8 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true } } }, @@ -21359,6 +21483,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -21369,6 +21494,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21380,6 +21506,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -21388,6 +21515,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21399,6 +21527,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -21407,6 +21536,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21418,6 +21548,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -21426,6 +21557,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21436,12 +21568,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/highlight": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -21451,12 +21585,14 @@ "@babel/parser": { "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", - "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true }, "@babel/template": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -21467,6 +21603,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21478,6 +21615,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -21494,6 +21632,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -21505,6 +21644,7 @@ "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -21512,12 +21652,14 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -21528,6 +21670,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -21535,12 +21678,14 @@ "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true }, "yargs": { "version": "17.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.0.tgz", "integrity": "sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew==", + "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -21554,7 +21699,8 @@ "yargs-parser": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==" + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true } } }, @@ -21718,7 +21864,8 @@ "@babel/compat-data": { "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", - "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==" + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==", + "dev": true }, "@babel/core": { "version": "7.8.3", @@ -21793,6 +21940,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "dev": true, "requires": { "@babel/compat-data": "^7.16.0", "@babel/helper-validator-option": "^7.14.5", @@ -21803,7 +21951,8 @@ "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true } } }, @@ -22032,6 +22181,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz", "integrity": "sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -22039,12 +22189,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22056,6 +22208,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -22063,12 +22216,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22080,6 +22235,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz", "integrity": "sha512-My4cr9ATcaBbmaEa8M0dZNA74cfI6gitvUAskgDtAFmAqyFKDSHQo5YstxPbN+lzHl2D9l/YOEFqb2mtUh4gfA==", + "dev": true, "requires": { "@babel/helper-module-imports": "^7.16.0", "@babel/helper-replace-supers": "^7.16.0", @@ -22095,6 +22251,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "requires": { "@babel/highlight": "^7.16.0" } @@ -22103,6 +22260,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "requires": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -22113,6 +22271,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -22123,6 +22282,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22131,6 +22291,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22139,6 +22300,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22146,12 +22308,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/highlight": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -22161,12 +22325,14 @@ "@babel/parser": { "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", - "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true }, "@babel/template": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -22177,6 +22343,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -22193,6 +22360,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22204,6 +22372,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz", "integrity": "sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -22211,12 +22380,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22272,6 +22443,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz", "integrity": "sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==", + "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.16.0", "@babel/helper-optimise-call-expression": "^7.16.0", @@ -22283,6 +22455,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "dev": true, "requires": { "@babel/highlight": "^7.16.0" } @@ -22291,6 +22464,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.0.tgz", "integrity": "sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==", + "dev": true, "requires": { "@babel/types": "^7.16.0", "jsesc": "^2.5.1", @@ -22301,6 +22475,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.16.0", "@babel/template": "^7.16.0", @@ -22311,6 +22486,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22319,6 +22495,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22327,6 +22504,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "dev": true, "requires": { "@babel/types": "^7.16.0" } @@ -22334,12 +22512,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/highlight": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "chalk": "^2.0.0", @@ -22349,12 +22529,14 @@ "@babel/parser": { "version": "7.16.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz", - "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==" + "integrity": "sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng==", + "dev": true }, "@babel/template": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/parser": "^7.16.0", @@ -22365,6 +22547,7 @@ "version": "7.16.3", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.3.tgz", "integrity": "sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag==", + "dev": true, "requires": { "@babel/code-frame": "^7.16.0", "@babel/generator": "^7.16.0", @@ -22381,6 +22564,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22392,6 +22576,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz", "integrity": "sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw==", + "dev": true, "requires": { "@babel/types": "^7.16.0" }, @@ -22399,12 +22584,14 @@ "@babel/helper-validator-identifier": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", - "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true }, "@babel/types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.15.7", "to-fast-properties": "^2.0.0" @@ -22455,7 +22642,8 @@ "@babel/helper-validator-option": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", - "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==" + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true }, "@babel/helper-wrap-function": { "version": "7.16.0", @@ -23795,14 +23983,12 @@ "@clr/icons": { "version": "12.0.8", "resolved": "https://registry.npmjs.org/@clr/icons/-/icons-12.0.8.tgz", - "integrity": "sha512-nGvpRtLvkeHzDbF/lZoWhkc9to1bAiQsu4TWHjPuEDYiD6NMk75oG6L4DtjHNQo6RLavauqFBpdnUSyWORYZqg==", - "requires": {} + "integrity": "sha512-nGvpRtLvkeHzDbF/lZoWhkc9to1bAiQsu4TWHjPuEDYiD6NMk75oG6L4DtjHNQo6RLavauqFBpdnUSyWORYZqg==" }, "@clr/ui": { "version": "12.0.8", "resolved": "https://registry.npmjs.org/@clr/ui/-/ui-12.0.8.tgz", - "integrity": "sha512-OlJRCcufCgQZ3Lb2b8tFUkxQXGIUeZiNfK0fgSrCVrEMTy/eCebQgiZMNoHN7ePyraSg+o8u7rC6JiqEHojXMQ==", - "requires": {} + "integrity": "sha512-OlJRCcufCgQZ3Lb2b8tFUkxQXGIUeZiNfK0fgSrCVrEMTy/eCebQgiZMNoHN7ePyraSg+o8u7rC6JiqEHojXMQ==" }, "@colors/colors": { "version": "1.5.0", @@ -23865,8 +24051,7 @@ "version": "12.2.16", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-12.2.16.tgz", "integrity": "sha512-Y2wYX0ybpTYCuSXrG7+3FAtL4dSa7D1vMxymeJEmPTf5QcFTbllwcGQ82Q9lzTn3UDxvt6ZqAYfmWZHGp2GQ9w==", - "dev": true, - "requires": {} + "dev": true }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -23991,6 +24176,21 @@ "read-package-json-fast": "^2.0.1" } }, + "@rxweb/reactive-form-validators": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@rxweb/reactive-form-validators/-/reactive-form-validators-2.1.6.tgz", + "integrity": "sha512-16NSt644vKppUGDiTgO9ij7b/oW1BTNjTfCPWnx9Qa6VbKOrggGMLZNMewSmoXFBoErBYSCQLEwY4wrLfix25A==", + "requires": { + "tslib": "2.2.0" + }, + "dependencies": { + "tslib": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" + } + } + }, "@schematics/angular": { "version": "12.2.14", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-12.2.14.tgz", @@ -24436,8 +24636,7 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "adjust-sourcemap-loader": { "version": "4.0.0", @@ -24501,8 +24700,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv-formats": { "version": "2.1.0", @@ -24537,8 +24735,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "alphanum-sort": { "version": "1.0.2", @@ -24584,6 +24781,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -25031,7 +25229,8 @@ "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true }, "bindings": { "version": "1.5.0", @@ -25156,6 +25355,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -25164,6 +25364,7 @@ "version": "4.18.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.18.1.tgz", "integrity": "sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ==", + "dev": true, "requires": { "caniuse-lite": "^1.0.30001280", "electron-to-chromium": "^1.3.896", @@ -25338,12 +25539,14 @@ "caniuse-lite": { "version": "1.0.30001286", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz", - "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==" + "integrity": "sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ==", + "dev": true }, "canonical-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", - "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==" + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true }, "caseless": { "version": "0.12.0", @@ -25371,6 +25574,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -25386,6 +25590,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "optional": true } } @@ -25406,8 +25611,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz", "integrity": "sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==", - "dev": true, - "requires": {} + "dev": true }, "class-utils": { "version": "0.3.6", @@ -26376,8 +26580,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-2.0.1.tgz", "integrity": "sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==", - "dev": true, - "requires": {} + "dev": true }, "csso": { "version": "4.2.0", @@ -26591,7 +26794,8 @@ "dependency-graph": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==" + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true }, "destroy": { "version": "1.0.4", @@ -26729,7 +26933,8 @@ "electron-to-chromium": { "version": "1.4.16", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.16.tgz", - "integrity": "sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA==" + "integrity": "sha512-BQb7FgYwnu6haWLU63/CdVW+9xhmHls3RCQUFiV4lvw3wimEHTVcUk2hkuZo76QhR8nnDdfZE7evJIZqijwPdA==", + "dev": true }, "emoji-regex": { "version": "8.0.0", @@ -26801,8 +27006,7 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, - "requires": {} + "dev": true } } }, @@ -27501,6 +27705,7 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -27798,6 +28003,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -28270,8 +28476,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ieee754": { "version": "1.2.1", @@ -28567,6 +28772,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -28632,7 +28838,8 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true }, "is-finite": { "version": "1.1.0", @@ -28649,6 +28856,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -28685,7 +28893,8 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-path-cwd": { "version": "2.2.0", @@ -29234,8 +29443,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.6.0.tgz", "integrity": "sha512-ELO9yf0cNqpzaNLsfFgXd/wxZVYkE2+ECUwhMHUD4PZ17kcsPsYsVyjquiRqyMn2jkd2sHt0IeMyAyq1MC23Fw==", - "dev": true, - "requires": {} + "dev": true }, "karma-source-map-support": { "version": "1.4.0", @@ -29574,6 +29782,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -29582,6 +29791,7 @@ "version": "0.25.7", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, "requires": { "sourcemap-codec": "^1.4.4" } @@ -30169,7 +30379,8 @@ "node-releases": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", - "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==", + "dev": true }, "nopt": { "version": "5.0.0", @@ -30203,7 +30414,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "normalize-range": { "version": "0.1.2", @@ -30901,12 +31113,14 @@ "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true }, "pify": { "version": "4.0.1", @@ -31407,29 +31621,25 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.0.1.tgz", "integrity": "sha512-lgZBPTDvWrbAYY1v5GYEv8fEO/WhKOu/hmZqmCYfrpD6eyDWWzAOsl2rF29lpvziKO02Gc5GJQtlpkTmakwOWg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-duplicates": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.1.tgz", "integrity": "sha512-svx747PWHKOGpAXXQkCc4k/DsWo+6bc5LsVrAsw+OU+Ibi7klFZCyX54gjYzX4TH+f2uzXjRviLARxkMurA2bA==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-empty": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.0.1.tgz", "integrity": "sha512-vfU8CxAQ6YpMxV2SvMcMIyF2LX1ZzWpy0lqHDsOdaKKLQVQGVP1pzhrI9JlsO65s66uQTfkQBKBD/A5gp9STFw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-overridden": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.0.1.tgz", "integrity": "sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==", - "dev": true, - "requires": {} + "dev": true }, "postcss-double-position-gradients": { "version": "1.0.0", @@ -31900,8 +32110,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -31969,8 +32178,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.0.1.tgz", "integrity": "sha512-6J40l6LNYnBdPSk+BHZ8SF+HAkS4q2twe5jnocgd+xWpz/mx/5Sa32m3W1AA8uE8XaXN+eg8trIlfu8V9x61eg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-normalize-display-values": { "version": "5.0.1", @@ -33086,6 +33294,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -33114,7 +33323,8 @@ "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true }, "regenerate": { "version": "1.4.2", @@ -34339,7 +34549,8 @@ "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true }, "spdx-correct": { "version": "3.1.1", @@ -34612,8 +34823,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.2.1.tgz", "integrity": "sha512-1k9ZosJCRFaRbY6hH49JFlRB0fVSbmnyq1iTPjNxUmGVjBNEmwrrHPenhlp+Lgo51BojHSf6pl2FcqYaN3PfVg==", - "dev": true, - "requires": {} + "dev": true }, "stylehacks": { "version": "5.0.1", @@ -34931,6 +35141,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "requires": { "is-number": "^7.0.0" } @@ -35062,7 +35273,8 @@ "typescript": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==" + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true }, "ua-parser-js": { "version": "0.7.31", @@ -36112,7 +36324,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "1.10.2", diff --git a/pkg/v1/tkg/web/package.json b/pkg/v1/tkg/web/package.json index 577280feee..5fc81bde13 100644 --- a/pkg/v1/tkg/web/package.json +++ b/pkg/v1/tkg/web/package.json @@ -35,6 +35,7 @@ "@clr/icons": "^12.0.7", "@clr/ui": "^12.0.7", "@ctrl/ngx-codemirror": "2.2.1", + "@rxweb/reactive-form-validators": "^2.1.6", "@types/lodash": "^4.14.170", "@webcomponents/custom-elements": "1.3.2", "@webcomponents/webcomponentsjs": "^2.6.0", diff --git a/pkg/v1/tkg/web/src/app/app.module.ts b/pkg/v1/tkg/web/src/app/app.module.ts index 9b0762a681..7b49d1301a 100644 --- a/pkg/v1/tkg/web/src/app/app.module.ts +++ b/pkg/v1/tkg/web/src/app/app.module.ts @@ -3,21 +3,18 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - // Third-party imports import { LogMonitorModule } from 'ngx-log-monitor'; import { CodemirrorModule } from '@ctrl/ngx-codemirror'; - +import { RxReactiveFormsModule } from '@rxweb/reactive-form-validators'; // Module imports import { APIClientModule } from './swagger/index'; import { AppRoutingModule } from './app-routing.module'; import { SharedModule } from './shared/shared.module'; - // Component imports import { AppComponent } from './app.component'; import { HeaderBarModule } from './shared/components/header-bar/header-bar.module'; import { ThemeToggleComponent } from './shared/components/theme-toggle/theme-toggle.component'; - // Service imports import { BrandingService } from './shared/service/branding.service'; import { WebsocketService } from './shared/service/websocket.service'; @@ -42,7 +39,9 @@ import { WebsocketService } from './shared/service/websocket.service'; } }), SharedModule, - CodemirrorModule + CodemirrorModule, + + RxReactiveFormsModule ], providers: [ BrandingService, @@ -50,4 +49,5 @@ import { WebsocketService } from './shared/service/websocket.service'; ], bootstrap: [AppComponent] }) -export class AppModule { } +export class AppModule { +} diff --git a/pkg/v1/tkg/web/src/app/shared/service/user-data-form.service.ts b/pkg/v1/tkg/web/src/app/shared/service/user-data-form.service.ts index a12610b3fc..e359635fe3 100644 --- a/pkg/v1/tkg/web/src/app/shared/service/user-data-form.service.ts +++ b/pkg/v1/tkg/web/src/app/shared/service/user-data-form.service.ts @@ -1,12 +1,16 @@ -import { BackingObjectMap, FieldMapping, StepMapping } from '../../views/landing/wizard/shared/field-mapping/FieldMapping'; -import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; +import { + BackingObjectMap, + FieldMapping, + StepMapping +} from '../../views/landing/wizard/shared/field-mapping/FieldMapping'; +import { AbstractControl, FormGroup } from '@angular/forms'; import AppServices from './appServices'; import { UserDataIdentifier, UserDataService } from './user-data.service'; import { FormUtils } from '../../views/landing/wizard/shared/utils/form-utils'; export class UserDataFormService { storeFromMapping(wizard, step: string, stepMapping: StepMapping, formGroup: FormGroup) { - AppServices.fieldMapUtilities.getActiveFieldMappings(stepMapping).forEach( fieldMapping => { + AppServices.fieldMapUtilities.getActiveFieldMappings(stepMapping).forEach(fieldMapping => { if (AppServices.fieldMapUtilities.shouldAutoSave(fieldMapping)) { this.storeFromFieldMapping(wizard, step, fieldMapping, formGroup); } @@ -15,7 +19,7 @@ export class UserDataFormService { } private storeFromFieldMapping(wizard, step: string, fieldMapping: FieldMapping, formGroup: FormGroup) { - const identifier: UserDataIdentifier = { wizard, step, field: fieldMapping.name }; + const identifier: UserDataIdentifier = {wizard, step, field: fieldMapping.name}; if (fieldMapping.hasNoDomControl) { this.storeFieldWithNoDomControl(wizard, step, fieldMapping); } else if (fieldMapping.isBoolean) { @@ -26,8 +30,8 @@ export class UserDataFormService { this.storeMapField(identifier, formGroup); } else if (fieldMapping.backingObject) { this.storeBackingObjectField(identifier, formGroup, fieldMapping.backingObject) - } else { - this.storeInputField(identifier, formGroup); + } else { + this.storeInputField(identifier, formGroup, fieldMapping.displayFunction); } } @@ -37,7 +41,7 @@ export class UserDataFormService { return; } const value = fieldMapping.retriever(null); - const identifier = { wizard, step, field: fieldMapping.name }; + const identifier = {wizard, step, field: fieldMapping.name}; if (fieldMapping.isBoolean) { AppServices.userDataService.storeBoolean(identifier, value); } else if (fieldMapping.isMap) { @@ -48,12 +52,13 @@ export class UserDataFormService { } // convenience methods - storeInputField(identifier: UserDataIdentifier, formGroup: FormGroup): boolean { + storeInputField(identifier: UserDataIdentifier, formGroup: FormGroup, displayFunction?: (f) => string): boolean { const control = this.getFormControl(identifier, formGroup); if (!control) { return false; } - AppServices.userDataService.store(identifier, { display: control.value, value: control.value }); + const displayValue = displayFunction ? displayFunction(control.value) : control.value + AppServices.userDataService.store(identifier, {display: displayValue, value: control.value}); return true; } @@ -83,7 +88,7 @@ export class UserDataFormService { restoreForm(wizard, step: string, formGroup: FormGroup, stepMapping: StepMapping) { AppServices.fieldMapUtilities.getFieldMappingsToRestore(stepMapping).forEach(fieldMapping => { - const identifier = { wizard, step, field: fieldMapping.name }; + const identifier = {wizard, step, field: fieldMapping.name}; this.restoreField(identifier, fieldMapping, formGroup); // Re-store the masked field value, so that if there WAS a value for this masked field in local storage, @@ -95,7 +100,7 @@ export class UserDataFormService { // Note: we set the values on the primary trigger fields AFTER all the "regular" fields are restored because the // handler for the trigger field change may make use the values of the other fields AppServices.fieldMapUtilities.getPrimaryTriggerMappingsToRestore(stepMapping).forEach(fieldMapping => { - const identifier = { wizard, step, field: fieldMapping.name }; + const identifier = {wizard, step, field: fieldMapping.name}; this.restoreField(identifier, fieldMapping, formGroup); }) } @@ -103,17 +108,7 @@ export class UserDataFormService { private buildFormField(formGroup: FormGroup, wizard, step: string, fieldMapping: FieldMapping) { AppServices.fieldMapUtilities.validateFieldMapping(step, fieldMapping); const initialValue = AppServices.fieldMapUtilities.getInitialValue(wizard, step, fieldMapping); - const validators = AppServices.fieldMapUtilities.getValidatorArray(fieldMapping); - FormUtils.addControl( - formGroup, - fieldMapping.name, - new FormControl(initialValue, validators) - ); - // TODO: figure out why we cannot seem to set the initialValue using the above code: new FormControl(initialValue, validators), - // but putting it into a setTimeout closure seems to "fix" the problem - setTimeout(() => { - formGroup.controls[fieldMapping.name].setValue(initialValue); - }); + FormUtils.addDynamicControl(formGroup, initialValue, fieldMapping); } private shouldBuildField(fieldMapping: FieldMapping) { @@ -165,7 +160,7 @@ export class UserDataFormService { if (!control) { return false; } - AppServices.userDataService.store(identifier, { display: control.value ? UserDataService.MASK : '', value: '' }); + AppServices.userDataService.store(identifier, {display: control.value ? UserDataService.MASK : '', value: ''}); return true; } diff --git a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.html b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.html index a0f35726d5..77ef957a5f 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.html +++ b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.html @@ -2,15 +2,15 @@

@@ -42,7 +42,7 @@

Deploy {{ clusterTypeDescriptorTitleCase }} Cluster diff --git a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.spec.ts b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.spec.ts index b3ad2e6bf7..2fd53c1fa2 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.spec.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.spec.ts @@ -80,8 +80,8 @@ describe('AwsWizardComponent', () => { }), metadataForm: fb.group({ clusterDescription: [''], - clusterLabels: [new Map()], - clusterLocation: [''], + clusterLabels: [{key: 'a', value: '1'}], + clusterLocation: [''] }), networkForm: fb.group({ clusterPodCidr: [''], @@ -150,10 +150,7 @@ describe('AwsWizardComponent', () => { expect(formGroup).toBeTruthy(); formGroup.addControl(fieldName, new FormControl(desiredValue)); }); - // NOTE: because cluster labels are pulled from storage (not a DOM control) we have to put the test values in storage - const clusterLabels = new Map([['key1', 'value1']]); - const identifierClusterLabels = { wizard: component.wizardName, step: WizardForm.METADATA, field: 'clusterLabels'}; - AppServices.userDataService.storeMap(identifierClusterLabels, clusterLabels); + // NOTE: because cluster plan is pulled from storage (not a DOM control) we have to put the test values in storage const identifierClusterPlan = { wizard: component.wizardName, step: AwsForm.NODESETTING, field: NodeSettingField.CLUSTER_PLAN }; AppServices.userDataService.store(identifierClusterPlan, { display: ClusterPlan.DEV, value: ClusterPlan.DEV }); @@ -175,9 +172,7 @@ describe('AwsWizardComponent', () => { clusterPodCIDR: '100.96.0.0/11', cniType: 'antrea' }); - expect(payload.labels).toEqual({ - key1: 'value1' - }); + expect(payload.labels).toEqual({}); expect(payload.annotations).toEqual({ description: 'DescriptionEXAMPLE', location: 'mylocation1' diff --git a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.ts b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.ts index 47d1a2e916..cddc72f44c 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/aws-wizard/aws-wizard.component.ts @@ -334,18 +334,18 @@ export class AwsWizardComponent extends WizardBaseDirective implements OnInit { return {name: AwsForm.PROVIDER, title: 'IaaS Provider', description: 'Validate the AWS provider account for ' + this.title, i18n: {title: 'IaaS provder step name', description: 'IaaS provder step description'}, - clazz: AwsProviderStepComponent}; + clazz: AwsProviderStepComponent}; } get AwsNodeSettingForm(): FormDataForHTML { return { name: AwsForm.NODESETTING, title: FormUtility.titleCase(this.clusterTypeDescriptor) + ' Cluster Settings', description: `Specify the resources backing the ${this.clusterTypeDescriptor} cluster`, i18n: {title: 'IaaS provder step name', description: 'IaaS provder step description'}, - clazz: NodeSettingStepComponent}; + clazz: NodeSettingStepComponent}; } get AwsVpcForm(): FormDataForHTML { return {name: AwsForm.VPC, title: 'VPC for AWS', description: 'Specify VPC settings for AWS', - i18n: {title: 'vpc step name', description: 'vpc step description'}, - clazz: VpcStepComponent}; + i18n: {title: 'vpc step name', description: 'vpc step description'}, + clazz: VpcStepComponent}; } get AwsOsImageForm(): FormDataForHTML { return this.getOsImageForm(AwsOsImageStepComponent); @@ -358,12 +358,12 @@ export class AwsWizardComponent extends WizardBaseDirective implements OnInit { private subscribeToServices() { AppServices.messenger.subscribe(TanzuEventType.AWS_REGION_CHANGED, event => { - const region = event.payload; - AppServices.dataServiceRegistrar.trigger([TanzuEventType.AWS_GET_OS_IMAGES], { region }); - // NOTE: even though the VPC and AZ endpoints don't take the region as a payload, they DO return different data - // if the user logs in to AWS using a different region. Therefore, we re-fetch that data if the region changes. - AppServices.dataServiceRegistrar.trigger([TanzuEventType.AWS_GET_EXISTING_VPCS, TanzuEventType.AWS_GET_AVAILABILITY_ZONES]); - }); + const region = event.payload; + AppServices.dataServiceRegistrar.trigger([TanzuEventType.AWS_GET_OS_IMAGES], { region }); + // NOTE: even though the VPC and AZ endpoints don't take the region as a payload, they DO return different data + // if the user logs in to AWS using a different region. Therefore, we re-fetch that data if the region changes. + AppServices.dataServiceRegistrar.trigger([TanzuEventType.AWS_GET_EXISTING_VPCS, TanzuEventType.AWS_GET_AVAILABILITY_ZONES]); + }); } private registerServices() { diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.html b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.html index 7d3a3a1fcc..825e3ab625 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.html +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.html @@ -33,7 +33,7 @@

- + @@ -50,7 +50,7 @@

- + @@ -88,7 +88,8 @@

- + Controller certificate authority is required @@ -98,7 +99,8 @@

- @@ -119,7 +121,7 @@

- + @@ -130,7 +132,8 @@

-
- - - - - By default, all clusters will have NSX Advanced Load Balancer enabled. Here you may optionally - specify cluster labels to identify a subset of clusters that should have NSX Advanced Load Balancer - enabled. - -
-
-
-
-
- -
- - - - - - : - - - - - - - -
-
-
- - - - - - Workload Cluster Label Keys must start and end with an alphanumeric character, and can contain only - letters, numbers, hyphens, underscores, and dots. - - - Workload Cluster Label Keys must not include whitespace on ends. - - - Workload Cluster Label Keys max length is 63 characters. - - - : - - - - - - Workload Cluster Label Values must start and end with an alphanumeric character, and can contain only - letters, numbers, hyphens, underscores, and dots. - - - Workload Cluster Label Values must not include whitespace on ends. - - - Workload Cluster Label Keys max length is 63 characters. - - - -
+
diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.spec.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.spec.ts index d507c91209..97d84498c3 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.spec.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.spec.ts @@ -1,13 +1,11 @@ // Angular imports import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { ReactiveFormsModule } from '@angular/forms'; -import { FormBuilder } from '@angular/forms'; +import { FormArray, FormBuilder, ReactiveFormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; // App imports import { APIClient } from '../../../../../../../swagger/api-client.service'; import AppServices from 'src/app/shared/service/appServices'; -import { FieldMapUtilities } from '../../../field-mapping/FieldMapUtilities'; import { Messenger } from 'src/app/shared/service/Messenger'; import { SharedLoadBalancerStepComponent } from './load-balancer-step.component'; import { SharedModule } from '../../../../../../../shared/shared.module'; @@ -43,12 +41,20 @@ describe('SharedLoadBalancerStepComponent', () => { const fb = new FormBuilder(); fixture = TestBed.createComponent(SharedLoadBalancerStepComponent); component = fixture.componentInstance; - component.formGroup = fb.group({ - }); + component.formGroup = fb.group({}); fixture.detectChanges(); }); + it('should initialize tkgLabelsConfig', () => { + component.ngOnInit(); + const config = component.tkgLabelsConfig; + + expect(config.label.title).toEqual('CLUSTER LABELS (OPTIONAL)'); + expect(config.forms.parent.get('clusterLabels')).toBeInstanceOf(FormArray); + expect(config.fields.clusterTypeDescriptor).toEqual('Workload'); + }) + it('should call get clouds when controller credentials have been validated', () => { const apiSpy = spyOn(component['apiClient'], 'getAviClouds').and.callThrough(); component.getClouds(); @@ -60,18 +66,4 @@ describe('SharedLoadBalancerStepComponent', () => { component.getServiceEngineGroups(); expect(apiSpy).toHaveBeenCalled(); }); - - it('should add new label', () => { - component.addLabel("somekey", "someval"); - component.addLabel("somekey2", "someval2"); - expect(component.labels.get("somekey")).toEqual("someval"); - expect(component.labels.get("somekey2")).toEqual("someval2"); - }); - - it('should delete existing label', () => { - component.addLabel("akey", "avalue"); - expect(component.labels.get("akey")).toEqual('avalue'); - component.deleteLabel("akey"); - expect(component.labels.get("akey")).toBeFalsy(); - }); }); diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.ts index 5f645112bb..8a98c1d1aa 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.component.ts @@ -1,20 +1,21 @@ // Angular imports import { Component, OnInit } from '@angular/core'; -import { Validators } from '@angular/forms'; +import { FormArray, Validators } from '@angular/forms'; +import { ClrLoadingState } from "@clr/angular"; // Third party imports import { debounceTime, distinctUntilChanged, finalize, takeUntil } from 'rxjs/operators'; -// App imports -import { APIClient } from "../../../../../../../swagger"; -import AppServices from '../../../../../../../shared/service/appServices'; +import { IpFamilyEnum } from 'src/app/shared/constants/app.constants'; import { AviCloud } from "src/app/swagger/models/avi-cloud.model"; import { AviServiceEngineGroup } from "src/app/swagger/models/avi-service-engine-group.model"; -import { AviVipNetwork } from './../../../../../../../swagger/models/avi-vip-network.model'; -import { ClrLoadingState } from "@clr/angular"; -import { IpFamilyEnum } from 'src/app/shared/constants/app.constants'; -import { LoadBalancerField, LoadBalancerStepMapping } from './load-balancer-step.fieldmapping'; -import { StepFormDirective } from "../../../step-form/step-form"; +import AppServices from '../../../../../../../shared/service/appServices'; +// App imports +import { APIClient } from "../../../../../../../swagger"; import { StepMapping } from '../../../field-mapping/FieldMapping'; +import { StepFormDirective } from "../../../step-form/step-form"; import { ValidationService } from "../../../validation/validation.service"; +import { TKGLabelsConfig } from '../../widgets/tkg-labels/interfaces/tkg-labels.interface'; +import { AviVipNetwork } from './../../../../../../../swagger/models/avi-vip-network.model'; +import { LoadBalancerField, LoadBalancerStepMapping } from './load-balancer-step.fieldmapping'; const SupervisedFields = [ LoadBalancerField.CONTROLLER_HOST, @@ -37,11 +38,11 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement selectedCloudName: string; serviceEngineGroups: Array; serviceEngineGroupsFiltered: Array; - labels: Map = new Map(); vipNetworks: Array = []; selectedNetworkName: string; selectedManagementClusterNetworkName: string; loadBalancerLabel = 'Load Balancer Settings'; + tkgLabelsConfig: TKGLabelsConfig; private stepMapping: StepMapping; @@ -50,67 +51,28 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement super(); } - protected customizeForm() { - SupervisedFields.forEach(field => { - this.formGroup.get(field).valueChanges - .pipe( - debounceTime(500), - distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)), - takeUntil(this.unsubscribe) - ) - .subscribe(() => { - if (this.connected) { - this.connected = false; - this.disarmField(LoadBalancerField.CLOUD_NAME, true); - this.clouds = []; - this.disarmField(LoadBalancerField.SERVICE_ENGINE_GROUP_NAME, true); - this.serviceEngineGroups = []; - this.disarmField(LoadBalancerField.NETWORK_CIDR, true); - this.disarmField(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, true); - - // If connection cleared, toggle validators OFF - this.toggleValidators(false); - } - }); - }); - - this.formGroup.get(LoadBalancerField.CLOUD_NAME).valueChanges.pipe( - distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)), - takeUntil(this.unsubscribe) - ).subscribe((cloud) => { - this.selectedCloudName = cloud; - this.onSelectCloud(this.selectedCloudName); - }); - - this.registerOnValueChange("networkName", this.onSelectVipNetwork.bind(this)); - this.registerOnValueChange("networkCIDR", this.onSelectVipCIDR.bind(this)); - this.registerOnValueChange(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME, this.onSelectManagementNetwork.bind(this)); - this.registerOnIpFamilyChange(LoadBalancerField.NETWORK_CIDR, [], []); - this.registerOnIpFamilyChange(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, [ - this.validationService.isValidIpNetworkSegment()], [ - this.validationService.isValidIpv6NetworkSegment() - ]); + /** + * This is to make sense that the list returned is always up to date. + */ + get vipNetworksPerCloud() { + if (this.vipNetworks && this.vipNetworks.length > 0 && this.selectedCloud) { + return this.vipNetworks.filter(net => net.cloud === this.selectedCloud.uuid); + } + return []; } - private supplyStepMapping(): StepMapping { - if (!this.stepMapping) { - this.stepMapping = this.createStepMapping(); - } - return this.stepMapping; + /** + * This is to make sense that the list returned is always up to date. + */ + get subnetsPerNetwork() { + return this.getSubnets(this.selectedNetworkName); } - private createStepMapping(): StepMapping { - const result = LoadBalancerStepMapping; - const managementClusterNetworkNameMapping = AppServices.fieldMapUtilities.getFieldMapping('managementClusterNetworkName', result); - const managementClusterNetworkCidrMapping = AppServices.fieldMapUtilities.getFieldMapping('managementClusterNetworkCIDR', result); - if (this.modeClusterStandalone) { - managementClusterNetworkNameMapping.label = 'STANDALONE CLUSTER VIP NETWORK NAME'; - managementClusterNetworkCidrMapping.label = 'STANDALONE CLUSTER VIP NETWORK CIDR'; - } - const clusterFieldMapping = AppServices.fieldMapUtilities.getFieldMapping(LoadBalancerField.CLUSTER_LABELS, result); - clusterFieldMapping.retriever = this.getClusterLabels.bind(this); - clusterFieldMapping.restorer = this.setClusterLabels.bind(this); - return result; + /** + * This is to make sense that the list returned is always up to date. + */ + get subnetsPerManagementNetwork() { + return this.getSubnets(this.selectedManagementClusterNetworkName); } ngOnInit() { @@ -122,14 +84,27 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement this.registerDefaultFileImportErrorHandler(this.eventFileImportError); this.customizeForm(); - } - - private setClusterLabels(data: Map) { - return this.labels = data; - } - private getClusterLabels(): Map { - return this.labels; + this.tkgLabelsConfig = { + label: { + title: this.htmlFieldLabels['clusterLabels'], + tooltipText: `By default, all clusters will have NSX Advanced Load Balancer enabled. Here you may + optionally specify cluster labels to identify a subset of clusters that should have + NSX Advanced Load Balancer enabled. Note: Ensure that these labels are present on + individual clusters that should be enabled with NSX Advanced Load Balancer.`, + helperText: `By default, all clusters will have NSX Advanced Load Balancer enabled. Here you may optionally + specify cluster labels to identify a subset of clusters that should have NSX Advanced Load Balancer + enabled.` + }, + forms: { + parent: this.formGroup, + control: this.formGroup.get('clusterLabels') as FormArray + }, + fields: { + clusterTypeDescriptor: 'Workload', + fieldMapping: LoadBalancerStepMapping.fieldMappings.find((m) => m.name === LoadBalancerField.CLUSTER_LABELS) + } + }; } /** @@ -249,7 +224,9 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement this.serviceEngineGroupsFiltered = []; if (cloudName && this.clouds) { - this.selectedCloud = this.clouds.find((cloud: AviCloud) => { return cloud.name === cloudName; }); + this.selectedCloud = this.clouds.find((cloud: AviCloud) => { + return cloud.name === cloudName; + }); if (this.selectedCloud) { this.serviceEngineGroupsFiltered = this.serviceEngineGroups.filter((group: AviServiceEngineGroup) => { return group.location.includes(this.selectedCloud.uuid); @@ -263,13 +240,14 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement */ onSelectVipNetwork(networkName: string): void { this.selectedNetworkName = networkName; - if (!this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME).value) { } + if (!this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME).value) { + } this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME).setValue(networkName) } onSelectVipCIDR(cidr: string): void { if (!this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR).value) { - this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR).setValue(cidr); + this.formGroup.get(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR).setValue(cidr); } } @@ -315,51 +293,10 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement getDisabled(): boolean { return ( SupervisedFields.some(f => !this.formGroup.get(f).value) || - SupervisedFields.some(f => !this.formGroup.get(f).valid) + SupervisedFields.some(f => !this.formGroup.get(f).valid) ) } - /** - * Add workload cluster label' - */ - addLabel(key: string, value: string) { - if (key === '' || value === '') { - this.errorNotification = `Key and value for Labels are required.`; - } else if (!this.labels.has(key)) { - this.labels.set(key, value); - this.formGroup.controls[LoadBalancerField.NEW_LABEL_KEY].setValue(''); - this.formGroup.controls[LoadBalancerField.NEW_LABEL_VALUE].setValue(''); - } else { - this.errorNotification = `A Label with the same key already exists.`; - } - } - - /** - * Delete workload cluster label' - */ - deleteLabel(key: string) { - this.labels.delete(key); - } - - /** - * @method getLabelDisabled - * helper method to get if label add btn should be disabled - */ - getLabelDisabled(): boolean { - return !(this.formGroup.get(LoadBalancerField.NEW_LABEL_KEY).valid && - this.formGroup.get(LoadBalancerField.NEW_LABEL_VALUE).valid); - } - - /** - * This is to make sense that the list returned is always up to date. - */ - get vipNetworksPerCloud() { - if (this.vipNetworks && this.vipNetworks.length > 0 && this.selectedCloud) { - return this.vipNetworks.filter(net => net.cloud === this.selectedCloud.uuid); - } - return []; - } - getSubnets(networkName: string): any[] { if (!this.isEmptyArray(this.vipNetworksPerCloud) && networkName) { const temp = this.vipNetworksPerCloud @@ -372,22 +309,69 @@ export class SharedLoadBalancerStepComponent extends StepFormDirective implement } return []; } - /** - * This is to make sense that the list returned is always up to date. - */ - get subnetsPerNetwork() { - return this.getSubnets(this.selectedNetworkName); - } - /** - * This is to make sense that the list returned is always up to date. - */ - get subnetsPerManagementNetwork() { - return this.getSubnets(this.selectedManagementClusterNetworkName); + protected customizeForm() { + SupervisedFields.forEach(field => { + this.formGroup.get(field).valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)), + takeUntil(this.unsubscribe) + ) + .subscribe(() => { + if (this.connected) { + this.connected = false; + this.disarmField(LoadBalancerField.CLOUD_NAME, true); + this.clouds = []; + this.disarmField(LoadBalancerField.SERVICE_ENGINE_GROUP_NAME, true); + this.serviceEngineGroups = []; + this.disarmField(LoadBalancerField.NETWORK_CIDR, true); + this.disarmField(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, true); + + // If connection cleared, toggle validators OFF + this.toggleValidators(false); + } + }); + }); + + this.formGroup.get(LoadBalancerField.CLOUD_NAME).valueChanges.pipe( + distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)), + takeUntil(this.unsubscribe) + ).subscribe((cloud) => { + this.selectedCloudName = cloud; + this.onSelectCloud(this.selectedCloudName); + }); + + this.registerOnValueChange("networkName", this.onSelectVipNetwork.bind(this)); + this.registerOnValueChange("networkCIDR", this.onSelectVipCIDR.bind(this)); + this.registerOnValueChange(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME, this.onSelectManagementNetwork.bind(this)); + this.registerOnIpFamilyChange(LoadBalancerField.NETWORK_CIDR, [], []); + this.registerOnIpFamilyChange(LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, [ + this.validationService.isValidIpNetworkSegment()], [ + this.validationService.isValidIpv6NetworkSegment() + ]); } protected storeUserData() { this.storeUserDataFromMapping(this.supplyStepMapping()); this.storeDefaultDisplayOrder(this.supplyStepMapping()); } + + private supplyStepMapping(): StepMapping { + if (!this.stepMapping) { + this.stepMapping = this.createStepMapping(); + } + return this.stepMapping; + } + + private createStepMapping(): StepMapping { + const result = LoadBalancerStepMapping; + const managementClusterNetworkNameMapping = AppServices.fieldMapUtilities.getFieldMapping('managementClusterNetworkName', result); + const managementClusterNetworkCidrMapping = AppServices.fieldMapUtilities.getFieldMapping('managementClusterNetworkCIDR', result); + if (this.modeClusterStandalone) { + managementClusterNetworkNameMapping.label = 'STANDALONE CLUSTER VIP NETWORK NAME'; + managementClusterNetworkCidrMapping.label = 'STANDALONE CLUSTER VIP NETWORK CIDR'; + } + return result; + } } diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.fieldmapping.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.fieldmapping.ts index c2c8d926e1..61f2121900 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.fieldmapping.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/load-balancer/load-balancer-step.fieldmapping.ts @@ -1,4 +1,4 @@ -import { StepMapping } from '../../../field-mapping/FieldMapping'; +import { ControlType, StepMapping } from '../../../field-mapping/FieldMapping'; import { SimpleValidator } from '../../../constants/validation.constants'; export enum LoadBalancerField { @@ -19,19 +19,47 @@ export enum LoadBalancerField { export const LoadBalancerStepMapping: StepMapping = { fieldMappings: [ - { name: LoadBalancerField.CONTROLLER_HOST, validators: [SimpleValidator.IS_VALID_FQDN_OR_IP], label: 'CONTROLLER HOST' }, - { name: LoadBalancerField.USERNAME, label: 'USERNAME' }, - { name: LoadBalancerField.PASSWORD, mask: true, label: 'PASSWORD' }, - { name: LoadBalancerField.CLOUD_NAME, label: 'CLOUD NAME' }, - { name: LoadBalancerField.SERVICE_ENGINE_GROUP_NAME, label: 'SERVICE ENGINE GROUP NAME' }, - { name: LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME, label: 'MANAGEMENT VIP NETWORK NAME' }, - { name: LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, validators: [SimpleValidator.IS_VALID_IP_NETWORK_SEGMENT], - label: 'MANAGEMENT VIP NETWORK CIDR' }, - { name: LoadBalancerField.NETWORK_NAME, label: 'WORKLOAD VIP NETWORK NAME' }, - { name: LoadBalancerField.NETWORK_CIDR, label: 'WORKLOAD VIP NETWORK CIDR' }, - { name: LoadBalancerField.CONTROLLER_CERT, doNotAutoSave: true, label: 'CONTROLLER CERTIFICATE AUTHORITY' }, - { name: LoadBalancerField.CLUSTER_LABELS, hasNoDomControl: true, isMap: true, label: 'CLUSTER LABELS (OPTIONAL)' }, - { name: LoadBalancerField.NEW_LABEL_KEY, validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], neverStore: true }, - { name: LoadBalancerField.NEW_LABEL_VALUE, validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], neverStore: true }, + { + name: LoadBalancerField.CONTROLLER_HOST, + validators: [SimpleValidator.IS_VALID_FQDN_OR_IP], + label: 'CONTROLLER HOST' + }, + {name: LoadBalancerField.USERNAME, label: 'USERNAME'}, + {name: LoadBalancerField.PASSWORD, mask: true, label: 'PASSWORD'}, + {name: LoadBalancerField.CLOUD_NAME, label: 'CLOUD NAME'}, + {name: LoadBalancerField.SERVICE_ENGINE_GROUP_NAME, label: 'SERVICE ENGINE GROUP NAME'}, + {name: LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_NAME, label: 'MANAGEMENT VIP NETWORK NAME'}, + { + name: LoadBalancerField.MANAGEMENT_CLUSTER_NETWORK_CIDR, + validators: [SimpleValidator.IS_VALID_IP_NETWORK_SEGMENT], + label: 'MANAGEMENT VIP NETWORK CIDR' + }, + {name: LoadBalancerField.NETWORK_NAME, label: 'WORKLOAD VIP NETWORK NAME'}, + {name: LoadBalancerField.NETWORK_CIDR, label: 'WORKLOAD VIP NETWORK CIDR'}, + {name: LoadBalancerField.CONTROLLER_CERT, doNotAutoSave: true, label: 'CONTROLLER CERTIFICATE AUTHORITY'}, + { + name: LoadBalancerField.CLUSTER_LABELS, + label: 'CLUSTER LABELS (OPTIONAL)', + controlType: ControlType.FormArray, + displayFunction: labels => labels.map(label => `${label.key} : ${label.value}`).join(', '), + children: [ + { + name: 'key', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [ + SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, + SimpleValidator.RX_UNIQUE, + SimpleValidator.RX_REQUIRED_IF_VALUE + ] + }, + { + name: 'value', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, SimpleValidator.RX_REQUIRED_IF_KEY] + } + ] + } ] -} +}; diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.html b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.html index 34c0c954fa..8f66db7049 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.html +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.html @@ -33,17 +33,21 @@

- + - - {{ clusterTypeDescriptorTitleCase }} Cluster Location must start and end with an alphanumeric character, and can contain only + + {{ clusterTypeDescriptorTitleCase }} Cluster Location must start and end with an alphanumeric + character, and can contain only letters, numbers, hyphens, underscores, and dots. - + {{ clusterTypeDescriptorTitleCase }} Cluster Location must not include whitespace on ends. - + {{ clusterTypeDescriptorTitleCase }} Cluster Location max length is 63 characters. @@ -63,12 +67,16 @@

-
+
-
- {{ clusterTypeDescriptorTitleCase }} Cluster Description must start and end with a letter, and can contain only + placeholder="optional" aria-label="description" + aria-describedby="cluster-description-error"> +
+ {{ clusterTypeDescriptorTitleCase }} Cluster Description must start and end with a letter, + and can contain only lowercase letters, numbers, and hyphens. It must not include whitespace on ends. It has a max length of 63 characters.
@@ -79,76 +87,7 @@

- - - -
-
-
-
-
- -
- - - - - - {{ clusterTypeDescriptorTitleCase }} Cluster label keys must start and end with an alphanumeric character, and can contain only - letters, numbers, hyphens, underscores, and dots. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label keys must not include whitespace on ends. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label keys max length is 63 characters. - - - A Label with the same key already exists. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label value is required if its value is not empty. - - - : - - - - - - {{ clusterTypeDescriptorTitleCase }} Cluster label values must start and end with an alphanumeric character, and can contain only - letters, numbers, hyphens, underscores, and dots. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label values must not include whitespace on ends. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label values max length is 63 characters. - - - {{ clusterTypeDescriptorTitleCase }} Cluster label value is required if its key is not empty. - - - - -
-
-
- -
+
diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.spec.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.spec.ts index 4a045cdf87..6543c08315 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.spec.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.spec.ts @@ -1,12 +1,10 @@ // Angular imports import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { ReactiveFormsModule } from '@angular/forms'; +import { FormArray, FormBuilder, ReactiveFormsModule } from '@angular/forms'; // App imports import { APIClient } from '../../../../../../../swagger/api-client.service'; import AppServices from 'src/app/shared/service/appServices'; -import { FieldMapUtilities } from '../../../field-mapping/FieldMapUtilities'; import { Messenger, TanzuEventType } from 'src/app/shared/service/Messenger'; import { MetadataStepComponent } from './metadata-step.component'; import { SharedModule } from '../../../../../../../shared/shared.module'; @@ -42,30 +40,26 @@ describe('MetadataStepComponent', () => { fixture = TestBed.createComponent(MetadataStepComponent); component = fixture.componentInstance; // NOTE: using Azure file import events just for testing - component.setStepRegistrantData({ wizard: 'BozoWizard', step: WizardForm.METADATA, formGroup: new FormBuilder().group({}), + component.setStepRegistrantData({ + wizard: 'BozoWizard', step: WizardForm.METADATA, formGroup: new FormBuilder().group({}), eventFileImported: TanzuEventType.AZURE_CONFIG_FILE_IMPORTED, - eventFileImportError: TanzuEventType.AZURE_CONFIG_FILE_IMPORT_ERROR}); + eventFileImportError: TanzuEventType.AZURE_CONFIG_FILE_IMPORT_ERROR + }); + component.ngOnInit(); fixture.detectChanges(); }); - it('should add new label', () => { - component.addLabel("somekey", "someval"); - component.addLabel("somekey2", "someval2"); - const labels = component.getClusterLabels(); - expect(labels.get("somekey")).toEqual("someval"); - expect(labels.get("somekey2")).toEqual("someval2"); - }); + it('should initialize tkgLabelsConfig', () => { + component.setClusterTypeDescriptor('Management'); + component.ngOnInit(); + const config = component.tkgLabelsConfig; - it('should delete existing label', () => { - component.addLabel("akey", "avalue"); - let labels = component.getClusterLabels(); - expect(labels.get("akey")).toEqual("avalue"); - component.deleteLabel("newLabelKey2"); - labels = component.getClusterLabels(); - expect(labels.get("newLabelKey2")).toBeFalsy(); - }); + expect(config.label.title).toEqual('LABELS (OPTIONAL)'); + expect(config.forms.parent.get('clusterLabels')).toBeInstanceOf(FormArray); + expect(config.fields.clusterTypeDescriptor).toEqual('Management'); + }) it('should announce description change', () => { const msgSpy = spyOn(AppServices.messenger, 'publish').and.callThrough(); diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.ts index b625ed04df..fcc840c676 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.component.ts @@ -1,17 +1,12 @@ // Angular imports import { Component, OnInit } from '@angular/core'; -import { FormControl, Validators } from '@angular/forms'; - +import { FormArray } from '@angular/forms'; // App imports import AppServices from '../../../../../../../shared/service/appServices'; -import { FormUtils } from '../../../utils/form-utils'; -import { MetadataField, MetadataStepMapping } from './metadata-step.fieldmapping'; -import { StepFormDirective } from '../../../step-form/step-form'; import { StepMapping } from '../../../field-mapping/FieldMapping'; -import { ValidationService } from '../../../validation/validation.service'; - -const LABEL_KEY_NAME = 'newLabelKey'; -const LABEL_VALUE_NAME = 'newLabelValue'; +import { StepFormDirective } from '../../../step-form/step-form'; +import { TKGLabelsConfig } from '../../widgets/tkg-labels/interfaces/tkg-labels.interface'; +import { MetadataField, MetadataStepMapping } from './metadata-step.fieldmapping'; @Component({ selector: 'app-metadata-step', @@ -19,172 +14,50 @@ const LABEL_VALUE_NAME = 'newLabelValue'; styleUrls: ['./metadata-step.component.scss'] }) export class MetadataStepComponent extends StepFormDirective implements OnInit { - labels: Map = new Map(); - keySet: Set = new Set(); - savedKeySet: Set = new Set(); - labelCounter: number = 0; - private stepMapping: StepMapping; + tkgLabelsConfig: TKGLabelsConfig; - constructor(private validationService: ValidationService) { + constructor() { super(); } ngOnInit() { super.ngOnInit(); - AppServices.userDataFormService.buildForm(this.formGroup, this.wizardName, this.formName, this.supplyStepMapping()); - this.htmlFieldLabels = AppServices.fieldMapUtilities.getFieldLabelMap(this.supplyStepMapping()); - this.storeDefaultLabels(this.supplyStepMapping()); + AppServices.userDataFormService.buildForm(this.formGroup, this.wizardName, this.formName, MetadataStepMapping); + this.htmlFieldLabels = AppServices.fieldMapUtilities.getFieldLabelMap(MetadataStepMapping); + this.storeDefaultLabels(MetadataStepMapping); this.registerStepDescriptionTriggers({ fields: [MetadataField.CLUSTER_LOCATION], - clusterTypeDescriptor: true, - }) - this.registerDefaultFileImportedHandler(this.eventFileImported, this.supplyStepMapping()); - this.registerDefaultFileImportErrorHandler(this.eventFileImportError); - - // initialize label controls - if (this.labels.size === 0) { - this.addLabel(); - } - } - - private supplyStepMapping(): StepMapping { - if (!this.stepMapping) { - this.stepMapping = this.createStepMapping(); - } - return this.stepMapping; - } - - private createStepMapping() { - const result = MetadataStepMapping; - const clusterFieldMapping = AppServices.fieldMapUtilities.getFieldMapping(MetadataField.CLUSTER_LABELS, result); - clusterFieldMapping.retriever = this.getClusterLabels.bind(this); - clusterFieldMapping.restorer = this.setClusterLabels.bind(this); - return result; - } - - // TODO: the 'labels' field now holds a keyField => valueField mapping, so when receiving the data, we build new controls to hold data - private setClusterLabels(data: Map) { - this.clearLabels(); - // ADD new ones - for (const [key, value] of data) { - this.addLabel(key, value); - } - // ensure at least one field - if (this.labels.size === 0) { - this.addLabel(); - } - } - - private clearLabels() { - // REMOVE existing label fields - for (const [keyField, valueField] of this.labels) { - this.formGroup.removeControl(keyField); - this.formGroup.removeControl(valueField); - } - this.labels = new Map(); - this.keySet = new Set(); - this.labelCounter = 0; - } - - // TODO: the 'labels' field holds a keyField => valueField mapping, so when returning the data, we build a new map from field data - // TODO: public for testing only - getClusterLabels(): Map { - const result = new Map(); - for (const [keyField, valueField] of this.labels) { - const key = this.formGroup.get(keyField).value; - const val = this.formGroup.get(valueField).value; - if (key && val) { - result.set(key, val); - } - } - return result; - } - - addLabel(key?: string, value?: string) { - this.labelCounter++; - this.labels.set(LABEL_KEY_NAME + this.labelCounter, LABEL_VALUE_NAME + this.labelCounter); - this.keySet.add(LABEL_KEY_NAME + this.labelCounter); - FormUtils.addControl( - this.formGroup, - LABEL_KEY_NAME + this.labelCounter, - new FormControl(key || '', [ - this.validationService.isValidLabelOrAnnotation(), - this.validationService.isUniqueLabel( - this.formGroup, - this.keySet, - LABEL_KEY_NAME + this.labelCounter) - ]) - ); - - FormUtils.addControl( - this.formGroup, - LABEL_VALUE_NAME + this.labelCounter, - new FormControl(value || '', [ - this.validationService.isValidLabelOrAnnotation() - ]) - ); - // Label value depends on Label key. e.g.: if label key is not empty, then label value is required - this.onChangeWithDependentField(LABEL_KEY_NAME + this.labelCounter, LABEL_VALUE_NAME + this.labelCounter); - // Label key depends on Label value. e.g.: if label value is not empty, then label key is required - this.onChangeWithDependentField(LABEL_VALUE_NAME + this.labelCounter, LABEL_KEY_NAME + this.labelCounter); - this.validateAllLabels(); - } - - /** - * @method onChangeWithDependentField - * make the dependent field is required if the indepdent field is not empty. - * @param fieldName is a independent field which determines if the dependent field is required. - * @param dependentFieldName is dependent on the independent field. - */ - onChangeWithDependentField(fieldName: string, dependentFieldName: string) { - const control = this.formGroup.get(dependentFieldName); - this.registerOnValueChange(fieldName, (data) => { - if (data !== '') { - if (!control.hasValidator(Validators.required)) { - control.addValidators(Validators.required); - control.markAsPending(); // validation will not be triggered until the field is touched. - control.setErrors({required: true}); - } - } else { - control.removeValidators(Validators.required); - } - this.validateAllLabels(); // all the same label keys can show error message. + clusterTypeDescriptor: true }); - } + this.registerDefaultFileImportedHandler(this.eventFileImported, MetadataStepMapping); + this.registerDefaultFileImportErrorHandler(this.eventFileImportError); - validateAllLabels () { - // The setTimeout wrapper ensures that validation logic will run after a new label field is added. - setTimeout(_ => { - for (const [labelKey, labelVal] of this.labels) { - const key = this.formGroup.get(labelKey); - const val = this.formGroup.get(labelVal); - if (key) { - if (this.savedKeySet.has(labelKey)) { - key.markAsTouched(); - } - key.updateValueAndValidity(); - } - if (val) { - val.updateValueAndValidity(); - } + this.tkgLabelsConfig = { + label: { + title: this.htmlFieldLabels['clusterLabels'], + tooltipText: `Optionally specify labels for the ${this.clusterTypeDescriptor} cluster.` + }, + forms: { + parent: this.formGroup, + control: this.formGroup.get('clusterLabels') as FormArray + }, + fields: { + clusterTypeDescriptor: this.clusterTypeDescriptorTitleCase, + fieldMapping: MetadataStepMapping.fieldMappings.find((m) => m.name === MetadataField.CLUSTER_LABELS) } - }); - } - - deleteLabel(key: string) { - this.formGroup.removeControl(key); - this.formGroup.removeControl(this.labels.get(key)); - this.labels.delete(key); - this.keySet.delete(key); + }; } dynamicDescription(): string { const clusterLocation = this.getFieldValue(MetadataField.CLUSTER_LOCATION, true); - return clusterLocation ? 'Location: ' + clusterLocation : 'Specify metadata for the ' + this.clusterTypeDescriptor + ' cluster'; + return clusterLocation + ? `Location: ${clusterLocation}` + : `Specify metadata for the ${this.clusterTypeDescriptor} cluster`; } protected storeUserData() { - this.storeUserDataFromMapping(this.supplyStepMapping()); - this.storeDefaultDisplayOrder(this.supplyStepMapping()); + this.storeUserDataFromMapping(MetadataStepMapping); + this.storeDefaultDisplayOrder(MetadataStepMapping); } + } diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.fieldmapping.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.fieldmapping.ts index f20a9968c8..fd75adc5d7 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.fieldmapping.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/steps/metadata-step/metadata-step.fieldmapping.ts @@ -1,21 +1,47 @@ -import { StepMapping } from '../../../field-mapping/FieldMapping'; +import { ControlType, StepMapping } from '../../../field-mapping/FieldMapping'; import { SimpleValidator } from '../../../constants/validation.constants'; export enum MetadataField { CLUSTER_LABELS = 'clusterLabels', CLUSTER_DESCRIPTION = 'clusterDescription', - CLUSTER_LOCATION = 'clusterLocation' + CLUSTER_LOCATION = 'clusterLocation', } export const MetadataStepMapping: StepMapping = { fieldMappings: [ - { name: MetadataField.CLUSTER_LOCATION, validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], label: 'LOCATION (OPTIONAL)' }, - { name: MetadataField.CLUSTER_DESCRIPTION, validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], label: 'DESCRIPTION (OPTIONAL)' }, - { name: MetadataField.CLUSTER_LABELS, hasNoDomControl: true, isMap: true, label: 'LABELS (OPTIONAL)' }, + { + name: MetadataField.CLUSTER_LOCATION, + validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], + label: 'LOCATION (OPTIONAL)' + }, + { + name: MetadataField.CLUSTER_DESCRIPTION, + validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION], + label: 'DESCRIPTION (OPTIONAL)' + }, + { + name: MetadataField.CLUSTER_LABELS, + label: 'LABELS (OPTIONAL)', + controlType: ControlType.FormArray, + displayFunction: labels => labels.map(label => `${label.key} : ${label.value}`).join(', '), + children: [ + { + name: 'key', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [ + SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, + SimpleValidator.RX_UNIQUE, + SimpleValidator.RX_REQUIRED_IF_VALUE + ] + }, + { + name: 'value', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, SimpleValidator.RX_REQUIRED_IF_KEY] + } + ] + } ] -} -// About MetadataStep: -// The clusterLabels field does not actually exist in the DOM; the values are held in the step component. -// We use hasNoDomControl because the display value needs to be "manually" generated. -// Note that there are DOM fields that hold various pieces of the cluster labels, but we ignore them in favor of a single "field" -// that contains the entire map. +}; diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/interfaces/tkg-labels.interface.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/interfaces/tkg-labels.interface.ts new file mode 100644 index 0000000000..6d8de9f9c4 --- /dev/null +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/interfaces/tkg-labels.interface.ts @@ -0,0 +1,16 @@ +import { FormArray, FormGroup } from '@angular/forms'; + +export interface TKGLabelsConfig { + label: { + title: string; + tooltipText: string; + helperText?: string; + }; + forms: { + parent: FormGroup; // the parent form group + control: FormArray; // the control of the labels form array + }; + fields: { + [key: string]: any; + }; +} diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.html b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.html new file mode 100644 index 0000000000..d2e1d836e6 --- /dev/null +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.html @@ -0,0 +1,95 @@ +
+
+ + + + {{ config.label.helperText }} +
+ +
+
+ + + + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label keys must start and end with an + alphanumeric + character, and can contain only letters, numbers, hyphens, underscores, and dots. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label keys must not include whitespace on ends. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label keys max length is 63 characters. + + + A Label with the same key already exists. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label value is required if its value is not + empty. + + + + : + + + + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label values must start and end with an + alphanumeric + character, and can contain only letters, numbers, hyphens, underscores, and dots. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label values must not include whitespace on + ends. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label values max length is 63 characters. + + + {{ config?.fields?.clusterTypeDescriptor }} Cluster label value is required if its key is not empty. + + + + +
+
+ +
+ +
+
diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.scss b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.scss new file mode 100644 index 0000000000..26ee508c8e --- /dev/null +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.scss @@ -0,0 +1,20 @@ +.label-details { + align-items: baseline; + padding-left: 0.6rem; +} + +.btn-delete { + border: none; +} + +.btn-add { + margin-top: 1rem; +} + +.label-container { + margin-left: 0; +} + +.err-label { + max-width: 8.7rem; +} diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.spec.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.spec.ts new file mode 100644 index 0000000000..e0be42a58d --- /dev/null +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.spec.ts @@ -0,0 +1,106 @@ +import { TkgLabelsComponent } from './tkg-labels.component'; +import { FormArray, FormBuilder, FormControl, FormGroup } from "@angular/forms"; +import { ControlType } from "../../../field-mapping/FieldMapping"; +import { SimpleValidator } from "../../../constants/validation.constants"; +import { TKGLabelsConfig } from "./interfaces/tkg-labels.interface"; +import { FormUtils } from "../../../utils/form-utils"; + +describe('TkgLabelsComponent', () => { + let component: TkgLabelsComponent; + let parentFormGroup: FormGroup; + + const formBuilder = new FormBuilder(); + const tkgLabelsConfig: TKGLabelsConfig = { + label: { + title: 'LABELS (OPTIONAL)', + tooltipText: `Optionally specify labels for the Management cluster.` + }, + forms: { + parent: null, + control: null + }, + fields: { + clusterType: 'Management', + fieldMapping: { + name: 'clusterLabels', + label: 'LABELS (OPTIONAL)', + controlType: ControlType.FormArray, + children: [ + { + name: 'key', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [ + SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, + SimpleValidator.RX_UNIQUE, + SimpleValidator.RX_REQUIRED_IF_VALUE + ] + }, + { + name: 'value', + defaultValue: '', + controlType: ControlType.FormControl, + validators: [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, SimpleValidator.RX_REQUIRED_IF_KEY] + } + ] + } + } + }; + + beforeEach(() => { + parentFormGroup = formBuilder.group({ + clusterLabels: formBuilder.array([ + formBuilder.group({ + key: [''], + value: [''] + }) + ]) + }); + tkgLabelsConfig.forms.parent = parentFormGroup; + tkgLabelsConfig.forms.control = parentFormGroup.get('clusterLabels') as FormArray; + + component = new TkgLabelsComponent(); + component.config = tkgLabelsConfig; + }); + + it('should create label', () => { + const group = component.createLabel(); + expect(group.controls.key).toBeInstanceOf(FormControl); + expect(group.controls.value).toBeInstanceOf(FormControl); + }) + + it('should not add new Label on form invalid', () => { + spyOn(component.labelsFormArray, 'markAllAsTouched'); + spyOn(component.labelsFormArray, 'push'); + + spyOnProperty(component.labelsFormArray, 'invalid').and.returnValue(true); + + component.addNewLabel(); + + expect(component.labelsFormArray.markAllAsTouched).toHaveBeenCalledTimes(1); + expect(component.labelsFormArray.push).not.toHaveBeenCalledTimes(1); + + }); + + it('should add new Label on form valid', () => { + spyOn(component.labelsFormArray, 'markAllAsTouched'); + spyOn(FormUtils, 'addDynamicControl'); + + spyOnProperty(component.labelsFormArray, 'invalid').and.returnValue(false); + + expect(component.labelsFormArray.controls.length).toEqual(1); + + component.addNewLabel(); + + expect(component.labelsFormArray.markAllAsTouched).toHaveBeenCalledTimes(1); + expect(FormUtils.addDynamicControl).toHaveBeenCalledTimes(2); + expect(component.labelsFormArray.length).toEqual(2); + + }); + + it('should delete label', () => { + component.deleteLabel(0); + expect(component.labelsFormArray.length).toEqual(0) + }) + +}); diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.ts new file mode 100644 index 0000000000..c597271c4d --- /dev/null +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/components/widgets/tkg-labels/tkg-labels.component.ts @@ -0,0 +1,69 @@ +import { Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; +import { FormArray, FormGroup } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { distinctUntilChanged, takeUntil } from 'rxjs/operators'; +import { ValidatorEnum } from '../../../constants/validation.constants'; +import { FieldMapping } from '../../../field-mapping/FieldMapping'; +import { FormUtils } from '../../../utils/form-utils'; +import { TKGLabelsConfig } from './interfaces/tkg-labels.interface'; + +@Component({ + selector: 'app-tkg-labels', + templateUrl: './tkg-labels.component.html', + styleUrls: ['./tkg-labels.component.scss'] +}) +export class TkgLabelsComponent implements OnChanges, OnDestroy { + @Input() config: TKGLabelsConfig; + + validatorEnum = ValidatorEnum; + + stopSubscriptions$ = new Subject(); + + get labelsFormArray(): FormArray { + return this.config.forms.control as FormArray; + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.config.previousValue !== changes.config.currentValue && changes.config.currentValue) { + this.labelsFormArray.valueChanges + .pipe( + takeUntil(this.stopSubscriptions$), + distinctUntilChanged((k, v) => JSON.stringify(k) === JSON.stringify(v)) + ) + .subscribe(() => { + this.labelsFormArray.controls.forEach((label: FormGroup) => { + (this.config.fields.fieldMapping as FieldMapping).children.forEach((field) => + label.get(field.name).updateValueAndValidity() + ); + }); + }); + } + } + + createLabel(): FormGroup { + const labelFormGroup = new FormGroup({}); + (this.config.fields.fieldMapping as FieldMapping).children.forEach((fieldMapping) => + FormUtils.addDynamicControl(labelFormGroup, '', fieldMapping) + ); + + return labelFormGroup; + } + + deleteLabel(index: number): void { + this.labelsFormArray.removeAt(index); + } + + addNewLabel(): void { + this.labelsFormArray.markAllAsTouched(); + + if (this.labelsFormArray.invalid) { + return; + } + this.labelsFormArray.push(this.createLabel()); + } + + ngOnDestroy(): void { + this.stopSubscriptions$.next(); + this.stopSubscriptions$.complete(); + } +} diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/constants/validation.constants.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/constants/validation.constants.ts index 2d9acad3c3..769ffd1273 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/constants/validation.constants.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/constants/validation.constants.ts @@ -91,7 +91,8 @@ export enum ValidatorEnum { VALID_CLUSTER_NAME = 'cluster name valid', // Metadata label - LABEL_UNIQUE = 'label unique' + LABEL_UNIQUE = 'label unique', + UNIQUE = 'unique' } // SimpleValidator identifies validators available from the Validation service @@ -119,5 +120,8 @@ export enum SimpleValidator { IS_VALID_PORT, IS_VALID_RESOURCE_GROUP_NAME, NO_WHITE_SPACE, - NO_TRAILING_SLASH + NO_TRAILING_SLASH, + RX_UNIQUE, + RX_REQUIRED_IF_VALUE, + RX_REQUIRED_IF_KEY } diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/field-mapping/FieldMapping.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/field-mapping/FieldMapping.ts index 49ed37bf3f..64f348b9e2 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/field-mapping/FieldMapping.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/field-mapping/FieldMapping.ts @@ -32,7 +32,13 @@ export interface FieldMapping { restorer?: (value: any) => void, // given a saved value (or a retrieved object) this closure will store it. Used esp w/hasNoDomControl retriever?: (value: any) => any, // given a saved value, this closure will retrieve a backing object. validators?: SimpleValidator[], // validators used by Clarity framework + + // Enhanced Field Mapping + controlType?: ControlType // type of control to use for this field FormControl, FormArray, FormGroup + children?: FieldMapping[] + displayFunction?: (field) => string } + // NOTES on FieldMapping: // requiresBackendData: // This is for fields that give the user the option to pick from a list of backend resources or options. The field should use a stored @@ -61,3 +67,9 @@ export interface FieldMapping { // retriever: // For fields that use a JavaScript OBJECT (not a string or a Map), when the retrieves the saved value, it needs a way // to retrieve the full object using the stored value (which is a string). This "retriever" closure provides that retrieval mechanism. + +export const enum ControlType { + FormControl, + FormArray, + FormGroup +} diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/utils/form-utils.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/utils/form-utils.ts index de14faf3c4..5592d7ba31 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/utils/form-utils.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/utils/form-utils.ts @@ -1,4 +1,6 @@ -import { AbstractControl, FormGroup } from "@angular/forms"; +import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import AppServices from 'src/app/shared/service/appServices'; +import { ControlType, FieldMapping } from '../field-mapping/FieldMapping'; export class FormUtils { static addControl(formGroup: FormGroup, name: string, control: AbstractControl) { @@ -7,6 +9,50 @@ export class FormUtils { // Clarity 5's status change logic, which triggers form validation to occur // immediately after form controls are created. Setting emitEvent to false // avoids the Clarity 'triggerAllFormControlValidation' method from being executed. - formGroup.addControl(name, control, { emitEvent: false }); + formGroup.addControl(name, control, {emitEvent: false}); + } + + static addDynamicControl(formGroup: FormGroup, initialValue: any, fieldMapping: FieldMapping): void { + const dynamicControlHandler = + (dynamicMapping: Record void>, defaultCase = ControlType.FormControl) => + (controlType: ControlType) => + (dynamicMapping[controlType] || dynamicMapping[defaultCase])(); + + const formArrayHandler: () => void = () => { + const formData: any[] = (initialValue && initialValue !== '' ? initialValue : null) ?? + fieldMapping.defaultValue ?? [ + fieldMapping.children.reduce((obj, item) => ((obj[item.name] = item.defaultValue), obj), {}) + ]; + + const formArray: any[] = formData.map((obj) => { + const group: any = {}; + for (const [key, value] of Object.entries(obj)) { + const childFieldMapping: FieldMapping = fieldMapping.children.find((child) => child.name === key); + const childValidators = AppServices.fieldMapUtilities.getValidatorArray(childFieldMapping); + group[key] = new FormControl(value, childValidators); + } + return new FormGroup(group); + }); + + formGroup.addControl(fieldMapping.name, new FormArray(formArray), {emitEvent: false}); + }; + + const formControlHandler: () => void = () => { + const validators = AppServices.fieldMapUtilities.getValidatorArray(fieldMapping); + formGroup.addControl(fieldMapping.name, new FormControl(initialValue, validators), {emitEvent: false}); + setTimeout(() => { + formGroup.controls[fieldMapping.name].setValue(initialValue); + }); + }; + + const dynamicMappings: Record void> = { + [ControlType.FormArray]: formArrayHandler, + [ControlType.FormControl]: formControlHandler, + // TODO: Add FormGroup handler + [ControlType.FormGroup]: () => { + } + }; + + dynamicControlHandler(dynamicMappings)(fieldMapping.controlType); } } diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/validation/validation.service.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/validation/validation.service.ts index 7911210303..9a6846c3c2 100755 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/validation/validation.service.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/validation/validation.service.ts @@ -12,6 +12,7 @@ import isIp from 'is-ip'; */ import * as validationMethods from './validation.methods'; import { SimpleValidator, ValidatorEnum } from '../constants/validation.constants'; +import { RxwebValidators } from "@rxweb/reactive-form-validators"; /** * @class ValidationService @@ -23,29 +24,35 @@ export class ValidationService { constructor() { this.simpleValidatorMap = new Map any>([ - [ SimpleValidator.IS_COMMA_SEPARATED_LIST, this.isCommaSeperatedList() ], - [ SimpleValidator.IS_HTTP_OR_HTTPS, this.isHttpOrHttps() ], - [ SimpleValidator.IS_NUMBER_POSITIVE, this.isNumberGreaterThanZero() ], - [ SimpleValidator.IS_NUMERIC_ONLY, this.isNumericOnly() ], - [ SimpleValidator.IS_STRING_WITHOUT_QUERY_PARAMS, this.isStringWithoutQueryParams() ], - [ SimpleValidator.IS_STRING_WITHOUT_URL_FRAGMENT, this.isStringWithoutUrlFragment() ], - [ SimpleValidator.IS_TRUE, this.isTrue() ], - [ SimpleValidator.IS_VALID_CLUSTER_NAME, this.isValidClusterName() ], - [ SimpleValidator.IS_VALID_FQDN, this.isValidFqdn() ], - [ SimpleValidator.IS_VALID_FQDN_OR_IP, this.isValidIpOrFqdn() ], - [ SimpleValidator.IS_VALID_FQDN_OR_IP_HTTPS, this.isValidIpOrFqdnWithHttpsProtocol() ], - [ SimpleValidator.IS_VALID_FQDN_OR_IP_LIST, this.isCommaSeparatedIpsOrFqdn() ], - [ SimpleValidator.IS_VALID_FQDN_OR_IPV6, this.isValidIpv6OrFqdn() ], - [ SimpleValidator.IS_VALID_FQDN_OR_IPV6_HTTPS, this.isValidIpv6OrFqdnWithHttpsProtocol() ], - [ SimpleValidator.IS_VALID_IP, this.isValidIp() ], - [ SimpleValidator.IS_VALID_IP_LIST, this.isValidIps() ], - [ SimpleValidator.IS_VALID_IP_NETWORK_SEGMENT, this.isValidIpNetworkSegment() ], - [ SimpleValidator.IS_VALID_IPV6_NETWORK_SEGMENT, this.isValidIpv6NetworkSegment() ], - [ SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, this.isValidLabelOrAnnotation() ], - [ SimpleValidator.IS_VALID_PORT, this.isValidPort() ], - [ SimpleValidator.IS_VALID_RESOURCE_GROUP_NAME, this.isValidResourceGroupName() ], - [ SimpleValidator.NO_WHITE_SPACE, this.noWhitespaceOnEnds() ], - [ SimpleValidator.NO_TRAILING_SLASH, this.noTrailingSlash() ], + [SimpleValidator.IS_COMMA_SEPARATED_LIST, this.isCommaSeperatedList()], + [SimpleValidator.IS_HTTP_OR_HTTPS, this.isHttpOrHttps()], + [SimpleValidator.IS_NUMBER_POSITIVE, this.isNumberGreaterThanZero()], + [SimpleValidator.IS_NUMERIC_ONLY, this.isNumericOnly()], + [SimpleValidator.IS_STRING_WITHOUT_QUERY_PARAMS, this.isStringWithoutQueryParams()], + [SimpleValidator.IS_STRING_WITHOUT_URL_FRAGMENT, this.isStringWithoutUrlFragment()], + [SimpleValidator.IS_TRUE, this.isTrue()], + [SimpleValidator.IS_VALID_CLUSTER_NAME, this.isValidClusterName()], + [SimpleValidator.IS_VALID_FQDN, this.isValidFqdn()], + [SimpleValidator.IS_VALID_FQDN_OR_IP, this.isValidIpOrFqdn()], + [SimpleValidator.IS_VALID_FQDN_OR_IP_HTTPS, this.isValidIpOrFqdnWithHttpsProtocol()], + [SimpleValidator.IS_VALID_FQDN_OR_IP_LIST, this.isCommaSeparatedIpsOrFqdn()], + [SimpleValidator.IS_VALID_FQDN_OR_IPV6, this.isValidIpv6OrFqdn()], + [SimpleValidator.IS_VALID_FQDN_OR_IPV6_HTTPS, this.isValidIpv6OrFqdnWithHttpsProtocol()], + [SimpleValidator.IS_VALID_IP, this.isValidIp()], + [SimpleValidator.IS_VALID_IP_LIST, this.isValidIps()], + [SimpleValidator.IS_VALID_IP_NETWORK_SEGMENT, this.isValidIpNetworkSegment()], + [SimpleValidator.IS_VALID_IPV6_NETWORK_SEGMENT, this.isValidIpv6NetworkSegment()], + [SimpleValidator.IS_VALID_LABEL_OR_ANNOTATION, this.isValidLabelOrAnnotation()], + [SimpleValidator.IS_VALID_PORT, this.isValidPort()], + [SimpleValidator.IS_VALID_RESOURCE_GROUP_NAME, this.isValidResourceGroupName()], + [SimpleValidator.NO_WHITE_SPACE, this.noWhitespaceOnEnds()], + [SimpleValidator.NO_TRAILING_SLASH, this.noTrailingSlash()], + [SimpleValidator.RX_UNIQUE, RxwebValidators.unique()], + [ + SimpleValidator.RX_REQUIRED_IF_VALUE, + RxwebValidators.required({conditionalExpression: (x, _) => !!x.value}) + ], + [SimpleValidator.RX_REQUIRED_IF_KEY, RxwebValidators.required({conditionalExpression: (x, _) => !!x.key})] ]); } @@ -62,7 +69,7 @@ export class ValidationService { const ctrlValue: string = control.value; if (ctrlValue) { return validationMethods.isValidIp(ctrlValue) ? - null : { [ValidatorEnum.VALID_IP]: true }; + null : {[ValidatorEnum.VALID_IP]: true}; } return null; } @@ -113,7 +120,7 @@ export class ValidationService { const ips: Array = ctrlValue.split(','); return ips .map(ipStr => validationMethods.isValidIp(ipStr)) - .reduce((a, b) => a && b, true) ? null : { [ValidatorEnum.VALID_IP]: true }; + .reduce((a, b) => a && b, true) ? null : {[ValidatorEnum.VALID_IP]: true}; } return null; } @@ -128,7 +135,7 @@ export class ValidationService { const ctrlValue: string = control.value; if (ctrlValue) { return validationMethods.isValidFqdn(ctrlValue) ? - null : { [ValidatorEnum.VALID_FQDN]: true }; + null : {[ValidatorEnum.VALID_FQDN]: true}; } return null; } @@ -156,7 +163,7 @@ export class ValidationService { /** * @method isValidIpOrFqdn validator to check if input is valid IP or FQDN */ - isValidIpv6OrFqdn(): any { + isValidIpv6OrFqdn(): any { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue) { @@ -192,11 +199,11 @@ export class ValidationService { } } - /** + /** * @method isValidIpv6OrFqdnWithHttpsProtocol validator to check if input is valid IP or FQDN * with protocol prefix */ - isValidIpv6OrFqdnWithHttpsProtocol(): any { + isValidIpv6OrFqdnWithHttpsProtocol(): any { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue) { @@ -313,7 +320,7 @@ export class ValidationService { }; } return validationMethods.isValidClustername(ctrlValue) ? - null : { [ValidatorEnum.VALID_CLUSTER_NAME]: true }; + null : {[ValidatorEnum.VALID_CLUSTER_NAME]: true}; } return null; } @@ -340,7 +347,7 @@ export class ValidationService { } } return validationMethods.isValidLabelOrAnnotation(ctrlValue) ? - null : { [ValidatorEnum.VALID_CLUSTER_NAME]: true }; + null : {[ValidatorEnum.VALID_CLUSTER_NAME]: true}; } return null; } @@ -531,7 +538,7 @@ export class ValidationService { * @method isValidIpv6NetworkSegment * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/xxx */ - isValidIpv6NetworkSegment(): any { + isValidIpv6NetworkSegment(): any { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue) { @@ -566,7 +573,7 @@ export class ValidationService { const currentControlIp = control.value; for (const ipAddr of otherControls) { if (currentControlIp === ipAddr.value) { - return { [ValidatorEnum.NETWORKING_IP_UNIQUE]: true }; + return {[ValidatorEnum.NETWORKING_IP_UNIQUE]: true}; } } } @@ -609,17 +616,17 @@ export class ValidationService { if (validationMethods.isNumericOnly(ctrlValue)) { return null; } - return { [ValidatorEnum.NUMERIC_ONLY]: true }; + return {[ValidatorEnum.NUMERIC_ONLY]: true}; } return null; } } /** - * @method commaSeparatedIpOrFqdn - * @param {string} arg input string - * @return {boolean} - */ + * @method commaSeparatedIpOrFqdn + * @param {string} arg input string + * @return {boolean} + */ commaSeparatedIpOrFqdn(arg: string): any { const ips = arg.split(','); return ips.map(ip => validationMethods.isValidIp(ip) || validationMethods.isValidFqdn(ip)).reduce((a, b) => a && b, true); @@ -633,7 +640,7 @@ export class ValidationService { const ctrlValue: string = control.value; if (ctrlValue) { if (!this.commaSeparatedIpOrFqdn(ctrlValue)) { - return { [ValidatorEnum.VALID_IP_OR_FQDN]: true }; + return {[ValidatorEnum.VALID_IP_OR_FQDN]: true}; } } return null; @@ -650,7 +657,7 @@ export class ValidationService { return null; } if (typeof ctrlValue !== 'number' || ctrlValue < 1) { - return { [ValidatorEnum.GREATER_THAN_ZERO]: true }; + return {[ValidatorEnum.GREATER_THAN_ZERO]: true}; } return null; } @@ -662,7 +669,7 @@ export class ValidationService { const currentAz = control.value; for (const az of otherControls) { if (currentAz === az.value) { - return { [ValidatorEnum.AVAILABILITY_ZONE_UNIQUE]: true }; + return {[ValidatorEnum.AVAILABILITY_ZONE_UNIQUE]: true}; } } } @@ -673,7 +680,7 @@ export class ValidationService { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue && !XRegExp('^[\\pL-_.()\\w]+$').test(ctrlValue)) { - return { [ValidatorEnum.VALID_RESOURCE_GROUP_NAME]: true }; + return {[ValidatorEnum.VALID_RESOURCE_GROUP_NAME]: true}; } return null; } @@ -683,7 +690,7 @@ export class ValidationService { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue && resourceGroups.find(x => x.name === ctrlValue) != null) { - return { [ValidatorEnum.UNIQUE_RESOURCE_GROUP_NAME]: true }; + return {[ValidatorEnum.UNIQUE_RESOURCE_GROUP_NAME]: true}; } return null; } @@ -709,7 +716,7 @@ export class ValidationService { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (ctrlValue && !XRegExp('^https?:\/\/').test(ctrlValue)) { - return { [ValidatorEnum.HTTP_OR_HTTPS]: true }; + return {[ValidatorEnum.HTTP_OR_HTTPS]: true}; } return null; } @@ -728,21 +735,22 @@ export class ValidationService { const inputVal = ipCtrl.value; if (inputVal) { if (!validationMethods.isValidIp(inputVal) && !(validationMethods.isValidFqdn(inputVal))) { - return { [ValidatorEnum.VALID_IP_OR_FQDN]: true }; + return {[ValidatorEnum.VALID_IP_OR_FQDN]: true}; } } else { - return { [ValidatorEnum.REQUIRED]: true }; + return {[ValidatorEnum.REQUIRED]: true}; } if (control.value) { if (!validationMethods.isNumericOnly(control.value)) { - return { [ValidatorEnum.VALID_PORT]: true }; + return {[ValidatorEnum.VALID_PORT]: true}; } return null; } - return { [ValidatorEnum.REQUIRED]: true }; + return {[ValidatorEnum.REQUIRED]: true}; } } + /** * @method isValidIpv6Ldap * - non-empty @@ -751,30 +759,31 @@ export class ValidationService { * * @param {AbstractControl} ipCtrl */ - isValidIpv6Ldap(ipCtrl: AbstractControl): any { + isValidIpv6Ldap(ipCtrl: AbstractControl): any { return (control: AbstractControl) => { const inputVal = ipCtrl.value; if (inputVal) { if (!isIp.v6(inputVal) && !(validationMethods.isValidFqdn(inputVal))) { - return { [ValidatorEnum.VALID_IP_OR_FQDN]: true }; + return {[ValidatorEnum.VALID_IP_OR_FQDN]: true}; } } else { - return { [ValidatorEnum.REQUIRED]: true }; + return {[ValidatorEnum.REQUIRED]: true}; } if (control.value) { if (!validationMethods.isNumericOnly(control.value)) { - return { [ValidatorEnum.VALID_PORT]: true }; + return {[ValidatorEnum.VALID_PORT]: true}; } return null; } - return { [ValidatorEnum.REQUIRED]: true }; + return {[ValidatorEnum.REQUIRED]: true}; } } + isValidNameInList(list: Array): any { return (control: AbstractControl) => { const ctrlValue: string = control.value; if (list.indexOf(ctrlValue) === -1) { - return { [ValidatorEnum.NOT_IN_DATALIST]: true }; + return {[ValidatorEnum.NOT_IN_DATALIST]: true}; } return null; } @@ -784,7 +793,7 @@ export class ValidationService { return (control: AbstractControl) => { const ctrlValue: boolean = control.value; if (ctrlValue === true) { - return { [ValidatorEnum.TRUE]: true }; + return {[ValidatorEnum.TRUE]: true}; } return null; } @@ -795,7 +804,7 @@ export class ValidationService { const ctrlValue: string = control.value; for (const key of keys) { if (ctrlValue !== '' && ctrlValue === formGroup.value[key] && key !== name) { - return { [ValidatorEnum.LABEL_UNIQUE]: true} + return {[ValidatorEnum.LABEL_UNIQUE]: true} } } return null; diff --git a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/wizard-base/wizard-base.ts b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/wizard-base/wizard-base.ts index eedcfedddc..ef6e1fa640 100644 --- a/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/wizard-base/wizard-base.ts +++ b/pkg/v1/tkg/web/src/app/views/landing/wizard/shared/wizard-base/wizard-base.ts @@ -17,8 +17,11 @@ import { EditionData } from '../../../../../shared/service/branding.service'; import { FieldMapping } from '../field-mapping/FieldMapping'; import { FormDataForHTML, FormUtility } from '../components/steps/form-utility'; import { IdentityField } from '../components/steps/identity-step/identity-step.fieldmapping'; -import { LoadBalancerField } from '../components/steps/load-balancer/load-balancer-step.fieldmapping'; -import { MetadataField } from '../components/steps/metadata-step/metadata-step.fieldmapping'; +import { + LoadBalancerField, + LoadBalancerStepMapping +} from '../components/steps/load-balancer/load-balancer-step.fieldmapping'; +import { MetadataField, MetadataStepMapping } from '../components/steps/metadata-step/metadata-step.fieldmapping'; import { MetadataStepComponent } from '../components/steps/metadata-step/metadata-step.component'; import { NetworkField } from '../components/steps/network-step/network-step.fieldmapping'; import { OsImageField } from '../components/steps/os-image-step/os-image-step.fieldmapping'; @@ -51,6 +54,16 @@ export interface StepRegistrantData { eventFileImportError: TanzuEventType, // the event the wizard broadcasts when an error occurs during file import } +export interface Params { + key: (obj: T) => string, + value: (obj: T) => string +} + +export interface Label { + key: string, + value: string +} + @Directive() export abstract class WizardBaseDirective extends BasicSubscriber implements WizardStepRegistrar, OnInit { APP_ROUTES: Routes = APP_ROUTES; @@ -88,12 +101,16 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz // supplyFileImportedEvent() allows the child class to give this class the event to broadcast on successful file import protected abstract supplyFileImportedEvent(): TanzuEventType; + // supplyFileImportErrorEvent() allows the child class to give this class the event to broadcast on file import error protected abstract supplyFileImportErrorEvent(): TanzuEventType; + // supplyStepData() allows the child class gives this class the data for the steps. protected abstract supplyStepData(): FormDataForHTML[]; + // supplyWizardName() allows the child class gives this class the wizard name; this is used to identify which wizard a step belongs to protected abstract supplyWizardName(): string; + // supplyDisplayOrder() allows the child class to specify the order (and which steps) get displayed (on confirmation page). // By default, we take the order from the stepData (so stepData should be set before invoking this method) protected supplyDisplayOrder(): string[] { @@ -124,19 +141,21 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz // set step description (if it's a step description for this wizard) AppServices.messenger.subscribe(TanzuEventType.STEP_DESCRIPTION_CHANGE, data => { - const stepDescriptionPayload = data.payload as StepDescriptionChangePayload; - if (this.supplyWizardName() === stepDescriptionPayload.wizard) { - // we use setTimeout to avoid a possible ExpressionChangedAfterItHasBeenCheckedError - setTimeout(() => { this.stepDescription[stepDescriptionPayload.step] = stepDescriptionPayload.description; }, 0); - } - }, this.unsubscribe); + const stepDescriptionPayload = data.payload as StepDescriptionChangePayload; + if (this.supplyWizardName() === stepDescriptionPayload.wizard) { + // we use setTimeout to avoid a possible ExpressionChangedAfterItHasBeenCheckedError + setTimeout(() => { + this.stepDescription[stepDescriptionPayload.step] = stepDescriptionPayload.description; + }, 0); + } + }, this.unsubscribe); // set branding and cluster type on branding change for base wizard components AppServices.messenger.subscribe(TanzuEventType.BRANDING_CHANGED, data => { - this.edition = data.payload.edition; - this.clusterTypeDescriptor = data.payload.clusterTypeDescriptor; - this.title = data.payload.branding.title; - }, this.unsubscribe); + this.edition = data.payload.edition; + this.clusterTypeDescriptor = data.payload.clusterTypeDescriptor; + this.title = data.payload.branding.title; + }, this.unsubscribe); setTimeout(() => this.broadcastStepStarted(this.firstStep), 0); } @@ -150,7 +169,7 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz /** * Retrieve the config file from the backend and return as a string */ - abstract retrieveExportFile(): Observable; + abstract retrieveExportFile(): Observable; /** * Switch the mode between "Review Configuration" and "Edit Configuration" @@ -190,7 +209,9 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz * @method method to trigger deployment */ abstract createRegionalCluster(params: any): Observable; + abstract getPayload(): any; + abstract setFromPayload(payload: any); isOnFirstStep() { @@ -320,6 +341,31 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz }); } + /** + * @param arrObj of type [ {key: 'a', value: '1}, {key : 'b, value:'2}] + * @param key To Identify the ObjectKey + * @param value To Identify the ObjectValue + * @return Object {'a': 1 , 'b': '1'} + */ + arrayOfObjectsToObject(arrObj: T[], params: Params): { [key: string]: string; } { + return arrObj && arrObj instanceof Array + ? arrObj.reduce((obj, item) => ((obj[params.key(item)] = params.value(item)), obj), {}) + : {}; + } + + /** + * @param obj of type {'a' : '1', 'b': '2'} + * @return Array of Objects [ {key: 'a', value:'1'}, {key:'b', value:'2'}] + */ + objectToArrayOfObjects(obj: Record): Label[] { + let responseObject: Label[] = []; + for (const [key, value] of Object.entries(obj)) { + responseObject = [...responseObject, {key, value: value as string} + ] + } + return responseObject; + } + /** * Converts ES6 map to stringifyable object * @param strMap ES6 map that will be converted @@ -414,11 +460,15 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz } payload.ceipOptIn = this.getBooleanFieldValue(WizardForm.CEIP, CeipField.OPTIN); - // TODO: for labels, we are reaching into storage to get the value, whereas all the other data come from fields - // It would be better for ALL the fields to use a FieldMapping to retrieve the data - const labelFieldMapping: FieldMapping = {name: MetadataField.CLUSTER_LABELS, isMap: true}; - let labelsMap = AppServices.userDataService.retrieveStoredValue(this.supplyWizardName(), WizardForm.METADATA, labelFieldMapping); - payload.labels = this.strMapToObj(labelsMap); + + const metaDataLabels = this.getFieldValue(WizardForm.METADATA, MetadataField.CLUSTER_LABELS); + payload.labels = this.arrayOfObjectsToObject<{ key: string, value: string }>( + metaDataLabels, + { + key: label => label.key, + value: label => label.value + } + ); payload.os = this.getFieldValue(WizardForm.OSIMAGE, OsImageField.IMAGE); payload.annotations = { @@ -469,9 +519,8 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz , payload.identityManagement); } - // TODO: for clusterLabels, we are reaching into storage to get the value, whereas all the other data come from fields - // It would be better for ALL the fields to use a FieldMapping to retrieve the data - labelsMap = AppServices.userDataService.retrieveStoredValue(this.supplyWizardName(), 'loadBalancerForm', labelFieldMapping); + const loadBalancerLabels = this.getFieldValue(WizardForm.LOADBALANCER, LoadBalancerField.CLUSTER_LABELS); + payload.aviConfig = { 'controller': this.getFieldValue(WizardForm.LOADBALANCER, LoadBalancerField.CONTROLLER_HOST), 'username': this.getFieldValue(WizardForm.LOADBALANCER, LoadBalancerField.USERNAME), @@ -483,7 +532,13 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz 'name': this.getFieldValue(WizardForm.LOADBALANCER, LoadBalancerField.NETWORK_NAME), 'cidr': this.getFieldValue(WizardForm.LOADBALANCER, LoadBalancerField.NETWORK_CIDR) }, - 'labels': this.strMapToObj(labelsMap), + 'labels': this.arrayOfObjectsToObject<{ key: string, value: string }>( + loadBalancerLabels, + { + key: label => label.key, + value: label => label.value + } + ) } return payload; } @@ -497,25 +552,34 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz step: stepName, formGroup: this.form.controls[stepName] as FormGroup, eventFileImported: this.supplyFileImportedEvent(), - eventFileImportError: this.supplyFileImportErrorEvent(), + eventFileImportError: this.supplyFileImportErrorEvent() } stepComponent.setStepRegistrantData(stepRegistrantData); } + // // Methods that fulfill WizardStepRegistrar // storeFieldString() is a convenience method to avoid lengthy code lines storeFieldString(step, field, value: string, displayString?: string) { - const identifier = { wizard: this.supplyWizardName(), step, field }; + const identifier = {wizard: this.supplyWizardName(), step, field}; const display = displayString ? displayString : value; - AppServices.userDataService.store(identifier, { value, display }); + AppServices.userDataService.store(identifier, {value, display}); + } + + storeFieldArray(step, field, value: T[], display?: string, displayFunction?: (field) => string) { + const identifier = {wizard: this.supplyWizardName(), step, field}; + AppServices.userDataService.store(identifier, { + value, + display: display ?? displayFunction(value) ?? value.join(', ') + }); } storeFieldBoolean(step, field: string, booleanValue: boolean) { - const identifier = { wizard: this.supplyWizardName(), step, field }; + const identifier = {wizard: this.supplyWizardName(), step, field}; const display = booleanValue ? 'yes' : 'no'; const value = String(booleanValue); - AppServices.userDataService.store(identifier, { value, display }); + AppServices.userDataService.store(identifier, {value, display}); } storeProxyFieldsFromPayload(payload: any) { @@ -557,7 +621,7 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz * @param payload */ saveCommonFieldsFromPayload(payload: any) { - if (payload.networking !== undefined ) { + if (payload.networking !== undefined) { // Networking - general this.storeFieldString(WizardForm.NETWORK, NetworkField.NETWORK_NAME, payload.networking.networkName); this.storeFieldString(WizardForm.NETWORK, NetworkField.CLUSTER_SERVICE_CIDR, payload.networking.clusterServiceCIDR); @@ -571,15 +635,14 @@ export abstract class WizardBaseDirective extends BasicSubscriber implements Wiz // Other fields this.storeFieldString(WizardForm.CEIP, CeipField.OPTIN, payload.ceipOptIn); if (payload.labels !== undefined) { - // we construct a label value that mimics how the meta-data step constructs the saved label value - // when the user creates it label by label - const labelArray: Array = []; - Object.keys(payload.labels).forEach(key => { - const value = payload.labels[key]; - labelArray[labelArray.length] = key + ":" + value; - }); - const labelValueToSave = labelArray.join(', '); - this.storeFieldString(WizardForm.METADATA, MetadataField.CLUSTER_LABELS, labelValueToSave); + const arrayOfObjects: Label[] = this.objectToArrayOfObjects(payload.labels); + const fieldMapping: FieldMapping = MetadataStepMapping.fieldMappings.find(field => field.name === MetadataField.CLUSTER_LABELS); + this.storeFieldArray