@platecms/delta-validation 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/index.ts +8 -7
- package/src/lib/enums/validation-rule-update-impact.enum.ts +5 -0
- package/src/lib/helpers/fields-to-schema.spec.ts +184 -0
- package/src/lib/helpers/fields-to-schema.ts +141 -0
- package/src/lib/helpers/get-content-value-type-name.ts +26 -0
- package/src/lib/helpers/is-superset.helper.ts +4 -0
- package/src/lib/helpers/schema-builder.ts +5 -0
- package/src/lib/schemas/content-value.schema.ts +27 -2
- package/src/lib/schemas/object.schema.spec.ts +223 -0
- package/src/lib/schemas/object.schema.ts +106 -0
- package/src/lib/types/validation-schema.interface.ts +1 -1
- package/src/lib/validation-rules/allowed-values.validation-rule.spec.ts +25 -0
- package/src/lib/validation-rules/allowed-values.validation-rule.ts +15 -0
- package/src/lib/validation-rules/base.validation-rule.ts +21 -4
- package/src/lib/validation-rules/count.validation-rule.spec.ts +38 -0
- package/src/lib/validation-rules/count.validation-rule.ts +35 -0
- package/src/lib/validation-rules/date-between.validation-rule.spec.ts +83 -0
- package/src/lib/validation-rules/date-between.validation-rule.ts +15 -0
- package/src/lib/validation-rules/decimal-count.validation-rule.spec.ts +26 -0
- package/src/lib/validation-rules/decimal-count.validation-rule.ts +12 -0
- package/src/lib/validation-rules/number-between.validation-rule.spec.ts +44 -0
- package/src/lib/validation-rules/number-between.validation-rule.ts +14 -0
- package/src/lib/validation-rules/relatable-content-types.validation-rule.spec.ts +33 -8
- package/src/lib/validation-rules/relatable-content-types.validation-rule.ts +17 -0
- package/src/lib/validation-rules/string-format.validation-rule.spec.ts +47 -21
- package/src/lib/validation-rules/string-format.validation-rule.ts +14 -0
- package/src/lib/validation-rules/value-type.validation-rule.spec.ts +104 -74
- package/src/lib/validation-rules/value-type.validation-rule.ts +17 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
2
|
import { DecimalCountValidationRule } from "./decimal-count.validation-rule";
|
|
3
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
3
4
|
|
|
4
5
|
describe("DecimalCountValidationRule", () => {
|
|
5
6
|
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:decimal-count");
|
|
@@ -43,4 +44,29 @@ describe("DecimalCountValidationRule", () => {
|
|
|
43
44
|
});
|
|
44
45
|
});
|
|
45
46
|
});
|
|
47
|
+
|
|
48
|
+
describe("classifyUpdate", () => {
|
|
49
|
+
let rule: DecimalCountValidationRule;
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
rule = new DecimalCountValidationRule(rulePrn, {
|
|
53
|
+
max: 4,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it.each([
|
|
58
|
+
[ValidationRuleUpdateImpact.NONE, { max: 5 }], // increase max
|
|
59
|
+
[ValidationRuleUpdateImpact.NONE, { max: 4 }], // unchanged
|
|
60
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { max: 3 }], // tighten
|
|
61
|
+
])("returns %s when the new max is %s", (expected, newSettings) => {
|
|
62
|
+
expect(rule.classifyUpdate(newSettings)).toBe(expected);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("classifyCreate", () => {
|
|
67
|
+
it("returns INVALIDATES", () => {
|
|
68
|
+
const rule = new DecimalCountValidationRule(rulePrn, { max: 0 });
|
|
69
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.INVALIDATES);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
46
72
|
});
|
|
@@ -4,6 +4,7 @@ import { RuleInstance } from "../types/rule-instance.interface";
|
|
|
4
4
|
import { RuleType } from "../enums/rule-types.enum";
|
|
5
5
|
import { BaseValidationRule, ContentValidationResultType } from "./base.validation-rule";
|
|
6
6
|
import { validationFunctionFromValidateValue } from "./validation-rule-function.factory";
|
|
7
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
7
8
|
|
|
8
9
|
function getDecimalCount(value: number): number {
|
|
9
10
|
const [_, decimalDigits] = value.toString().split(".");
|
|
@@ -45,6 +46,17 @@ export class DecimalCountValidationRule extends BaseValidationRule<DecimalCountV
|
|
|
45
46
|
public override validate(values: ContentValueType[]): ContentValidationResultType[] {
|
|
46
47
|
return validateDecimalCount(values, this);
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
51
|
+
return ValidationRuleUpdateImpact.INVALIDATES;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public override classifyUpdate(newSettings: DecimalCountValidationRuleSettings): ValidationRuleUpdateImpact {
|
|
55
|
+
const oldMax = this.settings.max;
|
|
56
|
+
const newMax = newSettings.max;
|
|
57
|
+
|
|
58
|
+
return newMax >= oldMax ? ValidationRuleUpdateImpact.NONE : ValidationRuleUpdateImpact.INVALIDATES;
|
|
59
|
+
}
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
export interface DecimalCountValidationRuleSettings {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
2
|
import { InvalidValidationRuleSettingsError } from "../errors/invalid-validation-rule-settings.error";
|
|
3
3
|
import { NumberBetweenValidationRule } from "./number-between.validation-rule";
|
|
4
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
4
5
|
|
|
5
6
|
describe("NumberBetweenValidationRule", () => {
|
|
6
7
|
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:1");
|
|
@@ -74,4 +75,47 @@ describe("NumberBetweenValidationRule", () => {
|
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
77
|
});
|
|
78
|
+
|
|
79
|
+
describe("classifyUpdate", () => {
|
|
80
|
+
it.each([
|
|
81
|
+
// OLD: { min: 1, max: 5 }
|
|
82
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { min: 1, max: 6 }], // widen upper bound
|
|
83
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { min: 0, max: 5 }], // widen lower bound
|
|
84
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { min: 0, max: 6 }], // widen both bounds
|
|
85
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { min: 1 }], // remove upper bound (→ Infinity)
|
|
86
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { max: 5 }], // remove lower bound (→ -Infinity)
|
|
87
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: 1, max: 5 }, { min: 0, max: 4 }], // narrow upper bound
|
|
88
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: 1, max: 5 }, { min: 2, max: 5 }], // narrow lower bound
|
|
89
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: 1, max: 5 }, { min: 2, max: 4 }], // narrow both bounds
|
|
90
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1, max: 5 }, { min: 1, max: 5 }], // unchanged
|
|
91
|
+
|
|
92
|
+
// OLD: { min: 1 } (max = Infinity)
|
|
93
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1 }, { min: 0 }], // widen lower bound
|
|
94
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: 1 }, { min: 2 }], // narrow lower bound
|
|
95
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: 1 }, { min: 1, max: 5 }], // introduce upper bound (tightening)
|
|
96
|
+
[ValidationRuleUpdateImpact.NONE, { min: 1 }, {}], // remove lower bound (→ -Infinity)
|
|
97
|
+
|
|
98
|
+
// OLD: { max: 5 } (min = -Infinity)
|
|
99
|
+
[ValidationRuleUpdateImpact.NONE, { max: 5 }, { max: 6 }], // widen upper bound
|
|
100
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { max: 5 }, { max: 4 }], // narrow upper bound
|
|
101
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { max: 5 }, { min: 1, max: 5 }], // introduce lower bound (tightening)
|
|
102
|
+
[ValidationRuleUpdateImpact.NONE, { max: 5 }, {}], // remove upper bound (→ Infinity)
|
|
103
|
+
|
|
104
|
+
// OLD: spans negatives
|
|
105
|
+
[ValidationRuleUpdateImpact.NONE, { min: -5, max: 5 }, { min: -6, max: 5 }], // widen lower bound into negatives
|
|
106
|
+
[ValidationRuleUpdateImpact.NONE, { min: -5, max: 5 }, { min: -5, max: 6 }], // widen upper bound
|
|
107
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: -5, max: 5 }, { min: -4, max: 5 }], // tighten lower bound
|
|
108
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { min: -5, max: 5 }, { min: -5, max: 4 }], // tighten upper bound
|
|
109
|
+
])("returns %s when old settings are %s and new settings are %s", (expected, oldSettings, newSettings) => {
|
|
110
|
+
const rule = new NumberBetweenValidationRule(rulePrn, oldSettings);
|
|
111
|
+
expect(rule.classifyUpdate(newSettings)).toBe(expected);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("classifyCreate", () => {
|
|
116
|
+
it("returns INVALIDATES", () => {
|
|
117
|
+
const rule = new NumberBetweenValidationRule(rulePrn, { min: 1, max: 2 });
|
|
118
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.INVALIDATES);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
77
121
|
});
|
|
@@ -6,6 +6,7 @@ import { RuleInstance } from "../types/rule-instance.interface";
|
|
|
6
6
|
import { RuleType } from "../enums/rule-types.enum";
|
|
7
7
|
import { BaseValidationRule, ContentValidationResultType } from "./base.validation-rule";
|
|
8
8
|
import { validationFunctionFromValidateValue } from "./validation-rule-function.factory";
|
|
9
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
9
10
|
|
|
10
11
|
export function validateValueNumberBetween(
|
|
11
12
|
value: ContentValueType,
|
|
@@ -50,6 +51,19 @@ export class NumberBetweenValidationRule extends BaseValidationRule<NumberBetwee
|
|
|
50
51
|
public override validate(values: ContentValueType[]): ContentValidationResultType[] {
|
|
51
52
|
return validateNumberBetween(values, this);
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
56
|
+
return ValidationRuleUpdateImpact.INVALIDATES;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public override classifyUpdate(newSettings: NumberBetweenValidationRuleSettings): ValidationRuleUpdateImpact {
|
|
60
|
+
const oldSettings = this.settings;
|
|
61
|
+
const [oldMin, oldMax] = [oldSettings.min ?? 0, oldSettings.max ?? Infinity];
|
|
62
|
+
const [newMin, newMax] = [newSettings.min ?? 0, newSettings.max ?? Infinity];
|
|
63
|
+
const isExtending = newMin <= oldMin && newMax >= oldMax;
|
|
64
|
+
|
|
65
|
+
return isExtending ? ValidationRuleUpdateImpact.NONE : ValidationRuleUpdateImpact.INVALIDATES;
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
export interface NumberBetweenValidationRuleSettings {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
2
|
import { InvalidValidationArgumentsError } from "../errors/invalid-validation-arguments.error";
|
|
3
3
|
import { RelatableContentTypesValidationRule } from "./relatable-content-types.validation-rule";
|
|
4
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
4
5
|
|
|
5
6
|
describe("RelatableContentTypesValidationRule", () => {
|
|
6
7
|
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:1");
|
|
@@ -21,15 +22,39 @@ describe("RelatableContentTypesValidationRule", () => {
|
|
|
21
22
|
});
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
[
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
describe("validate", () => {
|
|
26
|
+
it.each([
|
|
27
|
+
[contentItemPrn, allowedContentTypes[0], true],
|
|
28
|
+
[contentItemPrn, disallowedContentType, false],
|
|
29
|
+
["not a ContentItem", disallowedContentType, false],
|
|
30
|
+
])("returns %s for the provided content item", (contentItem, contentType, expected) => {
|
|
31
|
+
expect(rule.validate([contentItem], [contentType])).toBeContentValidationResult([expected]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("throws an error if the value does not have a ContentType", () => {
|
|
35
|
+
expect(() => rule.validate([contentItemPrn])).toThrowError(InvalidValidationArgumentsError);
|
|
36
|
+
});
|
|
30
37
|
});
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
describe("classifyUpdate", () => {
|
|
40
|
+
it.each([
|
|
41
|
+
[
|
|
42
|
+
ValidationRuleUpdateImpact.NONE,
|
|
43
|
+
{ allowedContentTypes: [...allowedContentTypes, PRN.fromString("prn:plate:-1:cms:content-type:3")] },
|
|
44
|
+
], // extending
|
|
45
|
+
[ValidationRuleUpdateImpact.NONE, { allowedContentTypes: [...allowedContentTypes] }], // unchanged
|
|
46
|
+
[
|
|
47
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
48
|
+
{ allowedContentTypes: [PRN.fromString("prn:plate:-1:cms:content-type:3")] },
|
|
49
|
+
], // tightening
|
|
50
|
+
])("returns %s when the new allowed content types are %s", (expected, newSettings) => {
|
|
51
|
+
expect(rule.classifyUpdate(newSettings)).toBe(expected);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("classifyCreate", () => {
|
|
56
|
+
it("returns INVALIDATES", () => {
|
|
57
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.INVALIDATES);
|
|
58
|
+
});
|
|
34
59
|
});
|
|
35
60
|
});
|
|
@@ -5,6 +5,8 @@ import { ValidationErrorDetails } from "../errors/validation-error-details";
|
|
|
5
5
|
import { RuleInstance } from "../types/rule-instance.interface";
|
|
6
6
|
import { RuleType } from "../enums/rule-types.enum";
|
|
7
7
|
import { BaseValidationRule, ContentValidationResultType } from "./base.validation-rule";
|
|
8
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
9
|
+
import { isSuperset } from "../helpers/is-superset.helper";
|
|
8
10
|
|
|
9
11
|
function validateValueRelatableContentTypes<TContentType>(
|
|
10
12
|
value: ContentValueType,
|
|
@@ -54,6 +56,21 @@ export class RelatableContentTypesValidationRule extends BaseValidationRule<
|
|
|
54
56
|
|
|
55
57
|
return validateRelatableContentTypes(values, this, contentTypes);
|
|
56
58
|
}
|
|
59
|
+
|
|
60
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
61
|
+
return ValidationRuleUpdateImpact.INVALIDATES;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public override classifyUpdate(
|
|
65
|
+
newSettings: RelatableContentTypesValidationRuleSettings<PRN>,
|
|
66
|
+
): ValidationRuleUpdateImpact {
|
|
67
|
+
const oldAllowedContentTypes = this.settings.allowedContentTypes;
|
|
68
|
+
const newAllowedContentTypes = newSettings.allowedContentTypes;
|
|
69
|
+
|
|
70
|
+
return isSuperset(newAllowedContentTypes, oldAllowedContentTypes)
|
|
71
|
+
? ValidationRuleUpdateImpact.NONE
|
|
72
|
+
: ValidationRuleUpdateImpact.INVALIDATES;
|
|
73
|
+
}
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
export interface RelatableContentTypesValidationRuleSettings<TContentType> {
|
|
@@ -1,43 +1,69 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
2
3
|
import { StringFormatValidationRule } from "./string-format.validation-rule";
|
|
3
4
|
|
|
4
5
|
describe("StringFormatValidationRule", () => {
|
|
5
6
|
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:string-format");
|
|
6
7
|
|
|
7
|
-
describe("
|
|
8
|
-
|
|
8
|
+
describe("validate", () => {
|
|
9
|
+
describe("with an email regex", () => {
|
|
10
|
+
let rule: StringFormatValidationRule;
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
rule = new StringFormatValidationRule(rulePrn, {
|
|
14
|
+
allowedFormat: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$",
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it.each([
|
|
19
|
+
["david@getplate.com", true],
|
|
20
|
+
["david@getplate", false],
|
|
21
|
+
[123, null],
|
|
22
|
+
])("returns %s for the provided value", (value, expected) => {
|
|
23
|
+
expect(rule.validate([value])).toBeContentValidationResult([expected]);
|
|
13
24
|
});
|
|
14
25
|
});
|
|
15
26
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
describe("with a just letters regex", () => {
|
|
28
|
+
let rule: StringFormatValidationRule;
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
rule = new StringFormatValidationRule(rulePrn, {
|
|
32
|
+
allowedFormat: "^[a-zA-Z]+$",
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it.each([
|
|
37
|
+
["abc", true],
|
|
38
|
+
["abc123", false],
|
|
39
|
+
["", false],
|
|
40
|
+
[123, null],
|
|
41
|
+
])("returns %s for the provided value", (value, expected) => {
|
|
42
|
+
expect(rule.validate([value])).toBeContentValidationResult([expected]);
|
|
43
|
+
});
|
|
22
44
|
});
|
|
23
45
|
});
|
|
24
46
|
|
|
25
|
-
describe("
|
|
47
|
+
describe("classifyUpdate", () => {
|
|
26
48
|
let rule: StringFormatValidationRule;
|
|
49
|
+
const allowedFormat = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";
|
|
27
50
|
|
|
28
51
|
beforeEach(() => {
|
|
29
|
-
rule = new StringFormatValidationRule(rulePrn, {
|
|
30
|
-
allowedFormat: "^[a-zA-Z]+$",
|
|
31
|
-
});
|
|
52
|
+
rule = new StringFormatValidationRule(rulePrn, { allowedFormat });
|
|
32
53
|
});
|
|
33
54
|
|
|
34
55
|
it.each([
|
|
35
|
-
[
|
|
36
|
-
["
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
[ValidationRuleUpdateImpact.NONE, { allowedFormat }], // unchanged
|
|
57
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { allowedFormat: "^[a-zA-Z]+$" }], // changing format
|
|
58
|
+
])("returns %s when the new allowed format is %s", (expected, newSettings) => {
|
|
59
|
+
expect(rule.classifyUpdate(newSettings)).toBe(expected);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("classifyCreate", () => {
|
|
64
|
+
it("returns INVALIDATES", () => {
|
|
65
|
+
const rule = new StringFormatValidationRule(rulePrn, { allowedFormat: "^[a-zA-Z]+$" });
|
|
66
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.INVALIDATES);
|
|
41
67
|
});
|
|
42
68
|
});
|
|
43
69
|
});
|
|
@@ -4,6 +4,7 @@ import { RuleInstance } from "../types/rule-instance.interface";
|
|
|
4
4
|
import { RuleType } from "../enums/rule-types.enum";
|
|
5
5
|
import { BaseValidationRule, ContentValidationResultType } from "./base.validation-rule";
|
|
6
6
|
import { validationFunctionFromValidateValue } from "./validation-rule-function.factory";
|
|
7
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
7
8
|
|
|
8
9
|
export function validateValueStringFormat(
|
|
9
10
|
value: ContentValueType,
|
|
@@ -43,6 +44,19 @@ export class StringFormatValidationRule extends BaseValidationRule<StringFormatV
|
|
|
43
44
|
public override validate(values: ContentValueType[]): ContentValidationResultType[] {
|
|
44
45
|
return validateStringFormat(values, this);
|
|
45
46
|
}
|
|
47
|
+
|
|
48
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
49
|
+
return ValidationRuleUpdateImpact.INVALIDATES;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public override classifyUpdate(newSettings: StringFormatValidationRuleSettings): ValidationRuleUpdateImpact {
|
|
53
|
+
const oldAllowedFormat = this.settings.allowedFormat;
|
|
54
|
+
const newAllowedFormat = newSettings.allowedFormat;
|
|
55
|
+
|
|
56
|
+
return oldAllowedFormat === newAllowedFormat
|
|
57
|
+
? ValidationRuleUpdateImpact.NONE
|
|
58
|
+
: ValidationRuleUpdateImpact.INVALIDATES;
|
|
59
|
+
}
|
|
46
60
|
}
|
|
47
61
|
|
|
48
62
|
export interface StringFormatValidationRuleSettings {
|
|
@@ -1,108 +1,138 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
|
-
import { ContentValueType, ContentValueTypeNames } from "@platecms/delta-types";
|
|
2
|
+
import { ContentValueType, ContentValueTypeName, ContentValueTypeNames } from "@platecms/delta-types";
|
|
3
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
3
4
|
import { ValueTypeValidationRule, ValueTypeValidationRuleSettings } from "./value-type.validation-rule";
|
|
4
5
|
|
|
5
6
|
describe("ValueTypeValidationRule", () => {
|
|
6
7
|
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:value-type");
|
|
7
8
|
|
|
8
|
-
describe
|
|
9
|
-
[
|
|
10
|
-
"with allowed string type",
|
|
11
|
-
{ allowedTypes: [ContentValueTypeNames.STRING] },
|
|
9
|
+
describe("validate", () => {
|
|
10
|
+
describe.each([
|
|
12
11
|
[
|
|
13
|
-
|
|
14
|
-
[
|
|
12
|
+
"with allowed string type",
|
|
13
|
+
{ allowedTypes: [ContentValueTypeNames.STRING] },
|
|
14
|
+
[
|
|
15
|
+
["foo", true],
|
|
16
|
+
[123, false],
|
|
17
|
+
],
|
|
15
18
|
],
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"with allowed number type",
|
|
19
|
-
{ allowedTypes: [ContentValueTypeNames.NUMBER] },
|
|
20
19
|
[
|
|
21
|
-
|
|
22
|
-
[
|
|
20
|
+
"with allowed number type",
|
|
21
|
+
{ allowedTypes: [ContentValueTypeNames.NUMBER] },
|
|
22
|
+
[
|
|
23
|
+
[123, true],
|
|
24
|
+
["foo", false],
|
|
25
|
+
],
|
|
23
26
|
],
|
|
24
|
-
],
|
|
25
|
-
[
|
|
26
|
-
"with allowed boolean type",
|
|
27
|
-
{ allowedTypes: [ContentValueTypeNames.BOOLEAN] },
|
|
28
27
|
[
|
|
29
|
-
|
|
30
|
-
[
|
|
28
|
+
"with allowed boolean type",
|
|
29
|
+
{ allowedTypes: [ContentValueTypeNames.BOOLEAN] },
|
|
30
|
+
[
|
|
31
|
+
[true, true],
|
|
32
|
+
["foo", false],
|
|
33
|
+
],
|
|
31
34
|
],
|
|
32
|
-
],
|
|
33
|
-
[
|
|
34
|
-
"with allowed date type",
|
|
35
|
-
{ allowedTypes: [ContentValueTypeNames.DATE] },
|
|
36
35
|
[
|
|
37
|
-
|
|
38
|
-
[
|
|
36
|
+
"with allowed date type",
|
|
37
|
+
{ allowedTypes: [ContentValueTypeNames.DATE] },
|
|
38
|
+
[
|
|
39
|
+
[new Date(), true],
|
|
40
|
+
["foo", false],
|
|
41
|
+
],
|
|
39
42
|
],
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
"with allowed content item type",
|
|
43
|
-
{ allowedTypes: [ContentValueTypeNames.CONTENT_ITEM] },
|
|
44
43
|
[
|
|
45
|
-
|
|
46
|
-
[
|
|
44
|
+
"with allowed content item type",
|
|
45
|
+
{ allowedTypes: [ContentValueTypeNames.CONTENT_ITEM] },
|
|
46
|
+
[
|
|
47
|
+
[PRN.fromString("prn:plate:-1:cms:content-item:1"), true],
|
|
48
|
+
["foo", false],
|
|
49
|
+
],
|
|
47
50
|
],
|
|
48
|
-
],
|
|
49
|
-
[
|
|
50
|
-
"with allowed asset type",
|
|
51
|
-
{ allowedTypes: [ContentValueTypeNames.ASSET] },
|
|
52
51
|
[
|
|
53
|
-
|
|
54
|
-
[
|
|
52
|
+
"with allowed asset type",
|
|
53
|
+
{ allowedTypes: [ContentValueTypeNames.ASSET] },
|
|
54
|
+
[
|
|
55
|
+
[PRN.fromString("prn:plate:-1:ps:asset:1"), true],
|
|
56
|
+
["foo", false],
|
|
57
|
+
],
|
|
55
58
|
],
|
|
56
|
-
],
|
|
57
|
-
[
|
|
58
|
-
"with allowed path part type",
|
|
59
|
-
{ allowedTypes: [ContentValueTypeNames.PATH_PART] },
|
|
60
59
|
[
|
|
61
|
-
|
|
62
|
-
[
|
|
60
|
+
"with allowed path part type",
|
|
61
|
+
{ allowedTypes: [ContentValueTypeNames.PATH_PART] },
|
|
62
|
+
[
|
|
63
|
+
[PRN.fromString("prn:plate:-1:cms:path-part:1"), true],
|
|
64
|
+
["foo", false],
|
|
65
|
+
],
|
|
63
66
|
],
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"with allowed grid placement type",
|
|
67
|
-
{ allowedTypes: [ContentValueTypeNames.GRID_PLACEMENT] },
|
|
68
67
|
[
|
|
69
|
-
|
|
70
|
-
[
|
|
68
|
+
"with allowed grid placement type",
|
|
69
|
+
{ allowedTypes: [ContentValueTypeNames.GRID_PLACEMENT] },
|
|
70
|
+
[
|
|
71
|
+
[PRN.fromString("prn:plate:-1:cms:grid-placement:1"), true],
|
|
72
|
+
["foo", false],
|
|
73
|
+
],
|
|
71
74
|
],
|
|
72
|
-
],
|
|
73
|
-
[
|
|
74
|
-
"with allowed smart text type",
|
|
75
|
-
{ allowedTypes: [ContentValueTypeNames.SMART_TEXT] },
|
|
76
75
|
[
|
|
77
|
-
|
|
78
|
-
[
|
|
76
|
+
"with allowed smart text type",
|
|
77
|
+
{ allowedTypes: [ContentValueTypeNames.SMART_TEXT] },
|
|
78
|
+
[
|
|
79
|
+
[{ type: "root", children: [] }, true],
|
|
80
|
+
[PRN.fromString("prn:plate:-1:cms:grid-placement:1"), false],
|
|
81
|
+
],
|
|
79
82
|
],
|
|
80
|
-
],
|
|
81
|
-
[
|
|
82
|
-
"with incorrect prn",
|
|
83
|
-
{ allowedTypes: [ContentValueTypeNames.CONTENT_ITEM] },
|
|
84
|
-
[[PRN.fromString("prn:plate:-1:ps:asset:1"), false]],
|
|
85
|
-
],
|
|
86
|
-
[
|
|
87
|
-
"with multiple types",
|
|
88
|
-
{
|
|
89
|
-
allowedTypes: [ContentValueTypeNames.STRING, ContentValueTypeNames.CONTENT_ITEM],
|
|
90
|
-
},
|
|
91
83
|
[
|
|
92
|
-
|
|
93
|
-
[
|
|
94
|
-
[
|
|
84
|
+
"with incorrect prn",
|
|
85
|
+
{ allowedTypes: [ContentValueTypeNames.CONTENT_ITEM] },
|
|
86
|
+
[[PRN.fromString("prn:plate:-1:ps:asset:1"), false]],
|
|
95
87
|
],
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
[
|
|
89
|
+
"with multiple types",
|
|
90
|
+
{
|
|
91
|
+
allowedTypes: [ContentValueTypeNames.STRING, ContentValueTypeNames.CONTENT_ITEM],
|
|
92
|
+
},
|
|
93
|
+
[
|
|
94
|
+
["foo", true],
|
|
95
|
+
[PRN.fromString("prn:plate:-1:cms:content-item:1"), true],
|
|
96
|
+
[new Date(), false],
|
|
97
|
+
],
|
|
98
|
+
],
|
|
99
|
+
])("%s", (_, ruleConfig, testCases: unknown[]) => {
|
|
100
|
+
let rule: ValueTypeValidationRule;
|
|
101
|
+
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
rule = new ValueTypeValidationRule(rulePrn, ruleConfig as ValueTypeValidationRuleSettings);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it.each(testCases)("returns %s for the provided value", (value, expected) => {
|
|
107
|
+
expect(rule.validate([value as ContentValueType])).toBeContentValidationResult([expected as boolean]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("classifyUpdate", () => {
|
|
98
113
|
let rule: ValueTypeValidationRule;
|
|
114
|
+
const allowedTypes: ContentValueTypeName<ContentValueType>[] = [
|
|
115
|
+
ContentValueTypeNames.STRING,
|
|
116
|
+
ContentValueTypeNames.NUMBER,
|
|
117
|
+
];
|
|
99
118
|
|
|
100
119
|
beforeEach(() => {
|
|
101
|
-
rule = new ValueTypeValidationRule(rulePrn,
|
|
120
|
+
rule = new ValueTypeValidationRule(rulePrn, { allowedTypes });
|
|
102
121
|
});
|
|
103
122
|
|
|
104
|
-
it.each(
|
|
105
|
-
|
|
123
|
+
it.each([
|
|
124
|
+
[ValidationRuleUpdateImpact.NONE, { allowedTypes: [...allowedTypes, ContentValueTypeNames.BOOLEAN] }], // extending
|
|
125
|
+
[ValidationRuleUpdateImpact.NONE, { allowedTypes: [...allowedTypes] }], // unchanged
|
|
126
|
+
[ValidationRuleUpdateImpact.BREAKS, { allowedTypes: [ContentValueTypeNames.BOOLEAN] }], // tightening
|
|
127
|
+
])("returns %s when the new allowed types are %s", (expected, newSettings) => {
|
|
128
|
+
expect(rule.classifyUpdate(newSettings as ValueTypeValidationRuleSettings)).toBe(expected);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("classifyCreate", () => {
|
|
133
|
+
it("returns BREAKS", () => {
|
|
134
|
+
const rule = new ValueTypeValidationRule(rulePrn, { allowedTypes: [ContentValueTypeNames.STRING] });
|
|
135
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.BREAKS);
|
|
106
136
|
});
|
|
107
137
|
});
|
|
108
138
|
});
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { InvalidCastError, validateCast } from "@platecms/delta-cast";
|
|
2
2
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
3
3
|
import { ContentValueType, ContentValueTypeName, ContentValueTypeNames } from "@platecms/delta-types";
|
|
4
|
+
import { RuleType } from "../enums/rule-types.enum";
|
|
5
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
4
6
|
import { ValidationErrorDetails } from "../errors/validation-error-details";
|
|
7
|
+
import { isSuperset } from "../helpers/is-superset.helper";
|
|
5
8
|
import { RuleInstance } from "../types/rule-instance.interface";
|
|
6
|
-
import { RuleType } from "../enums/rule-types.enum";
|
|
7
9
|
import { BaseValidationRule, ContentValidationResultType } from "./base.validation-rule";
|
|
8
10
|
import { validationFunctionFromValidateValue } from "./validation-rule-function.factory";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Mapping of PRN resource type to Content Value Type Name.
|
|
12
14
|
*/
|
|
13
|
-
const prnResourceTypeToContentValueTypeNameMap: Record<string,
|
|
15
|
+
const prnResourceTypeToContentValueTypeNameMap: Record<string, ContentValueTypeName<PRN>> = {
|
|
14
16
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
15
17
|
"content-item": ContentValueTypeNames.CONTENT_ITEM,
|
|
16
18
|
asset: ContentValueTypeNames.ASSET,
|
|
@@ -68,6 +70,19 @@ export class ValueTypeValidationRule extends BaseValidationRule<ValueTypeValidat
|
|
|
68
70
|
public override validate(values: ContentValueType[]): ContentValidationResultType[] {
|
|
69
71
|
return validateValueType(values, this);
|
|
70
72
|
}
|
|
73
|
+
|
|
74
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
75
|
+
return ValidationRuleUpdateImpact.BREAKS;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public override classifyUpdate(newSettings: ValueTypeValidationRuleSettings): ValidationRuleUpdateImpact {
|
|
79
|
+
const oldAllowedTypes = this.settings.allowedTypes;
|
|
80
|
+
const newAllowedTypes = newSettings.allowedTypes;
|
|
81
|
+
|
|
82
|
+
return isSuperset(newAllowedTypes, oldAllowedTypes)
|
|
83
|
+
? ValidationRuleUpdateImpact.NONE
|
|
84
|
+
: ValidationRuleUpdateImpact.BREAKS;
|
|
85
|
+
}
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
// The value type can be one or more of the types specified in the ContentValueType type
|