diff --git a/CHANGELOG.md b/CHANGELOG.md index d34de98..733e901 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] ### Added +- Support `@Validation.MinItems` and `@Validation.MaxItems` annotations on collection-typed properties and action parameters to emit `minItems`/`maxItems` in the generated OpenAPI schema ### Changed ### Deprecated ### Removed diff --git a/lib/compile/csdl.js b/lib/compile/csdl.js index 0b113d5..cb02fcf 100644 --- a/lib/compile/csdl.js +++ b/lib/compile/csdl.js @@ -14,7 +14,7 @@ const CDS_TERMS = Object.freeze({ Core: ['AcceptableMediaTypes', 'Computed', 'ComputedDefaultValue', 'DefaultNamespace', 'Description', 'Example', 'Immutable', 'LongDescription', 'MediaType', 'OptionalParameter', 'Permissions', 'SchemaVersion'], JSON: ['Schema'], - Validation: ['AllowedValues', 'Exclusive', 'Maximum', 'Minimum', 'Pattern'] + Validation: ['AllowedValues', 'Exclusive', 'Maximum', 'MaxItems', 'Minimum', 'MinItems', 'Pattern'] }) /** diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 6c1ae31..f57dd08 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -2574,7 +2574,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot } } - if (element[meta.voc.Validation.Maximum] != undefined) { + if (element[meta.voc.Validation.Maximum] !== undefined) { if (s.$ref) s = { allOf: [s] }; if (isAnyOfSchema(s) && isNumberSchema(s.anyOf[0])) { s.anyOf[0].maximum = element[meta.voc.Validation.Maximum]; @@ -2584,7 +2584,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot if (element[meta.voc.Validation.Maximum + meta.voc.Validation.Exclusive]) s.exclusiveMaximum = true; } - if (element[meta.voc.Validation.Minimum] != undefined) { + if (element[meta.voc.Validation.Minimum] !== undefined) { if (s.$ref) s = { allOf: [s] }; if (isAnyOfSchema(s) && isNumberSchema(s.anyOf[0])) { s.anyOf[0].minimum = element[meta.voc.Validation.Minimum]; @@ -2599,6 +2599,10 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot type: 'array', items: s }; + if (element[meta.voc.Validation.MinItems] !== undefined) + s.minItems = element[meta.voc.Validation.MinItems]; + if (element[meta.voc.Validation.MaxItems] !== undefined) + s.maxItems = element[meta.voc.Validation.MaxItems]; } const description = forParameter ? undefined : (element[meta.voc.Core.LongDescription] || element[meta.voc.Core.Description]); diff --git a/lib/compile/types.d.ts b/lib/compile/types.d.ts index b774ade..4f70f7e 100644 --- a/lib/compile/types.d.ts +++ b/lib/compile/types.d.ts @@ -23,7 +23,9 @@ type BooleanSchema = { type ArraySchema = { type: 'array', - items: Schema + items: Schema, + minItems?: number, + maxItems?: number } type ObjectSchema = { diff --git a/test/lib/compile/data/annotations.json b/test/lib/compile/data/annotations.json index 684dc2c..2dcdc41 100644 --- a/test/lib/compile/data/annotations.json +++ b/test/lib/compile/data/annotations.json @@ -90,6 +90,15 @@ "$Type": "Edm.Decimal", "$Scale": 5 }, + "MinMaxItems": { + "$Collection": true, + "@Validation.MinItems": 1, + "@Validation.MaxItems": 5 + }, + "MinItems": { + "$Collection": true, + "@Validation.MinItems": 2 + }, "Example": { "$Nullable": true, "@Core.Example": { diff --git a/test/lib/compile/data/annotations.openapi3.json b/test/lib/compile/data/annotations.openapi3.json index 9077d79..ac36152 100644 --- a/test/lib/compile/data/annotations.openapi3.json +++ b/test/lib/compile/data/annotations.openapi3.json @@ -113,6 +113,10 @@ "Minimum desc", "MinimumExt", "MinimumExt desc", + "MinMaxItems", + "MinMaxItems desc", + "MinItems", + "MinItems desc", "ExampleExt", "ExampleExt desc", "Computed", @@ -149,6 +153,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -250,6 +256,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -566,6 +574,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -867,6 +877,10 @@ "Minimum desc", "MinimumExt", "MinimumExt desc", + "MinMaxItems", + "MinMaxItems desc", + "MinItems", + "MinItems desc", "Example", "Example desc", "ExampleExt", @@ -905,6 +919,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -1006,6 +1022,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -1113,6 +1131,10 @@ "Minimum desc", "MinimumExt", "MinimumExt desc", + "MinMaxItems", + "MinMaxItems desc", + "MinItems", + "MinItems desc", "Example", "Example desc", "ExampleExt", @@ -1151,6 +1173,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -1252,6 +1276,8 @@ "MaximumExt", "Minimum", "MinimumExt", + "MinMaxItems", + "MinItems", "Example", "ExampleExt", "Computed", @@ -2351,6 +2377,21 @@ "x-sap-scale": 5, "exclusiveMinimum": true }, + "MinMaxItems": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "maxItems": 5 + }, + "MinItems": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + }, "Example": { "type": "string", "nullable": true,