Skip to content

Commit fdc83bc

Browse files
adamthom-amznsrchase
authored andcommitted
Add validator implementations
These validators are referenced from generated code, and implement both constraint traits as well as the recursive validation of traits for more complex shapes.
1 parent 2de1298 commit fdc83bc

File tree

6 files changed

+558
-1
lines changed

6 files changed

+558
-1
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,8 @@ wrapper/
2424

2525
# Visual Studio Code
2626
.vscode/*
27+
28+
# Integ test Yarn stuff
29+
smithy-typescript-integ-tests/dist
30+
smithy-typescript-integ-tests/node_modules
31+
smithy-typescript-integ-tests/yarn.lock

smithy-typescript-ssdk-libs/server-common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
export * as httpbinding from "./httpbinding";
1717
export * from "./errors";
18+
export * from "./validation";
1819

1920
import { HttpRequest, HttpResponse } from "@aws-sdk/protocol-http";
2021
import { SmithyException } from "@aws-sdk/smithy-client";
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
export * from "./validators";
17+
18+
interface StandardValidationFailure<ConstraintBoundsType, FailureType> {
19+
memberName: string;
20+
constraintType: string;
21+
constraintValues: ArrayLike<ConstraintBoundsType>;
22+
failureValue: FailureType;
23+
}
24+
25+
export interface EnumValidationFailure extends StandardValidationFailure<string, string> {
26+
constraintType: "enum";
27+
constraintValues: string[];
28+
}
29+
30+
export interface LengthValidationFailure extends StandardValidationFailure<number | undefined, number> {
31+
constraintType: "length";
32+
constraintValues: [number, number] | [undefined, number] | [number, undefined];
33+
}
34+
35+
export interface PatternValidationFailure {
36+
memberName: string;
37+
constraintType: "pattern";
38+
constraintValues: string;
39+
failureValue: string;
40+
}
41+
42+
export interface RangeValidationFailure extends StandardValidationFailure<number | undefined, number> {
43+
constraintType: "range";
44+
constraintValues: [number, number] | [undefined, number] | [number, undefined];
45+
}
46+
47+
export class RequiredValidationFailure {
48+
memberName: string;
49+
constraintType = "required";
50+
51+
constructor(memberName: string) {
52+
this.memberName = memberName;
53+
}
54+
}
55+
56+
export interface UniqueItemsValidationFailure {
57+
memberName: string;
58+
constraintType: "uniqueItems";
59+
failureValue: Array<any>;
60+
}
61+
62+
export type ValidationFailure =
63+
| EnumValidationFailure
64+
| LengthValidationFailure
65+
| PatternValidationFailure
66+
| RangeValidationFailure
67+
| RequiredValidationFailure
68+
| UniqueItemsValidationFailure;
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
import { EnumValidator, LengthValidator, PatternValidator, RangeValidator, UniqueItemsValidator } from "./validators";
17+
18+
describe("enum validation", () => {
19+
const enumValidator = new EnumValidator(["apple", "banana", "orange"]);
20+
21+
it("does not fail when the enum value is found", () => {
22+
expect(enumValidator.validate("apple", "fruit")).toBeNull();
23+
});
24+
25+
it("fails when the enum value is not found", () => {
26+
expect(enumValidator.validate("kiwi", "fruit")).toEqual({
27+
constraintType: "enum",
28+
constraintValues: ["apple", "banana", "orange"],
29+
memberName: "fruit",
30+
failureValue: "kiwi",
31+
});
32+
});
33+
});
34+
35+
describe("length validation", () => {
36+
const threeLengthThings = ["foo", { a: 1, b: 2, c: 3 }, ["a", "b", "c"], new Uint8Array([13, 37, 42])];
37+
38+
for (const value of threeLengthThings) {
39+
describe(`for ${JSON.stringify(value)}`, () => {
40+
it("should succeed with min = 1", () => {
41+
expect(new LengthValidator(1).validate(value, "aThreeLengthThing")).toBeNull();
42+
});
43+
it("should succeed with min = 3", () => {
44+
expect(new LengthValidator(3).validate(value, "aThreeLengthThing")).toBeNull();
45+
});
46+
it("should succeed with max = 100", () => {
47+
expect(new LengthValidator(undefined, 100).validate(value, "aThreeLengthThing")).toBeNull();
48+
});
49+
it("should succeed with max = 3", () => {
50+
expect(new LengthValidator(undefined, 3).validate(value, "aThreeLengthThing")).toBeNull();
51+
});
52+
it("should succeed with min = 3 and max = 3", () => {
53+
expect(new LengthValidator(3, 3).validate(value, "aThreeLengthThing")).toBeNull();
54+
});
55+
it("should succeed with min = 1 and max = 128", () => {
56+
expect(new LengthValidator(1, 128).validate(value, "aThreeLengthThing")).toBeNull();
57+
});
58+
it("should fail with min = 4", () => {
59+
expect(new LengthValidator(4).validate(value, "aThreeLengthThing")).toEqual({
60+
constraintType: "length",
61+
constraintValues: [4, undefined],
62+
memberName: "aThreeLengthThing",
63+
failureValue: 3,
64+
});
65+
});
66+
it("should fail with max = 2", () => {
67+
expect(new LengthValidator(undefined, 2).validate(value, "aThreeLengthThing")).toEqual({
68+
constraintType: "length",
69+
constraintValues: [undefined, 2],
70+
memberName: "aThreeLengthThing",
71+
failureValue: 3,
72+
});
73+
});
74+
it("should fail with min = 1 and max = 2", () => {
75+
expect(new LengthValidator(1, 2).validate(value, "aThreeLengthThing")).toEqual({
76+
constraintType: "length",
77+
constraintValues: [1, 2],
78+
memberName: "aThreeLengthThing",
79+
failureValue: 3,
80+
});
81+
});
82+
});
83+
}
84+
});
85+
86+
describe("pattern validation", () => {
87+
it("does not match the entire string", () => {
88+
const validator = new PatternValidator("\\w+");
89+
expect(validator.validate("hello", "aField")).toBeNull();
90+
expect(validator.validate("!hello!", "aField")).toBeNull();
91+
});
92+
it("can be anchored", () => {
93+
const validator = new PatternValidator("^\\w+$");
94+
expect(validator.validate("hello", "aField")).toBeNull();
95+
expect(validator.validate("!hello!", "aField")).toEqual({
96+
constraintType: "pattern",
97+
constraintValues: "^\\w+$",
98+
failureValue: "!hello!",
99+
memberName: "aField",
100+
});
101+
});
102+
it("supports character class expressions", () => {
103+
const validator = new PatternValidator("^\\p{L}+$");
104+
expect(validator.validate("hello", "aField")).toBeNull();
105+
expect(validator.validate("!hello!", "aField")).toEqual({
106+
constraintType: "pattern",
107+
constraintValues: "^\\p{L}+$",
108+
failureValue: "!hello!",
109+
memberName: "aField",
110+
});
111+
});
112+
});
113+
114+
describe("range validation", () => {
115+
it("supports min-only constraints", () => {
116+
const validator = new RangeValidator(3);
117+
expect(validator.validate(3, "aField")).toBeNull();
118+
expect(validator.validate(4, "aField")).toBeNull();
119+
expect(validator.validate(1, "aField")).toEqual({
120+
constraintType: "range",
121+
constraintValues: [3, undefined],
122+
failureValue: 1,
123+
memberName: "aField",
124+
});
125+
});
126+
it("supports max-only constraints", () => {
127+
const validator = new RangeValidator(undefined, 3);
128+
expect(validator.validate(3, "aField")).toBeNull();
129+
expect(validator.validate(1, "aField")).toBeNull();
130+
expect(validator.validate(4, "aField")).toEqual({
131+
constraintType: "range",
132+
constraintValues: [undefined, 3],
133+
failureValue: 4,
134+
memberName: "aField",
135+
});
136+
});
137+
it("supports min-max constraints", () => {
138+
const validator = new RangeValidator(3, 5);
139+
expect(validator.validate(3, "aField")).toBeNull();
140+
expect(validator.validate(4, "aField")).toBeNull();
141+
expect(validator.validate(5, "aField")).toBeNull();
142+
expect(validator.validate(1, "aField")).toEqual({
143+
constraintType: "range",
144+
constraintValues: [3, 5],
145+
failureValue: 1,
146+
memberName: "aField",
147+
});
148+
expect(validator.validate(6, "aField")).toEqual({
149+
constraintType: "range",
150+
constraintValues: [3, 5],
151+
failureValue: 6,
152+
memberName: "aField",
153+
});
154+
});
155+
});
156+
157+
describe("uniqueItems", () => {
158+
const validator = new UniqueItemsValidator();
159+
describe("supports strings", () => {
160+
expect(validator.validate(["a", "b", "c"], "aField")).toBeNull();
161+
expect(validator.validate(["a", "a", "c", "a", "b", "b"], "aField")).toEqual({
162+
constraintType: "uniqueItems",
163+
failureValue: ["a", "b"],
164+
memberName: "aField",
165+
});
166+
});
167+
describe("supports numbers", () => {
168+
expect(validator.validate([1, 2, 3], "aField")).toBeNull();
169+
expect(validator.validate([1, 1, 3, 1, 1, 2.5, 2.5], "aField")).toEqual({
170+
constraintType: "uniqueItems",
171+
failureValue: [1, 2.5],
172+
memberName: "aField",
173+
});
174+
});
175+
describe("supports booleans, I guess", () => {
176+
expect(validator.validate([true, false], "aField")).toBeNull();
177+
expect(validator.validate([true, false, true], "aField")).toEqual({
178+
constraintType: "uniqueItems",
179+
failureValue: [true],
180+
memberName: "aField",
181+
});
182+
});
183+
});

0 commit comments

Comments
 (0)