@platecms/delta-validation 0.13.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/rule-to-schema.spec.ts +4 -4
- package/src/lib/helpers/schema-builder.ts +5 -0
- package/src/lib/helpers/validate-with-function.spec.ts +3 -3
- package/src/lib/schemas/array.schema.spec.ts +1 -1
- package/src/lib/schemas/content-value.schema.spec.ts +5 -5
- package/src/lib/schemas/content-value.schema.ts +27 -2
- package/src/lib/schemas/date.schema.spec.ts +1 -1
- package/src/lib/schemas/object.schema.spec.ts +223 -0
- package/src/lib/schemas/object.schema.ts +106 -0
- package/src/lib/schemas/string.schema.spec.ts +1 -1
- package/src/lib/types/validation-schema.interface.ts +1 -1
- package/src/lib/validation-rules/allowed-values.validation-rule.spec.ts +30 -5
- 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 +39 -1
- package/src/lib/validation-rules/count.validation-rule.ts +35 -0
- package/src/lib/validation-rules/date-between.validation-rule.spec.ts +84 -1
- package/src/lib/validation-rules/date-between.validation-rule.ts +19 -4
- package/src/lib/validation-rules/decimal-count.validation-rule.spec.ts +27 -1
- package/src/lib/validation-rules/decimal-count.validation-rule.ts +12 -0
- package/src/lib/validation-rules/number-between.validation-rule.spec.ts +45 -1
- package/src/lib/validation-rules/number-between.validation-rule.ts +14 -0
- package/src/lib/validation-rules/relatable-content-types.validation-rule.spec.ts +38 -13
- package/src/lib/validation-rules/relatable-content-types.validation-rule.ts +17 -0
- package/src/lib/validation-rules/string-format.validation-rule.spec.ts +48 -22
- package/src/lib/validation-rules/string-format.validation-rule.ts +14 -0
- package/src/lib/validation-rules/value-type.validation-rule.spec.ts +105 -75
- package/src/lib/validation-rules/value-type.validation-rule.ts +17 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { PRN } from "@platecms/delta-plate-resource-notation";
|
|
2
|
+
import { ValidationRuleUpdateImpact } from "../enums/validation-rule-update-impact.enum";
|
|
2
3
|
import { InvalidValidationRuleSettingsError } from "../errors/invalid-validation-rule-settings.error";
|
|
3
4
|
import { DateBetweenValidationRule } from "./date-between.validation-rule";
|
|
4
5
|
|
|
5
6
|
describe("DateBetweenValidationRule", () => {
|
|
6
|
-
const rulePrn = PRN.fromString("prn:plate:-1:
|
|
7
|
+
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:1");
|
|
7
8
|
|
|
8
9
|
describe("constructor", () => {
|
|
9
10
|
it("throws an error if min and max are not provided", () => {
|
|
@@ -74,4 +75,86 @@ describe("DateBetweenValidationRule", () => {
|
|
|
74
75
|
});
|
|
75
76
|
});
|
|
76
77
|
});
|
|
78
|
+
|
|
79
|
+
describe("classifyUpdate", () => {
|
|
80
|
+
// eslint-disable-next-line func-style, id-length
|
|
81
|
+
const d = (iso: string): Date => new Date(iso);
|
|
82
|
+
|
|
83
|
+
it.each([
|
|
84
|
+
// OLD: bounded range
|
|
85
|
+
[
|
|
86
|
+
ValidationRuleUpdateImpact.NONE,
|
|
87
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
88
|
+
{ start: d("2025-01-10"), end: d("2025-01-21") },
|
|
89
|
+
], // widen end
|
|
90
|
+
[
|
|
91
|
+
ValidationRuleUpdateImpact.NONE,
|
|
92
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
93
|
+
{ start: d("2025-01-09"), end: d("2025-01-20") },
|
|
94
|
+
], // widen start (earlier)
|
|
95
|
+
[
|
|
96
|
+
ValidationRuleUpdateImpact.NONE,
|
|
97
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
98
|
+
{ start: d("2025-01-09"), end: d("2025-01-21") },
|
|
99
|
+
], // widen both
|
|
100
|
+
|
|
101
|
+
[ValidationRuleUpdateImpact.NONE, { start: d("2025-01-10"), end: d("2025-01-20") }, { start: d("2025-01-10") }], // remove end (→ Infinity)
|
|
102
|
+
[ValidationRuleUpdateImpact.NONE, { start: d("2025-01-10"), end: d("2025-01-20") }, { end: d("2025-01-20") }], // remove start (→ -Infinity)
|
|
103
|
+
[ValidationRuleUpdateImpact.NONE, { start: d("2025-01-10"), end: d("2025-01-20") }, {}], // remove both
|
|
104
|
+
[
|
|
105
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
106
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
107
|
+
{ start: d("2025-01-10"), end: d("2025-01-19") },
|
|
108
|
+
], // narrow end (earlier)
|
|
109
|
+
[
|
|
110
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
111
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
112
|
+
{ start: d("2025-01-11"), end: d("2025-01-20") },
|
|
113
|
+
], // narrow start (later)
|
|
114
|
+
[
|
|
115
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
116
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
117
|
+
{ start: d("2025-01-11"), end: d("2025-01-19") },
|
|
118
|
+
], // narrow both
|
|
119
|
+
// unchanged
|
|
120
|
+
[
|
|
121
|
+
ValidationRuleUpdateImpact.NONE,
|
|
122
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
123
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
124
|
+
], // unchanged
|
|
125
|
+
|
|
126
|
+
// OLD: start-only (end = Infinity)
|
|
127
|
+
[ValidationRuleUpdateImpact.NONE, { start: d("2025-01-10") }, { start: d("2025-01-09") }], // widen start (earlier)
|
|
128
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { start: d("2025-01-10") }, { start: d("2025-01-11") }], // narrow start (later)
|
|
129
|
+
[
|
|
130
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
131
|
+
{ start: d("2025-01-10") },
|
|
132
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
133
|
+
], // introduce end (tightening)
|
|
134
|
+
[ValidationRuleUpdateImpact.NONE, { start: d("2025-01-10") }, {}], // remove start (→ -Infinity)
|
|
135
|
+
|
|
136
|
+
// OLD: end-only (start = -Infinity)
|
|
137
|
+
[ValidationRuleUpdateImpact.NONE, { end: d("2025-01-20") }, { end: d("2025-01-21") }], // widen end (later)
|
|
138
|
+
[ValidationRuleUpdateImpact.INVALIDATES, { end: d("2025-01-20") }, { end: d("2025-01-19") }], // narrow end (earlier)
|
|
139
|
+
[
|
|
140
|
+
ValidationRuleUpdateImpact.INVALIDATES,
|
|
141
|
+
{ end: d("2025-01-20") },
|
|
142
|
+
{ start: d("2025-01-10"), end: d("2025-01-20") },
|
|
143
|
+
], // introduce start (tightening)
|
|
144
|
+
[ValidationRuleUpdateImpact.NONE, { end: d("2025-01-20") }, {}], // remove end (→ Infinity)
|
|
145
|
+
])("returns %s when old settings are %s and new settings are %s", (expected, oldSettings, newSettings) => {
|
|
146
|
+
const rule = new DateBetweenValidationRule(rulePrn, oldSettings);
|
|
147
|
+
expect(rule.classifyUpdate(newSettings)).toBe(expected);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe("classifyCreate", () => {
|
|
152
|
+
it("returns INVALIDATES", () => {
|
|
153
|
+
const rule = new DateBetweenValidationRule(rulePrn, {
|
|
154
|
+
start: new Date(2020, 1, 2, 3, 4, 5),
|
|
155
|
+
end: new Date(2020, 1, 3, 3, 4, 6),
|
|
156
|
+
});
|
|
157
|
+
expect(rule.classifyCreate()).toBe(ValidationRuleUpdateImpact.INVALIDATES);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
77
160
|
});
|
|
@@ -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 validateValueDateBetween(
|
|
11
12
|
value: ContentValueType,
|
|
@@ -15,15 +16,15 @@ export function validateValueDateBetween(
|
|
|
15
16
|
return null;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
if (ruleInstance.settings.start != null && value < ruleInstance.settings.start) {
|
|
19
|
+
if (ruleInstance.settings.start != null && value < new Date(ruleInstance.settings.start as unknown as Date)) {
|
|
19
20
|
return new ValidationErrorDetails(
|
|
20
|
-
`The date must be greater than or equal ${ruleInstance.settings.start.toDateString()}`,
|
|
21
|
+
`The date must be greater than or equal ${new Date(ruleInstance.settings.start).toDateString()}`,
|
|
21
22
|
ruleInstance,
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
|
-
if (ruleInstance.settings.end != null && value > ruleInstance.settings.end) {
|
|
25
|
+
if (ruleInstance.settings.end != null && value > new Date(ruleInstance.settings.end)) {
|
|
25
26
|
return new ValidationErrorDetails(
|
|
26
|
-
`The date must be lower than or equal ${ruleInstance.settings.end.toDateString()}`,
|
|
27
|
+
`The date must be lower than or equal ${new Date(ruleInstance.settings.end).toDateString()}`,
|
|
27
28
|
ruleInstance,
|
|
28
29
|
);
|
|
29
30
|
}
|
|
@@ -46,6 +47,20 @@ export class DateBetweenValidationRule extends BaseValidationRule<DateBetweenVal
|
|
|
46
47
|
public override validate(values: ContentValueType[]): ContentValidationResultType[] {
|
|
47
48
|
return validateDateBetween(values, this);
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
public override classifyCreate(): ValidationRuleUpdateImpact {
|
|
52
|
+
return ValidationRuleUpdateImpact.INVALIDATES;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public override classifyUpdate(newSettings: DateBetweenValidationRuleSettings): ValidationRuleUpdateImpact {
|
|
56
|
+
const oldStart = this.settings.start ? this.settings.start.getTime() : -Infinity;
|
|
57
|
+
const oldEnd = this.settings.end ? this.settings.end.getTime() : Infinity;
|
|
58
|
+
const newStart = newSettings.start ? newSettings.start.getTime() : -Infinity;
|
|
59
|
+
const newEnd = newSettings.end ? newSettings.end.getTime() : Infinity;
|
|
60
|
+
const extending = newStart <= oldStart && newEnd >= oldEnd;
|
|
61
|
+
|
|
62
|
+
return extending ? ValidationRuleUpdateImpact.NONE : ValidationRuleUpdateImpact.INVALIDATES;
|
|
63
|
+
}
|
|
49
64
|
}
|
|
50
65
|
|
|
51
66
|
export interface DateBetweenValidationRuleSettings {
|
|
@@ -1,8 +1,9 @@
|
|
|
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
|
-
const rulePrn = PRN.fromString("prn:plate:-1:
|
|
6
|
+
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:decimal-count");
|
|
6
7
|
|
|
7
8
|
describe("validate", () => {
|
|
8
9
|
describe("with max of 0", () => {
|
|
@@ -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,9 +1,10 @@
|
|
|
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
|
-
const rulePrn = PRN.fromString("prn:plate:-1:
|
|
7
|
+
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:1");
|
|
7
8
|
|
|
8
9
|
describe("constructor", () => {
|
|
9
10
|
it("throws an error if min and max are not provided", () => {
|
|
@@ -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,17 +1,18 @@
|
|
|
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
|
-
const rulePrn = PRN.fromString("prn:plate:-1:
|
|
7
|
-
const contentItemPrn = PRN.fromString("prn:plate:-1:
|
|
7
|
+
const rulePrn = PRN.fromString("prn:plate:-1:cms:validation-rule:1");
|
|
8
|
+
const contentItemPrn = PRN.fromString("prn:plate:-1:cms:content-item:1");
|
|
8
9
|
|
|
9
10
|
const allowedContentTypes = [
|
|
10
|
-
PRN.fromString("prn:plate:-1:
|
|
11
|
-
PRN.fromString("prn:plate:-1:
|
|
11
|
+
PRN.fromString("prn:plate:-1:cms:content-type:1"),
|
|
12
|
+
PRN.fromString("prn:plate:-1:cms:content-type:2"),
|
|
12
13
|
];
|
|
13
14
|
|
|
14
|
-
const disallowedContentType = PRN.fromString("prn:plate:-1:
|
|
15
|
+
const disallowedContentType = PRN.fromString("prn:plate:-1:cms:content-type:3");
|
|
15
16
|
|
|
16
17
|
let rule: RelatableContentTypesValidationRule;
|
|
17
18
|
|
|
@@ -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
|
-
const rulePrn = PRN.fromString("prn:plate:-1:
|
|
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 {
|