@tsed/schema 8.0.1 → 8.0.3
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 +11 -10
- package/readme.md +6 -6
- package/src/components/async-api/channelsMapper.ts +81 -0
- package/src/components/async-api/generate.ts +24 -0
- package/src/components/async-api/messageMapper.ts +62 -0
- package/src/components/async-api/payloadMapper.ts +61 -0
- package/src/components/async-api/responseMapper.ts +70 -0
- package/src/components/default/anyMapper.spec.ts +7 -0
- package/src/components/default/anyMapper.ts +33 -0
- package/src/components/default/classMapper.ts +55 -0
- package/src/components/default/discriminatorMappingMapper.ts +36 -0
- package/src/components/default/enumsMapper.spec.ts +64 -0
- package/src/components/default/enumsMapper.ts +17 -0
- package/src/components/default/genericsMapper.ts +70 -0
- package/src/components/default/inheritedClassMapper.ts +24 -0
- package/src/components/default/itemMapper.ts +8 -0
- package/src/components/default/lazyRefMapper.ts +21 -0
- package/src/components/default/mapMapper.ts +25 -0
- package/src/components/default/nullableMapper.ts +55 -0
- package/src/components/default/objectMapper.ts +36 -0
- package/src/components/default/ofMapper.ts +16 -0
- package/src/components/default/propertiesMapper.ts +25 -0
- package/src/components/default/requiredMapper.ts +69 -0
- package/src/components/default/schemaMapper.ts +151 -0
- package/src/components/index.ts +36 -0
- package/src/components/open-spec/discriminatorMappingMapper.ts +7 -0
- package/src/components/open-spec/enumsMapper.ts +20 -0
- package/src/components/open-spec/generate.ts +29 -0
- package/src/components/open-spec/nullableMapper.ts +51 -0
- package/src/components/open-spec/operationInFilesMapper.ts +32 -0
- package/src/components/open-spec/operationInParameterMapper.ts +56 -0
- package/src/components/open-spec/operationInParametersMapper.ts +9 -0
- package/src/components/open-spec/operationInQueryMapper.ts +74 -0
- package/src/components/open-spec/operationMapper.ts +65 -0
- package/src/components/open-spec/operationMediaMapper.ts +11 -0
- package/src/components/open-spec/operationRequestBodyMapper.ts +82 -0
- package/src/components/open-spec/operationResponseMapper.ts +26 -0
- package/src/components/open-spec/pathsMapper.ts +132 -0
- package/src/constants/OperationVerbs.ts +29 -0
- package/src/constants/httpStatusMessages.ts +21 -0
- package/src/constants/jsonSchemaProperties.ts +17 -0
- package/src/decorators/class/children.ts +22 -0
- package/src/decorators/class/discriminatorValue.ts +53 -0
- package/src/decorators/collections/collectionContains.ts +25 -0
- package/src/decorators/collections/collectionOf.spec.ts +327 -0
- package/src/decorators/collections/collectionOf.ts +182 -0
- package/src/decorators/collections/maxItems.spec.ts +40 -0
- package/src/decorators/collections/maxItems.ts +59 -0
- package/src/decorators/collections/maxProperties.spec.ts +109 -0
- package/src/decorators/collections/maxProperties.ts +82 -0
- package/src/decorators/collections/minItems.spec.ts +40 -0
- package/src/decorators/collections/minItems.ts +63 -0
- package/src/decorators/collections/minProperties.spec.ts +109 -0
- package/src/decorators/collections/minProperties.ts +81 -0
- package/src/decorators/collections/uniqueItems.spec.ts +28 -0
- package/src/decorators/collections/uniqueItems.ts +59 -0
- package/src/decorators/common/__snapshots__/groups.spec.ts.snap +321 -0
- package/src/decorators/common/additionalProperties.spec.ts +133 -0
- package/src/decorators/common/additionalProperties.ts +17 -0
- package/src/decorators/common/allOf.spec.ts +276 -0
- package/src/decorators/common/allOf.ts +19 -0
- package/src/decorators/common/allow.spec.ts +130 -0
- package/src/decorators/common/allow.ts +50 -0
- package/src/decorators/common/any.spec.ts +109 -0
- package/src/decorators/common/any.ts +45 -0
- package/src/decorators/common/anyOf.spec.ts +270 -0
- package/src/decorators/common/anyOf.ts +23 -0
- package/src/decorators/common/const.spec.ts +98 -0
- package/src/decorators/common/const.ts +68 -0
- package/src/decorators/common/customKey.spec.ts +105 -0
- package/src/decorators/common/customKey.ts +39 -0
- package/src/decorators/common/default.spec.ts +25 -0
- package/src/decorators/common/default.ts +47 -0
- package/src/decorators/common/defaultMsg.spec.ts +22 -0
- package/src/decorators/common/defaultMsg.ts +38 -0
- package/src/decorators/common/description.spec.ts +166 -0
- package/src/decorators/common/description.ts +77 -0
- package/src/decorators/common/discriminatorKey.ts +14 -0
- package/src/decorators/common/enum.spec.ts +305 -0
- package/src/decorators/common/enum.ts +111 -0
- package/src/decorators/common/errorMsg.spec.ts +57 -0
- package/src/decorators/common/errorMsg.ts +14 -0
- package/src/decorators/common/example.spec.ts +93 -0
- package/src/decorators/common/example.ts +18 -0
- package/src/decorators/common/examples.spec.ts +101 -0
- package/src/decorators/common/examples.ts +20 -0
- package/src/decorators/common/exclusiveMaximum.spec.ts +47 -0
- package/src/decorators/common/exclusiveMaximum.ts +75 -0
- package/src/decorators/common/exclusiveMinimum.spec.ts +47 -0
- package/src/decorators/common/exclusiveMinimum.ts +75 -0
- package/src/decorators/common/format.spec.ts +149 -0
- package/src/decorators/common/format.ts +486 -0
- package/src/decorators/common/forwardGroups.spec.ts +270 -0
- package/src/decorators/common/forwardGroups.ts +16 -0
- package/src/decorators/common/groups.spec.ts +968 -0
- package/src/decorators/common/groups.ts +66 -0
- package/src/decorators/common/hidden.spec.ts +52 -0
- package/src/decorators/common/hidden.ts +36 -0
- package/src/decorators/common/ignore.spec.ts +88 -0
- package/src/decorators/common/ignore.ts +66 -0
- package/src/decorators/common/integer.spec.ts +110 -0
- package/src/decorators/common/integer.ts +69 -0
- package/src/decorators/common/jsonEntityFn.spec.ts +16 -0
- package/src/decorators/common/jsonEntityFn.ts +33 -0
- package/src/decorators/common/maxLength.spec.ts +61 -0
- package/src/decorators/common/maxLength.ts +88 -0
- package/src/decorators/common/maximum.spec.ts +73 -0
- package/src/decorators/common/maximum.ts +146 -0
- package/src/decorators/common/minLength.spec.ts +186 -0
- package/src/decorators/common/minLength.ts +89 -0
- package/src/decorators/common/minimum.spec.ts +74 -0
- package/src/decorators/common/minimum.ts +145 -0
- package/src/decorators/common/multipleOf.spec.ts +33 -0
- package/src/decorators/common/multipleOf.ts +80 -0
- package/src/decorators/common/name.spec.ts +56 -0
- package/src/decorators/common/name.ts +50 -0
- package/src/decorators/common/nullable.spec.ts +818 -0
- package/src/decorators/common/nullable.ts +32 -0
- package/src/decorators/common/oneOf.spec.ts +301 -0
- package/src/decorators/common/oneOf.ts +24 -0
- package/src/decorators/common/optional.spec.ts +43 -0
- package/src/decorators/common/optional.ts +26 -0
- package/src/decorators/common/pattern.spec.ts +27 -0
- package/src/decorators/common/pattern.ts +102 -0
- package/src/decorators/common/property.ts +24 -0
- package/src/decorators/common/readOnly.spec.ts +135 -0
- package/src/decorators/common/readOnly.ts +17 -0
- package/src/decorators/common/recordOf.spec.ts +41 -0
- package/src/decorators/common/recordOf.ts +59 -0
- package/src/decorators/common/required.spec.ts +188 -0
- package/src/decorators/common/required.ts +50 -0
- package/src/decorators/common/requiredGroups.spec.ts +127 -0
- package/src/decorators/common/requiredGroups.ts +23 -0
- package/src/decorators/common/schema.spec.ts +23 -0
- package/src/decorators/common/schema.ts +64 -0
- package/src/decorators/common/title.spec.ts +20 -0
- package/src/decorators/common/title.ts +45 -0
- package/src/decorators/common/typeError.spec.ts +22 -0
- package/src/decorators/common/typeError.ts +15 -0
- package/src/decorators/common/writeOnly.spec.ts +105 -0
- package/src/decorators/common/writeOnly.ts +17 -0
- package/src/decorators/config/jsonEntityComponent.ts +16 -0
- package/src/decorators/generics/genericOf.spec.ts +246 -0
- package/src/decorators/generics/genericOf.ts +112 -0
- package/src/decorators/generics/generics.ts +22 -0
- package/src/decorators/operations/acceptMime.spec.ts +39 -0
- package/src/decorators/operations/acceptMime.ts +25 -0
- package/src/decorators/operations/consumes.spec.ts +89 -0
- package/src/decorators/operations/consumes.ts +37 -0
- package/src/decorators/operations/contentType.spec.ts +40 -0
- package/src/decorators/operations/contentType.ts +24 -0
- package/src/decorators/operations/deprecated.spec.ts +223 -0
- package/src/decorators/operations/deprecated.ts +49 -0
- package/src/decorators/operations/header.spec.ts +214 -0
- package/src/decorators/operations/header.ts +62 -0
- package/src/decorators/operations/in.spec.ts +321 -0
- package/src/decorators/operations/in.ts +141 -0
- package/src/decorators/operations/inFile.spec.ts +209 -0
- package/src/decorators/operations/inFile.ts +55 -0
- package/src/decorators/operations/location.spec.ts +51 -0
- package/src/decorators/operations/location.ts +26 -0
- package/src/decorators/operations/operation.ts +119 -0
- package/src/decorators/operations/operationId.spec.ts +43 -0
- package/src/decorators/operations/operationId.ts +22 -0
- package/src/decorators/operations/operationPath.spec.ts +42 -0
- package/src/decorators/operations/operationPath.ts +33 -0
- package/src/decorators/operations/partial.spec.ts +249 -0
- package/src/decorators/operations/partial.ts +14 -0
- package/src/decorators/operations/path.spec.ts +47 -0
- package/src/decorators/operations/path.ts +27 -0
- package/src/decorators/operations/produces.spec.ts +89 -0
- package/src/decorators/operations/produces.ts +44 -0
- package/src/decorators/operations/publish.spec.ts +26 -0
- package/src/decorators/operations/publish.ts +6 -0
- package/src/decorators/operations/redirect.spec.ts +146 -0
- package/src/decorators/operations/redirect.ts +82 -0
- package/src/decorators/operations/returns.spec.ts +1393 -0
- package/src/decorators/operations/returns.ts +662 -0
- package/src/decorators/operations/route.spec.ts +211 -0
- package/src/decorators/operations/route.ts +154 -0
- package/src/decorators/operations/security.spec.ts +162 -0
- package/src/decorators/operations/security.ts +83 -0
- package/src/decorators/operations/status.spec.ts +582 -0
- package/src/decorators/operations/status.ts +130 -0
- package/src/decorators/operations/subscribe.spec.ts +32 -0
- package/src/decorators/operations/subscribe.ts +6 -0
- package/src/decorators/operations/summary.spec.ts +45 -0
- package/src/decorators/operations/summary.ts +32 -0
- package/src/decorators/operations/tags.spec.ts +113 -0
- package/src/decorators/operations/tags.ts +53 -0
- package/src/decorators/operations/view.spec.ts +16 -0
- package/src/decorators/operations/view.ts +27 -0
- package/src/domain/DecoratorContext.ts +116 -0
- package/src/domain/JsonAliasMap.ts +19 -0
- package/src/domain/JsonClassStore.ts +35 -0
- package/src/domain/JsonDiscriminator.spec.ts +65 -0
- package/src/domain/JsonDiscriminator.ts +52 -0
- package/src/domain/JsonEntityStore.spec.ts +277 -0
- package/src/domain/JsonEntityStore.ts +284 -0
- package/src/domain/JsonFormatTypes.ts +17 -0
- package/src/domain/JsonLazyRef.ts +21 -0
- package/src/domain/JsonMap.spec.ts +15 -0
- package/src/domain/JsonMap.ts +33 -0
- package/src/domain/JsonMedia.ts +24 -0
- package/src/domain/JsonMethodStore.spec.ts +301 -0
- package/src/domain/JsonMethodStore.ts +212 -0
- package/src/domain/JsonOperation.spec.ts +126 -0
- package/src/domain/JsonOperation.ts +219 -0
- package/src/domain/JsonOperationPathsMap.ts +16 -0
- package/src/domain/JsonOperationRoute.spec.ts +109 -0
- package/src/domain/JsonOperationRoute.ts +75 -0
- package/src/domain/JsonParameter.ts +70 -0
- package/src/domain/JsonParameterStore.spec.ts +212 -0
- package/src/domain/JsonParameterStore.ts +138 -0
- package/src/domain/JsonParameterTypes.spec.ts +12 -0
- package/src/domain/JsonParameterTypes.ts +20 -0
- package/src/domain/JsonPropertyStore.spec.ts +177 -0
- package/src/domain/JsonPropertyStore.ts +92 -0
- package/src/domain/JsonRequestBody.ts +47 -0
- package/src/domain/JsonResponse.spec.ts +33 -0
- package/src/domain/JsonResponse.ts +64 -0
- package/src/domain/JsonSchema.spec.ts +1470 -0
- package/src/domain/JsonSchema.ts +1049 -0
- package/src/domain/SpecTypes.ts +6 -0
- package/src/hooks/alterIgnore.ts +25 -0
- package/src/hooks/alterOneOf.ts +67 -0
- package/src/hooks/alterRequiredGroups.ts +12 -0
- package/src/index.ts +185 -0
- package/src/interfaces/IgnoreCallback.ts +5 -0
- package/src/interfaces/JsonHookContext.ts +18 -0
- package/src/interfaces/JsonOpenSpec.ts +7 -0
- package/src/interfaces/JsonSchemaOptions.ts +35 -0
- package/src/registries/JsonSchemaMapperContainer.ts +52 -0
- package/src/registries/enumRegistries.ts +3 -0
- package/src/utils/__snapshots__/generateSpec.spec.ts.snap +109 -0
- package/src/utils/buildPath.ts +8 -0
- package/src/utils/concatParameters.ts +12 -0
- package/src/utils/concatPath.spec.ts +23 -0
- package/src/utils/concatPath.ts +24 -0
- package/src/utils/defineStatusModel.ts +11 -0
- package/src/utils/from.spec.ts +243 -0
- package/src/utils/from.ts +281 -0
- package/src/utils/generateSpec.spec.ts +1262 -0
- package/src/utils/generateSpec.ts +54 -0
- package/src/utils/generics.ts +83 -0
- package/src/utils/getComputedType.spec.ts +45 -0
- package/src/utils/getComputedType.ts +39 -0
- package/src/utils/getInheritedStores.spec.ts +47 -0
- package/src/utils/getInheritedStores.ts +23 -0
- package/src/utils/getJsonEntityStore.ts +36 -0
- package/src/utils/getJsonPathParameters.spec.ts +126 -0
- package/src/utils/getJsonPathParameters.ts +97 -0
- package/src/utils/getJsonSchema.spec.ts +695 -0
- package/src/utils/getJsonSchema.ts +69 -0
- package/src/utils/getJsonType.spec.ts +28 -0
- package/src/utils/getJsonType.ts +46 -0
- package/src/utils/getOperationsRoutes.spec.ts +284 -0
- package/src/utils/getOperationsRoutes.ts +56 -0
- package/src/utils/getOperationsStore.spec.ts +47 -0
- package/src/utils/getOperationsStores.ts +37 -0
- package/src/utils/getPropertiesStores.spec.ts +138 -0
- package/src/utils/getPropertiesStores.ts +66 -0
- package/src/utils/getSpec.spec.ts +610 -0
- package/src/utils/getSpec.ts +117 -0
- package/src/utils/getSpecType.spec.ts +16 -0
- package/src/utils/getSpecType.ts +19 -0
- package/src/utils/isSuccessStatus.spec.ts +22 -0
- package/src/utils/isSuccessStatus.ts +2 -0
- package/src/utils/mapHeaders.spec.ts +57 -0
- package/src/utils/mapHeaders.ts +33 -0
- package/src/utils/mapOpenSpec.ts +44 -0
- package/src/utils/mapOpenSpec2.ts +50 -0
- package/src/utils/mapOpenSpec3.ts +25 -0
- package/src/utils/mapOpenSpecInfo.ts +19 -0
- package/src/utils/mapOperationOptions.ts +34 -0
- package/src/utils/matchGroups.spec.ts +85 -0
- package/src/utils/matchGroups.ts +64 -0
- package/src/utils/mergeSpec.spec.ts +533 -0
- package/src/utils/mergeSpec.ts +54 -0
- package/src/utils/operationIdFormatter.spec.ts +11 -0
- package/src/utils/operationIdFormatter.ts +49 -0
- package/src/utils/ref.ts +65 -0
- package/src/utils/removeHiddenOperation.ts +5 -0
- package/src/utils/serializeEnumValues.ts +27 -0
- package/src/utils/somethingOf.ts +11 -0
- package/src/utils/toJsonMapCollection.spec.ts +18 -0
- package/src/utils/toJsonMapCollection.ts +12 -0
- package/src/utils/toJsonRegex.ts +7 -0
- package/src/utils/transformToOS2.spec.ts +177 -0
- package/src/utils/transformToOS2.ts +607 -0
- package/src/utils/withErrorMsg.spec.ts +16 -0
- package/src/utils/withErrorMsg.ts +56 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import {catchError} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {JsonEntityStore} from "../../domain/JsonEntityStore.js";
|
|
4
|
+
import {array, map, string} from "../../utils/from.js";
|
|
5
|
+
import {getJsonSchema} from "../../utils/getJsonSchema.js";
|
|
6
|
+
import {Property} from "../common/property.js";
|
|
7
|
+
import {Schema} from "../common/schema.js";
|
|
8
|
+
import {In} from "../operations/in.js";
|
|
9
|
+
import {OperationPath} from "../operations/operationPath.js";
|
|
10
|
+
import {CollectionContains} from "./collectionContains.js";
|
|
11
|
+
import {ArrayOf, CollectionOf, MapOf} from "./collectionOf.js";
|
|
12
|
+
import {MaxItems} from "./maxItems.js";
|
|
13
|
+
import {MinItems} from "./minItems.js";
|
|
14
|
+
|
|
15
|
+
describe("@CollectionOf", () => {
|
|
16
|
+
it("should declare a collection and catch error (Array of)", () => {
|
|
17
|
+
// WHEN
|
|
18
|
+
const error = catchError(() => {
|
|
19
|
+
class Model {
|
|
20
|
+
@CollectionOf(undefined)
|
|
21
|
+
num: number[];
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(error?.message).toEqual(
|
|
26
|
+
"A type is required on `@CollectionOf(type)` decorator. Please give a type or wrap it inside an arrow function if you have a circular reference."
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
it("should declare an array of map of string", () => {
|
|
30
|
+
const schema = array().items(map().additionalProperties(string()));
|
|
31
|
+
|
|
32
|
+
class Test {
|
|
33
|
+
@Schema(schema)
|
|
34
|
+
@CollectionOf(Map)
|
|
35
|
+
fields: Map<string, string>[] = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
expect(getJsonSchema(Test)).toEqual({
|
|
39
|
+
properties: {
|
|
40
|
+
fields: {
|
|
41
|
+
items: {
|
|
42
|
+
additionalProperties: {
|
|
43
|
+
type: "string"
|
|
44
|
+
},
|
|
45
|
+
type: "object"
|
|
46
|
+
},
|
|
47
|
+
type: "array"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
type: "object"
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("should declare a collection (Array of)", () => {
|
|
54
|
+
// WHEN
|
|
55
|
+
class Model {
|
|
56
|
+
@CollectionOf(Number)
|
|
57
|
+
num: number[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// THEN
|
|
61
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
62
|
+
|
|
63
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
64
|
+
properties: {
|
|
65
|
+
num: {
|
|
66
|
+
items: {
|
|
67
|
+
type: "number"
|
|
68
|
+
},
|
|
69
|
+
type: "array"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
type: "object"
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it("should declare a collection (Array of Model)", () => {
|
|
76
|
+
// WHEN
|
|
77
|
+
class Nested {
|
|
78
|
+
@Property()
|
|
79
|
+
id: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class Model {
|
|
83
|
+
@MaxItems(10)
|
|
84
|
+
@CollectionOf(Nested)
|
|
85
|
+
@MinItems(1)
|
|
86
|
+
prop: Nested[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// THEN
|
|
90
|
+
const entity = JsonEntityStore.from(Model, "prop");
|
|
91
|
+
const schema = getJsonSchema(Model);
|
|
92
|
+
|
|
93
|
+
expect(entity.schema.getTarget()).toEqual(Array);
|
|
94
|
+
expect(entity.schema.get("type")).toEqual("array");
|
|
95
|
+
expect(entity.itemSchema.getComputedType()).toEqual(Nested);
|
|
96
|
+
|
|
97
|
+
expect(schema).toEqual({
|
|
98
|
+
definitions: {
|
|
99
|
+
Nested: {
|
|
100
|
+
properties: {
|
|
101
|
+
id: {
|
|
102
|
+
type: "string"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
type: "object"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
properties: {
|
|
109
|
+
prop: {
|
|
110
|
+
items: {
|
|
111
|
+
$ref: "#/definitions/Nested"
|
|
112
|
+
},
|
|
113
|
+
minItems: 1,
|
|
114
|
+
maxItems: 10,
|
|
115
|
+
type: "array"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
type: "object"
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
it("should declare a collection (Array of Model on param)", () => {
|
|
122
|
+
// WHEN
|
|
123
|
+
class Nested {
|
|
124
|
+
@Property()
|
|
125
|
+
id: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
class Model {
|
|
129
|
+
@Property()
|
|
130
|
+
id: string;
|
|
131
|
+
@CollectionOf(Nested)
|
|
132
|
+
prop: Nested;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class Ctrl {
|
|
136
|
+
@OperationPath("POST", "/")
|
|
137
|
+
test(@In("body") @CollectionOf(Model) body: Model[]) {}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// THEN
|
|
141
|
+
const entity = JsonEntityStore.from(Ctrl.prototype, "test", 0);
|
|
142
|
+
|
|
143
|
+
expect(getJsonSchema(entity)).toEqual({
|
|
144
|
+
definitions: {
|
|
145
|
+
Model: {
|
|
146
|
+
properties: {
|
|
147
|
+
id: {
|
|
148
|
+
type: "string"
|
|
149
|
+
},
|
|
150
|
+
prop: {
|
|
151
|
+
$ref: "#/definitions/Nested"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
type: "object"
|
|
155
|
+
},
|
|
156
|
+
Nested: {
|
|
157
|
+
properties: {
|
|
158
|
+
id: {
|
|
159
|
+
type: "string"
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
type: "object"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
items: {
|
|
166
|
+
$ref: "#/definitions/Model"
|
|
167
|
+
},
|
|
168
|
+
type: "array"
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
it("should declare a collection (Map of)", () => {
|
|
172
|
+
// WHEN
|
|
173
|
+
class Model {
|
|
174
|
+
@(CollectionOf(Number).MinProperties(2).MaxProperties(5))
|
|
175
|
+
num: Map<string, number>;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// THEN
|
|
179
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
180
|
+
|
|
181
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
182
|
+
properties: {
|
|
183
|
+
num: {
|
|
184
|
+
additionalProperties: {
|
|
185
|
+
type: "number"
|
|
186
|
+
},
|
|
187
|
+
maxProperties: 5,
|
|
188
|
+
minProperties: 2,
|
|
189
|
+
type: "object"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
type: "object"
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
it("should declare a collection (Set of)", () => {
|
|
196
|
+
// WHEN
|
|
197
|
+
class Model {
|
|
198
|
+
@CollectionOf(Number)
|
|
199
|
+
num: Set<number>;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// THEN
|
|
203
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
204
|
+
|
|
205
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
206
|
+
properties: {
|
|
207
|
+
num: {
|
|
208
|
+
items: {
|
|
209
|
+
type: "number"
|
|
210
|
+
},
|
|
211
|
+
type: "array",
|
|
212
|
+
uniqueItems: true
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
type: "object"
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
it("should declare collection with additional props", () => {
|
|
219
|
+
// WHEN
|
|
220
|
+
class Model {
|
|
221
|
+
@(CollectionOf(String).MinItems(0).MaxItems(10).UniqueItems())
|
|
222
|
+
words: string[];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// THEN
|
|
226
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
227
|
+
|
|
228
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
229
|
+
properties: {
|
|
230
|
+
words: {
|
|
231
|
+
type: "array",
|
|
232
|
+
items: {
|
|
233
|
+
type: "string"
|
|
234
|
+
},
|
|
235
|
+
maxItems: 10,
|
|
236
|
+
minItems: 0,
|
|
237
|
+
uniqueItems: true
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
type: "object"
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
it("should declare collection with additional props and contains", () => {
|
|
244
|
+
// WHEN
|
|
245
|
+
class Model {
|
|
246
|
+
@(CollectionContains(String).MinItems(0).MaxItems(10))
|
|
247
|
+
words: string[];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// THEN
|
|
251
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
252
|
+
|
|
253
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
254
|
+
properties: {
|
|
255
|
+
words: {
|
|
256
|
+
type: "array",
|
|
257
|
+
contains: {
|
|
258
|
+
type: "string"
|
|
259
|
+
},
|
|
260
|
+
maxItems: 10,
|
|
261
|
+
minItems: 0
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
type: "object"
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
describe("@ArrayOf", () => {
|
|
270
|
+
interface ArrayList<T> extends Array<T> {
|
|
271
|
+
pull(query: any): this;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
it("should declare a collection (Array of)", () => {
|
|
275
|
+
// WHEN
|
|
276
|
+
class Model {
|
|
277
|
+
@ArrayOf(Number)
|
|
278
|
+
num: ArrayList<number>;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// THEN
|
|
282
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
283
|
+
|
|
284
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
285
|
+
properties: {
|
|
286
|
+
num: {
|
|
287
|
+
items: {
|
|
288
|
+
type: "number"
|
|
289
|
+
},
|
|
290
|
+
type: "array"
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
type: "object"
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe("@MapOf", () => {
|
|
299
|
+
interface MapCollection<K, V> extends Map<K, V> {
|
|
300
|
+
take(query: any): V;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
it("should declare a collection (Map of)", () => {
|
|
304
|
+
// WHEN
|
|
305
|
+
class Model {
|
|
306
|
+
@(MapOf(Number).MinProperties(2).MaxProperties(5))
|
|
307
|
+
num: MapCollection<string, number>;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// THEN
|
|
311
|
+
const classSchema = JsonEntityStore.from(Model);
|
|
312
|
+
|
|
313
|
+
expect(classSchema.schema.toJSON()).toEqual({
|
|
314
|
+
properties: {
|
|
315
|
+
num: {
|
|
316
|
+
additionalProperties: {
|
|
317
|
+
type: "number"
|
|
318
|
+
},
|
|
319
|
+
maxProperties: 5,
|
|
320
|
+
minProperties: 2,
|
|
321
|
+
type: "object"
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
type: "object"
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {JsonEntityStore} from "../../domain/JsonEntityStore.js";
|
|
2
|
+
|
|
3
|
+
export interface ArrayOfChainedDecorators {
|
|
4
|
+
(...args: any): any;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An array instance is valid against `minItems` if its size is greater than, or equal to, the value of this keyword.
|
|
8
|
+
*
|
|
9
|
+
* ::: warning
|
|
10
|
+
* The value `minItems` MUST be a non-negative integer.
|
|
11
|
+
* :::
|
|
12
|
+
*
|
|
13
|
+
* ::: tip
|
|
14
|
+
* Omitting this keyword has the same behavior as a value of 0.
|
|
15
|
+
* :::
|
|
16
|
+
*/
|
|
17
|
+
MinItems(minItems: number): this;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The value `maxItems` MUST be a non-negative integer.
|
|
21
|
+
*
|
|
22
|
+
* An array instance is valid against `maxItems` if its size is less than, or equal to, the value of this keyword.
|
|
23
|
+
*
|
|
24
|
+
* :: warning
|
|
25
|
+
* The value `maxItems` MUST be a non-negative integer.
|
|
26
|
+
* :::
|
|
27
|
+
*/
|
|
28
|
+
MaxItems(maxItems: number): this;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Set the type of the item collection. The possible value is String, Boolean, Number, Date, Object, Class, etc...
|
|
32
|
+
*
|
|
33
|
+
* The array instance will be valid against "contains" if at least one of its elements is valid against the given schema.
|
|
34
|
+
*/
|
|
35
|
+
Contains(): this;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* If this keyword has boolean value false, the instance validates successfully. If it has boolean value true, the instance validates successfully if all of its elements are unique.
|
|
39
|
+
*/
|
|
40
|
+
UniqueItems(uniqueItems?: boolean): this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface MapOfChainedDecorators {
|
|
44
|
+
(...args: any): any;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* An object instance is valid against `minProperties` if its number of properties is less than, or equal to, the value of this keyword.
|
|
48
|
+
*
|
|
49
|
+
* ::: warning
|
|
50
|
+
* The value of this keyword MUST be a non-negative integer.
|
|
51
|
+
* :::
|
|
52
|
+
*/
|
|
53
|
+
MinProperties(minProperties: number): this;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* An object instance is valid against `maxProperties` if its number of properties is less than, or equal to, the value of this keyword.
|
|
57
|
+
*
|
|
58
|
+
* ::: warning
|
|
59
|
+
* The value of this keyword MUST be a non-negative integer.
|
|
60
|
+
* :::
|
|
61
|
+
*/
|
|
62
|
+
MaxProperties(maxProperties: number): this;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface CollectionOfChainedDecorators extends MapOfChainedDecorators, ArrayOfChainedDecorators {}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Set the type of the item collection. The possible value is String, Boolean, Number, Date, Object, Class, etc...
|
|
69
|
+
*
|
|
70
|
+
* ```typescript
|
|
71
|
+
* class Model {
|
|
72
|
+
* @CollectionOf(String).MinLength(0).MaxLength(0)
|
|
73
|
+
* property: string[];
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
* ::: warning
|
|
77
|
+
* You mustn't use the `type Type = string | number` as parameters Type.
|
|
78
|
+
*
|
|
79
|
+
* This example doesn't work:
|
|
80
|
+
*
|
|
81
|
+
* ```typescript
|
|
82
|
+
* type Type = "string" | "number"
|
|
83
|
+
* class Model {
|
|
84
|
+
* @CollectionOf(Type)
|
|
85
|
+
* property: Type[];
|
|
86
|
+
* }
|
|
87
|
+
* ```
|
|
88
|
+
* :::
|
|
89
|
+
*
|
|
90
|
+
* @param {Type<any>} type
|
|
91
|
+
* @param collectionType
|
|
92
|
+
* @decorator
|
|
93
|
+
* @validation
|
|
94
|
+
* @swagger
|
|
95
|
+
* @schema
|
|
96
|
+
* @input
|
|
97
|
+
* @collections
|
|
98
|
+
*/
|
|
99
|
+
export function CollectionOf(type: any, collectionType?: any): CollectionOfChainedDecorators {
|
|
100
|
+
if (!type) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
"A type is required on `@CollectionOf(type)` decorator. Please give a type or wrap it inside an arrow function if you have a circular reference."
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const schema: any = {};
|
|
107
|
+
let contains: boolean = false;
|
|
108
|
+
|
|
109
|
+
const decorator = (...args: any) => {
|
|
110
|
+
const store = JsonEntityStore.from(...args);
|
|
111
|
+
|
|
112
|
+
if (collectionType) {
|
|
113
|
+
store.collectionType = collectionType;
|
|
114
|
+
store.schema.type(collectionType);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
store.type = type;
|
|
118
|
+
store.itemSchema.type(type);
|
|
119
|
+
|
|
120
|
+
store.schema.assign(schema);
|
|
121
|
+
|
|
122
|
+
if (store.isArray && contains) {
|
|
123
|
+
store.schema.set("contains", store.schema.get("items"));
|
|
124
|
+
store.schema.delete("items");
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
decorator.MinItems = (minItems: number) => {
|
|
129
|
+
schema.minItems = minItems;
|
|
130
|
+
|
|
131
|
+
return decorator;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
decorator.MaxItems = (maxItems: number) => {
|
|
135
|
+
schema.maxItems = maxItems;
|
|
136
|
+
|
|
137
|
+
return decorator;
|
|
138
|
+
};
|
|
139
|
+
decorator.MinProperties = (minProperties: number) => {
|
|
140
|
+
schema.minProperties = minProperties;
|
|
141
|
+
|
|
142
|
+
return decorator;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
decorator.MaxProperties = (maxProperties: number) => {
|
|
146
|
+
schema.maxProperties = maxProperties;
|
|
147
|
+
|
|
148
|
+
return decorator;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
decorator.Contains = () => {
|
|
152
|
+
contains = true;
|
|
153
|
+
|
|
154
|
+
return decorator;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
decorator.UniqueItems = (uniqueItems = true) => {
|
|
158
|
+
schema.uniqueItems = uniqueItems;
|
|
159
|
+
|
|
160
|
+
return decorator;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return decorator;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Alias of @@GenericOf@@ decorator.
|
|
168
|
+
* @param type
|
|
169
|
+
* @decorator
|
|
170
|
+
*/
|
|
171
|
+
export function ArrayOf(type: any): ArrayOfChainedDecorators {
|
|
172
|
+
return CollectionOf(type, Array);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Alias of @@GenericOf@@ decorator.
|
|
177
|
+
* @param type
|
|
178
|
+
* @decorator
|
|
179
|
+
*/
|
|
180
|
+
export function MapOf(type: any): MapOfChainedDecorators {
|
|
181
|
+
return CollectionOf(type, Map);
|
|
182
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {getJsonSchema} from "../../utils/getJsonSchema.js";
|
|
2
|
+
import {CollectionOf} from "./collectionOf.js";
|
|
3
|
+
import {MaxItems} from "./maxItems.js";
|
|
4
|
+
|
|
5
|
+
describe("@MaxItems", () => {
|
|
6
|
+
it("should declare a prop", () => {
|
|
7
|
+
// WHEN
|
|
8
|
+
class Model {
|
|
9
|
+
@CollectionOf(Number)
|
|
10
|
+
@MaxItems(10)
|
|
11
|
+
num: number[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// THEN
|
|
15
|
+
expect(getJsonSchema(Model)).toEqual({
|
|
16
|
+
properties: {
|
|
17
|
+
num: {
|
|
18
|
+
items: {
|
|
19
|
+
type: "number"
|
|
20
|
+
},
|
|
21
|
+
maxItems: 10,
|
|
22
|
+
type: "array"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
type: "object"
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it("should throw error", () => {
|
|
29
|
+
// WHEN
|
|
30
|
+
let actualError: any;
|
|
31
|
+
try {
|
|
32
|
+
MaxItems(-1);
|
|
33
|
+
} catch (er) {
|
|
34
|
+
actualError = er;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// THEN
|
|
38
|
+
expect(actualError.message).toBe("The value of maxItems MUST be a non-negative integer.");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {withErrorMsg} from "../../utils/withErrorMsg.js";
|
|
2
|
+
import {JsonEntityFn} from "../common/jsonEntityFn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The value `maxItems` MUST be a non-negative integer.
|
|
6
|
+
*
|
|
7
|
+
* An array instance is valid against `maxItems` if its size is less than, or equal to, the value of this keyword.
|
|
8
|
+
*
|
|
9
|
+
* :: warning
|
|
10
|
+
* The value `maxItems` MUST be a non-negative integer.
|
|
11
|
+
* :::
|
|
12
|
+
*
|
|
13
|
+
* :: warning
|
|
14
|
+
* This decorator will be removed in v7.
|
|
15
|
+
* For v6 user, use @@MaxItems@@ from @tsed/schema instead of @tsed/platform-http.
|
|
16
|
+
* :::
|
|
17
|
+
*
|
|
18
|
+
* ## Example
|
|
19
|
+
*
|
|
20
|
+
* ```typescript
|
|
21
|
+
* class Model {
|
|
22
|
+
* @CollectionOf(String)
|
|
23
|
+
* @MaxItems(10)
|
|
24
|
+
* property: string[];
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Will produce:
|
|
29
|
+
*
|
|
30
|
+
* ```json
|
|
31
|
+
* {
|
|
32
|
+
* "type": "object",
|
|
33
|
+
* "properties": {
|
|
34
|
+
* "property": {
|
|
35
|
+
* "type": "number",
|
|
36
|
+
* "maxItems": 10
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @param {number} maxItems
|
|
43
|
+
* @decorator
|
|
44
|
+
* @validation
|
|
45
|
+
* @swagger
|
|
46
|
+
* @schema
|
|
47
|
+
* @input
|
|
48
|
+
* @collections
|
|
49
|
+
* @ajv-errors
|
|
50
|
+
*/
|
|
51
|
+
export const MaxItems = withErrorMsg("maxItems", (maxItems: number) => {
|
|
52
|
+
if (maxItems < 0) {
|
|
53
|
+
throw new Error("The value of maxItems MUST be a non-negative integer.");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return JsonEntityFn((storedJson) => {
|
|
57
|
+
storedJson.schema.maxItems(maxItems);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {SpecTypes} from "../../domain/SpecTypes.js";
|
|
2
|
+
import {getJsonSchema} from "../../utils/getJsonSchema.js";
|
|
3
|
+
import {getSpec} from "../../utils/getSpec.js";
|
|
4
|
+
import {In} from "../operations/in.js";
|
|
5
|
+
import {OperationPath} from "../operations/operationPath.js";
|
|
6
|
+
import {MaxProperties} from "./maxProperties.js";
|
|
7
|
+
|
|
8
|
+
describe("@MaxProperties", () => {
|
|
9
|
+
it("should declare max value (any)", () => {
|
|
10
|
+
// WHEN
|
|
11
|
+
class Model {
|
|
12
|
+
@MaxProperties(10)
|
|
13
|
+
prop: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// THEN
|
|
17
|
+
expect(getJsonSchema(Model)).toEqual({
|
|
18
|
+
properties: {
|
|
19
|
+
prop: {
|
|
20
|
+
maxProperties: 10,
|
|
21
|
+
type: "object"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
type: "object"
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
it("should declare max value (Map<any>)", () => {
|
|
28
|
+
// WHEN
|
|
29
|
+
class Model {
|
|
30
|
+
@MaxProperties(10)
|
|
31
|
+
prop: Map<string, any>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// THEN
|
|
35
|
+
expect(getJsonSchema(Model)).toEqual({
|
|
36
|
+
properties: {
|
|
37
|
+
prop: {
|
|
38
|
+
maxProperties: 10,
|
|
39
|
+
type: "object"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
type: "object"
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
it("should declare max value on class", () => {
|
|
46
|
+
// WHEN
|
|
47
|
+
@MaxProperties(10)
|
|
48
|
+
class Model {}
|
|
49
|
+
|
|
50
|
+
// THEN
|
|
51
|
+
expect(getJsonSchema(Model)).toEqual({
|
|
52
|
+
maxProperties: 10,
|
|
53
|
+
type: "object"
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it("should declare max value on param", () => {
|
|
57
|
+
// WHEN
|
|
58
|
+
class MyController {
|
|
59
|
+
@OperationPath("POST", "/")
|
|
60
|
+
method(@In("body") @MaxProperties(10) test: any) {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// THEN
|
|
64
|
+
expect(getSpec(MyController, {specType: SpecTypes.OPENAPI})).toEqual({
|
|
65
|
+
paths: {
|
|
66
|
+
"/": {
|
|
67
|
+
post: {
|
|
68
|
+
operationId: "myControllerMethod",
|
|
69
|
+
parameters: [],
|
|
70
|
+
requestBody: {
|
|
71
|
+
content: {
|
|
72
|
+
"application/json": {
|
|
73
|
+
schema: {
|
|
74
|
+
maxProperties: 10,
|
|
75
|
+
type: "object"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: false
|
|
80
|
+
},
|
|
81
|
+
responses: {
|
|
82
|
+
"200": {
|
|
83
|
+
description: "Success"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
tags: ["MyController"]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
tags: [
|
|
91
|
+
{
|
|
92
|
+
name: "MyController"
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it("should throw an error when the given parameters is as negative integer", () => {
|
|
98
|
+
// WHEN
|
|
99
|
+
let actualError: any;
|
|
100
|
+
try {
|
|
101
|
+
MaxProperties(-1);
|
|
102
|
+
} catch (er) {
|
|
103
|
+
actualError = er;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// THEN
|
|
107
|
+
expect(actualError.message).toBe("The value of maxProperties MUST be a non-negative integer.");
|
|
108
|
+
});
|
|
109
|
+
});
|