Skip to content

Commit 75e0f2c

Browse files
Validation on Deploy Page: Labels -> Label Key and Label Value. All synchronous validation moved to custom directives.
1 parent f692b6b commit 75e0f2c

24 files changed

+622
-404
lines changed

src/app/frontend/common/validators/types/integertype.js renamed to src/app/frontend/common/validators/integervalidator.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import {Type} from './type';
15+
import {Validator} from './validator';
1616

1717
/**
1818
* @final
19-
* @extends {Type}
19+
* @extends {Validator}
2020
*/
21-
export class IntegerType extends Type {
21+
export class IntegerValidator extends Validator {
2222
constructor() { super(); }
2323

2424
/**

src/app/frontend/common/validators/validate_directive.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@
2323
*
2424
* usage:
2525
* `<input type="number" kd-validate="integer">`
26+
* `<input type="string" kd-validate="labelKeyNameLength">`
27+
* `<input type="string" kd-validate="labelKeyNamePattern">`
28+
* `<input type="string" kd-validate="labelKeyPrefixLength">`
29+
* `<input type="string" kd-validate="labelKeyPrefixPattern">`
30+
* `<input type="string" kd-validate="labelKeyNameLength">`
2631
*
27-
* @param {!./types/type_factory.TypeFactory} kdTypeFactory
32+
* @param {!./validator_factory.ValidatorFactory} kdValidatorFactory
2833
* @return {!angular.Directive}
2934
* @ngInject
3035
*/
31-
export default function validateDirective(kdTypeFactory) {
32-
const validateType = 'kdValidate';
36+
export default function validateDirective(kdValidatorFactory) {
37+
const validateValidator = 'kdValidate';
3338

3439
return {
3540
restrict: 'A',
@@ -41,10 +46,18 @@ export default function validateDirective(kdTypeFactory) {
4146
* @param {!angular.NgModelController} ctrl
4247
*/
4348
link: (scope, element, attrs, ctrl) => {
44-
/** @type {!./types/type.Type} */
45-
let type = kdTypeFactory.getType(attrs[validateType]);
49+
let validateValidatorNames = attrs[validateValidator].split(',');
4650

47-
ctrl.$validators['kdValid'] = (value) => { return type.isValid(value); };
51+
validateValidatorNames.forEach((validateValidatorName) => {
52+
validateValidatorName = validateValidatorName.trim();
53+
/** @type {!./validator.Validator} */
54+
let validator = kdValidatorFactory.getValidator(validateValidatorName);
55+
// To preserve camel case on validator name
56+
let validatorName =
57+
`kdValid${validateValidatorName[0].toUpperCase()}${validateValidatorName.substr(1)}`;
58+
59+
ctrl.$validators[validatorName] = (value) => { return validator.isValid(value); };
60+
});
4861
},
4962
};
5063
}

src/app/frontend/common/validators/types/type.js renamed to src/app/frontend/common/validators/validator.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
*
2121
* @class
2222
*/
23-
export class Type {
23+
export class Validator {
2424
constructor() {
25-
if (this.constructor === Type) {
26-
throw new TypeError('Abstract class "Type" cannot be instantiated directly.');
25+
if (this.constructor === Validator) {
26+
throw new TypeError('Abstract class "Validator" cannot be instantiated directly.');
2727
}
2828
}
2929

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright 2015 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {IntegerValidator} from './integervalidator';
16+
import {LabelKeyNameLengthValidator} from 'deploy/validators/labelkeynamelengthvalidator';
17+
import {LabelKeyPrefixLengthValidator} from 'deploy/validators/labelkeyprefixlengthvalidator';
18+
import {LabelKeyNamePatternValidator} from 'deploy/validators/labelkeynamepatternvalidator';
19+
import {LabelKeyPrefixPatternValidator} from 'deploy/validators/labelkeyprefixpatternvalidator';
20+
import {LabelValuePatternValidator} from 'deploy/validators/labelvaluepatternvalidator';
21+
22+
/**
23+
* @final
24+
*/
25+
export class ValidatorFactory {
26+
/**
27+
* @constructs ValidatorFactory
28+
*/
29+
constructor() {
30+
/** @private {Map<Array<string, !./validator.Validator>>} */
31+
this.validatorMap_ = new Map();
32+
33+
// Initialize map with supported types
34+
this.validatorMap_.set('integer', new IntegerValidator());
35+
this.validatorMap_.set('labelKeyNameLength', new LabelKeyNameLengthValidator());
36+
this.validatorMap_.set('labelKeyPrefixLength', new LabelKeyPrefixLengthValidator());
37+
this.validatorMap_.set('labelKeyNamePattern', new LabelKeyNamePatternValidator());
38+
this.validatorMap_.set('labelKeyPrefixPattern', new LabelKeyPrefixPatternValidator());
39+
this.validatorMap_.set('labelValuePattern', new LabelValuePatternValidator());
40+
}
41+
42+
/**
43+
* Returns specific Type class based on given type name.
44+
*
45+
* @method
46+
* @param validatorName
47+
* @returns {!./validator.Validator}
48+
*/
49+
getValidator(validatorName) {
50+
let validatorObject = this.validatorMap_.get(validatorName);
51+
52+
if (!validatorObject) {
53+
throw new Error(`Given validator '${validatorName}' is not supported.`);
54+
}
55+
56+
return validatorObject;
57+
}
58+
}

src/app/frontend/common/validators/validators_module.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
import validateDirective from './validate_directive';
16-
import {TypeFactory} from './types/type_factory';
16+
import {ValidatorFactory} from './validator_factory';
1717

1818
/**
1919
* Module containing validators for the application.
@@ -24,5 +24,5 @@ export default angular
2424
[
2525
'ngMaterial',
2626
])
27-
.service('kdTypeFactory', TypeFactory)
27+
.service('kdValidatorFactory', ValidatorFactory)
2828
.directive('kdValidate', validateDirective);

src/app/frontend/deploy/deployfromsettings.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
kd-validate="integer" kd-warn-threshold="100" kd-warn-threshold-bind="showWarning">
8181
<ng-messages for="ctrl.form.replicas.$error" role="alert" multiple>
8282
<ng-message when="required">Number of pods is required.</ng-message>
83-
<ng-message when="number, kdValid">Number of pods must be a positive integer.</ng-message>
83+
<ng-message when="number, kdValidInteger">Number of pods must be a positive integer.</ng-message>
8484
<ng-message when="min">Number of pods must be at least 1.</ng-message>
8585
</ng-messages>
8686
<span class="kd-warn-threshold" ng-show="showWarning">

src/app/frontend/deploy/deploylabel.html

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,34 @@
1717
<ng-form layout="row" flex="auto" name="labelForm">
1818
<md-input-container md-no-float class="kd-deploy-input-row" flex="45">
1919
<input name="key" ng-model="labelCtrl.label.key" ng-change="labelCtrl.check(labelForm)"
20-
placeholder="{{labelCtrl.label.key}}" ng-disabled="!labelCtrl.label.editable">
21-
<ng-messages for="labelForm.key.$error" ng-if="labelForm.key.$invalid">
20+
placeholder="{{labelCtrl.label.key}}" ng-disabled="!labelCtrl.label.editable"
21+
kd-validate="labelKeyNameLength,labelKeyPrefixLength,labelKeyNamePattern,labelKeyPrefixPattern"
22+
ng-model-options="{allowInvalid: true}">
23+
<ng-messages for="labelForm.key.$error" ng-if="labelForm.key.$touched && labelForm.key.$invalid">
2224
<ng-message when="unique">{{labelCtrl.label.key}} is not unique.</ng-message>
23-
<ng-message when="prefixPattern">
24-
Prefix (before slash) is not a valid DNS subdomain prefix. Example: my-domain.com
25+
<ng-message when="kdValidLabelKeyPrefixPattern">
26+
Prefix is not a valid DNS subdomain prefix. Example: my-domain.com
2527
</ng-message>
26-
<ng-message when="namePattern">
27-
Label key name should be lower or upper-case alphanumeric with '-', '_' and '.' between words only
28+
<ng-message when="kdValidLabelKeyNamePattern">
29+
Label key name must be alphanumeric separated by '-', '_' or '.',
30+
optionally prefixed by a DNS subdomain and '/'
2831
</ng-message>
29-
<ng-message when="prefixLength">Prefix should not exceed 253 characters</ng-message>
30-
<ng-message when="nameLength">Label Key name should not exceed 63 characters</ng-message>
32+
<ng-message when="kdValidLabelKeyPrefixLength">Prefix should not exceed 253 characters</ng-message>
33+
<ng-message when="kdValidLabelKeyNameLength">Label Key name should not exceed 63 characters</ng-message>
3134
</ng-messages>
3235
</md-input-container>
3336
<p flex="5"></p>
3437
<md-input-container md-no-float class="kd-deploy-input-row" flex="40">
35-
<input ng-model="labelCtrl.label.value" ng-change="labelCtrl.check()"
38+
<input name="value" ng-model="labelCtrl.label.value"
3639
placeholder="{{labelCtrl.label.value}}" ng-disabled="!labelCtrl.label.editable"
37-
ng-model-options="{ getterSetter: true }">
38-
<ng-messages ng-if="labelForm.key.$error.unique"></ng-messages>
40+
kd-validate="labelValuePattern" ng-change="labelCtrl.check()" md-maxlength="253"
41+
ng-model-options="{ getterSetter: true, allowInvalid: true }">
42+
<ng-messages for="labelForm.value.$error" ng-if="labelForm.value.$touched && labelForm.value.$invalid">
43+
<ng-message when="kdValidLabelValuePattern">
44+
Label value must be alphanumeric separated by '.' , '-' or '_'
45+
</ng-message>
46+
<ng-message when="maxlength">Label Value must not exceed 253 characters</ng-message>
47+
</ng-messages>
3948
</md-input-container>
4049
<md-button type="button" ng-show="labelCtrl.isRemovable()"
4150
ng-click="labelCtrl.deleteLabel()"

src/app/frontend/deploy/deploylabel_controller.js

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export default class DeployLabelController {
7575
addIfNeeded_() {
7676
/** @type {!DeployLabel} */
7777
let lastLabel = this.labels[this.labels.length - 1];
78-
7978
if (this.isFilled_(lastLabel)) {
8079
this.addNewLabel_();
8180
}
@@ -91,41 +90,18 @@ export default class DeployLabelController {
9190
* Validates label within label form.
9291
* Current checks:
9392
* - duplicated key
94-
* - key prefix pattern
95-
* - key name pattern
96-
* - key prefix length
97-
* - key name length
9893
* @param {!angular.FormController|undefined} labelForm
9994
* @private
10095
*/
101-
// TODO: @digitalfishpond Move these validations to directives
10296
validateKey_(labelForm) {
10397
if (angular.isDefined(labelForm)) {
10498
/** @type {!angular.NgModelController} */
10599
let elem = labelForm.key;
106-
/** @type {!RegExp} */
107-
let PrefixPattern = /^(.*\/.*)$/;
108-
/** @type {boolean} */
109-
let isPrefixed = PrefixPattern.test(this.label.key);
110-
/** @type {number} */
111-
let slashPosition = isPrefixed ? this.label.key.indexOf('/') : -1;
112100

113101
/** @type {boolean} */
114102
let isUnique = !this.isKeyDuplicated_();
115-
/** @type {boolean} */
116-
let isKeyPrefixPatternOk = this.matchesKeyPrefixPattern_(isPrefixed, slashPosition);
117-
/** @type {boolean} */
118-
let isKeyNamePatternOk = this.matchesKeyNamePattern_(isPrefixed, slashPosition);
119-
/** @type {boolean} */
120-
let isKeyPrefixLengthOk = this.matchesKeyPrefixLength_(isPrefixed, slashPosition);
121-
/** @type {boolean} */
122-
let isKeyNameLengthOk = this.matchesKeyNameLength_(isPrefixed, slashPosition);
123103

124104
elem.$setValidity('unique', isUnique);
125-
elem.$setValidity('prefixPattern', isKeyPrefixPatternOk);
126-
elem.$setValidity('namePattern', isKeyNamePatternOk);
127-
elem.$setValidity('prefixLength', isKeyPrefixLengthOk);
128-
elem.$setValidity('nameLength', isKeyNameLengthOk);
129105
}
130106
}
131107

@@ -148,84 +124,6 @@ export default class DeployLabelController {
148124
return duplications > 1;
149125
}
150126

151-
/**
152-
* Returns true if the label key prefix (before the "/" if there is one) matches a lowercase
153-
* alphanumeric character
154-
* optionally followed by lowercase alphanumeric or '-' or '.' and ending with a lower case
155-
* alphanumeric character,
156-
* with '.' only permitted if surrounded by lowercase alphanumeric characters (eg:
157-
* 'good.prefix-pattern',
158-
* otherwise returns false.
159-
* @return {boolean}
160-
* @param {boolean} isPrefixed
161-
* @param {number} slashPosition
162-
* @private
163-
*/
164-
matchesKeyPrefixPattern_(isPrefixed, slashPosition) {
165-
/** @type {!RegExp} */
166-
let labelKeyPrefixPattern = /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/;
167-
/** @type {string} */
168-
let labelPrefix = isPrefixed ? this.label.key.substring(0, slashPosition) : 'valid-pattern';
169-
170-
return (labelKeyPrefixPattern.test(labelPrefix));
171-
}
172-
173-
/**
174-
* Returns true if the label key name (after the "/" if there is one) matches an alphanumeric
175-
* character (upper
176-
* or lower case) optionally followed by alphanumeric or -_. and ending with an alphanumeric
177-
* character
178-
* (upper or lower case), otherwise returns false.
179-
* @return {boolean}
180-
* @param {boolean} isPrefixed
181-
* @param {number} slashPosition
182-
* @private
183-
*/
184-
matchesKeyNamePattern_(isPrefixed, slashPosition) {
185-
/** @type {!RegExp} */
186-
let labelKeyNamePattern = /^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$/;
187-
/** @type {string} */
188-
let labelName = isPrefixed ? this.label.key.substring(slashPosition + 1) : this.label.key;
189-
190-
return (labelKeyNamePattern.test(labelName));
191-
}
192-
193-
/**
194-
* Returns true if the label key name (after the "/" if there is one) is equal or shorter than 253
195-
* characters,
196-
* otherwise returns false.
197-
* @return {boolean}
198-
* @param {boolean} isPrefixed
199-
* @param {number} slashPosition
200-
* @private
201-
*/
202-
matchesKeyPrefixLength_(isPrefixed, slashPosition) {
203-
/** @type {number} */
204-
let maxLength = 253;
205-
/** @type {string} */
206-
let labelPrefix = isPrefixed ? this.label.key.substring(0, slashPosition) : '';
207-
208-
return (labelPrefix.length <= maxLength);
209-
}
210-
211-
/**
212-
* Returns true if the label key name (after the "/" if there is one) is equal or shorter than 63
213-
* characters,
214-
* otherwise returns false.
215-
* @return {boolean}
216-
* @param {boolean} isPrefixed
217-
* @param {number} slashPosition
218-
* @private
219-
*/
220-
matchesKeyNameLength_(isPrefixed, slashPosition) {
221-
/** @type {number} */
222-
let maxLength = 63;
223-
/** @type {string} */
224-
let labelName = isPrefixed ? this.label.key.substring(slashPosition + 1) : this.label.key;
225-
226-
return (labelName.length <= maxLength);
227-
}
228-
229127
/**
230128
* Returns true if label key and value are not empty, false otherwise.
231129
* @param {!DeployLabel} label
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2015 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import {Validator} from 'common/validators/validator';
16+
17+
/**
18+
* @final
19+
* @extends {Validator}
20+
*/
21+
export class LabelKeyNameLengthValidator extends Validator {
22+
constructor() {
23+
super();
24+
/** @type {number} */
25+
this.maxKeyLength = 63;
26+
}
27+
28+
/**
29+
* Returns true if the label key name (after the "/" if there is one) is equal or shorter than 63
30+
* characters, otherwise returns false.
31+
*
32+
* @override
33+
*/
34+
isValid(value) {
35+
/** @type {number} */
36+
let slashPosition = value.indexOf('/');
37+
/** @type {string} */
38+
let labelKeyName = slashPosition > -1 ? value.substring(slashPosition + 1) : value;
39+
40+
return labelKeyName.length <= this.maxKeyLength;
41+
}
42+
}

0 commit comments

Comments
 (0)