Skip to content

Commit 1a974f8

Browse files
committed
feat: add contains/minContains/maxContains members
1 parent 21488c6 commit 1a974f8

12 files changed

Lines changed: 276 additions & 6 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
namespace Microsoft.OpenApi;
2+
3+
/// <summary>
4+
/// Compatibility interface for the JSON Schema 2020-12 "contains" keywords support.
5+
/// This interface provides access to the Contains, MaxContains and MinContains properties, which were
6+
/// missed in the initial release of the IOpenApiSchema interface.
7+
///
8+
/// This is a temporary compatibility solution. In the next major version this interface should be
9+
/// merged into IOpenApiSchema.
10+
/// </summary>
11+
public interface IOpenApiSchemaWithContainsProperties
12+
{
13+
/// <summary>
14+
/// Follow JSON Schema definition: https://json-schema.org/draft/2020-12/json-schema-core#name-contains
15+
/// An array instance is valid against "contains" if at least one of its elements is valid against this schema.
16+
/// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema.
17+
/// </summary>
18+
IOpenApiSchema? Contains { get; }
19+
20+
/// <summary>
21+
/// Follow JSON Schema definition: https://json-schema.org/draft/2020-12/json-schema-validation
22+
/// The number of elements matching the "contains" schema MUST be less than or equal to this value.
23+
/// </summary>
24+
uint? MaxContains { get; }
25+
26+
/// <summary>
27+
/// Follow JSON Schema definition: https://json-schema.org/draft/2020-12/json-schema-validation
28+
/// The number of elements matching the "contains" schema MUST be greater than or equal to this value.
29+
/// </summary>
30+
uint? MinContains { get; }
31+
}

src/Microsoft.OpenApi/Models/OpenApiConstants.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,21 @@ public static class OpenApiConstants
485485
/// </summary>
486486
public const string UniqueItems = "uniqueItems";
487487

488+
/// <summary>
489+
/// Field: Contains
490+
/// </summary>
491+
public const string Contains = "contains";
492+
493+
/// <summary>
494+
/// Field: MaxContains
495+
/// </summary>
496+
public const string MaxContains = "maxContains";
497+
498+
/// <summary>
499+
/// Field: MinContains
500+
/// </summary>
501+
public const string MinContains = "minContains";
502+
488503
/// <summary>
489504
/// Field: MaxProperties
490505
/// </summary>

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Microsoft.OpenApi
1818
/// - Serialization: To produce something functionally equivalent to boolean schemas, create an empty <see cref="OpenApiSchema"/>
1919
/// for "true" behavior, or create a schema with only <see cref="Not"/> set to an empty schema for "false" behavior.
2020
/// </summary>
21-
public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IMetadataContainer
21+
public class OpenApiSchema : IOpenApiExtensible, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiSchemaWithContainsProperties, IMetadataContainer
2222
{
2323
/// <inheritdoc />
2424
public string? Title { get; set; }
@@ -207,6 +207,15 @@ public string? Minimum
207207
/// <inheritdoc />
208208
public bool? UniqueItems { get; set; }
209209

210+
/// <inheritdoc />
211+
public IOpenApiSchema? Contains { get; set; }
212+
213+
/// <inheritdoc />
214+
public uint? MaxContains { get; set; }
215+
216+
/// <inheritdoc />
217+
public uint? MinContains { get; set; }
218+
210219
/// <inheritdoc />
211220
public IDictionary<string, IOpenApiSchema>? Properties { get; set; }
212221

@@ -318,6 +327,12 @@ internal OpenApiSchema(IOpenApiSchema schema)
318327
MaxItems = schema.MaxItems ?? MaxItems;
319328
MinItems = schema.MinItems ?? MinItems;
320329
UniqueItems = schema.UniqueItems ?? UniqueItems;
330+
if (schema is IOpenApiSchemaWithContainsProperties containsSchema)
331+
{
332+
Contains = containsSchema.Contains?.CreateShallowCopy();
333+
MaxContains = containsSchema.MaxContains ?? MaxContains;
334+
MinContains = containsSchema.MinContains ?? MinContains;
335+
}
321336
Properties = schema.Properties != null ? new Dictionary<string, IOpenApiSchema>(schema.Properties) : null;
322337
PatternProperties = schema.PatternProperties != null ? new Dictionary<string, IOpenApiSchema>(schema.PatternProperties) : null;
323338
MaxProperties = schema.MaxProperties ?? MaxProperties;
@@ -630,6 +645,15 @@ internal void WriteJsonSchemaKeywords(IOpenApiWriter writer)
630645
writer.WriteOptionalCollection(OpenApiConstants.Examples, Examples, (nodeWriter, s) => nodeWriter.WriteAny(s));
631646
writer.WriteOptionalMap(OpenApiConstants.PatternProperties, PatternProperties, (w, s) => s.SerializeAsV31(w));
632647
writer.WriteOptionalMap(OpenApiConstants.DependentRequired, DependentRequired, (w, s) => w.WriteValue(s));
648+
649+
// contains
650+
writer.WriteOptionalObject(OpenApiConstants.Contains, Contains, (w, s) => s.SerializeAsV31(w));
651+
652+
// maxContains
653+
writer.WriteProperty(OpenApiConstants.MaxContains, MaxContains);
654+
655+
// minContains
656+
writer.WriteProperty(OpenApiConstants.MinContains, MinContains);
633657
}
634658

635659
internal void WriteAsItemsProperties(IOpenApiWriter writer)

src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.OpenApi
1010
/// <summary>
1111
/// Schema reference object
1212
/// </summary>
13-
public class OpenApiSchemaReference : BaseOpenApiReferenceHolder<OpenApiSchema, IOpenApiSchema, JsonSchemaReference>, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiExtensible
13+
public class OpenApiSchemaReference : BaseOpenApiReferenceHolder<OpenApiSchema, IOpenApiSchema, JsonSchemaReference>, IOpenApiSchema, IOpenApiSchemaWithUnevaluatedProperties, IOpenApiSchemaWithContainsProperties, IOpenApiExtensible
1414
{
1515

1616
/// <summary>
@@ -120,6 +120,12 @@ public bool WriteOnly
120120
/// <inheritdoc/>
121121
public bool? UniqueItems { get => Target?.UniqueItems; }
122122
/// <inheritdoc/>
123+
public IOpenApiSchema? Contains { get => (Target as IOpenApiSchemaWithContainsProperties)?.Contains; }
124+
/// <inheritdoc/>
125+
public uint? MaxContains { get => (Target as IOpenApiSchemaWithContainsProperties)?.MaxContains; }
126+
/// <inheritdoc/>
127+
public uint? MinContains { get => (Target as IOpenApiSchemaWithContainsProperties)?.MinContains; }
128+
/// <inheritdoc/>
123129
public IDictionary<string, IOpenApiSchema>? Properties { get => Target?.Properties; }
124130
/// <inheritdoc/>
125131
public IDictionary<string, IOpenApiSchema>? PatternProperties { get => Target?.PatternProperties; }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,17 @@
11
#nullable enable
2+
const Microsoft.OpenApi.OpenApiConstants.Contains = "contains" -> string!
3+
const Microsoft.OpenApi.OpenApiConstants.MaxContains = "maxContains" -> string!
4+
const Microsoft.OpenApi.OpenApiConstants.MinContains = "minContains" -> string!
5+
Microsoft.OpenApi.IOpenApiSchemaWithContainsProperties
6+
Microsoft.OpenApi.IOpenApiSchemaWithContainsProperties.Contains.get -> Microsoft.OpenApi.IOpenApiSchema?
7+
Microsoft.OpenApi.IOpenApiSchemaWithContainsProperties.MaxContains.get -> uint?
8+
Microsoft.OpenApi.IOpenApiSchemaWithContainsProperties.MinContains.get -> uint?
9+
Microsoft.OpenApi.OpenApiSchema.Contains.get -> Microsoft.OpenApi.IOpenApiSchema?
10+
Microsoft.OpenApi.OpenApiSchema.Contains.set -> void
11+
Microsoft.OpenApi.OpenApiSchema.MaxContains.get -> uint?
12+
Microsoft.OpenApi.OpenApiSchema.MaxContains.set -> void
13+
Microsoft.OpenApi.OpenApiSchema.MinContains.get -> uint?
14+
Microsoft.OpenApi.OpenApiSchema.MinContains.set -> void
15+
Microsoft.OpenApi.OpenApiSchemaReference.Contains.get -> Microsoft.OpenApi.IOpenApiSchema?
16+
Microsoft.OpenApi.OpenApiSchemaReference.MaxContains.get -> uint?
17+
Microsoft.OpenApi.OpenApiSchemaReference.MinContains.get -> uint?

src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,32 @@ internal static partial class OpenApiV31Deserializer
144144
}
145145
}
146146
},
147+
{
148+
"contains",
149+
(o, n, doc, c) => o.Contains = LoadSchema(n, doc, c)
150+
},
151+
{
152+
"maxContains",
153+
(o, n, _, _) =>
154+
{
155+
var maxContains = n.GetScalarValue();
156+
if (maxContains != null)
157+
{
158+
o.MaxContains = uint.Parse(maxContains, CultureInfo.InvariantCulture);
159+
}
160+
}
161+
},
162+
{
163+
"minContains",
164+
(o, n, _, _) =>
165+
{
166+
var minContains = n.GetScalarValue();
167+
if (minContains != null)
168+
{
169+
o.MinContains = uint.Parse(minContains, CultureInfo.InvariantCulture);
170+
}
171+
}
172+
},
147173
{
148174
"unevaluatedProperties",
149175
(o, n, t, c) =>

src/Microsoft.OpenApi/Reader/V32/OpenApiSchemaDeserializer.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,32 @@ internal static partial class OpenApiV32Deserializer
144144
}
145145
}
146146
},
147+
{
148+
"contains",
149+
(o, n, doc, c) => o.Contains = LoadSchema(n, doc, c)
150+
},
151+
{
152+
"maxContains",
153+
(o, n, _, _) =>
154+
{
155+
var maxContains = n.GetScalarValue();
156+
if (maxContains != null)
157+
{
158+
o.MaxContains = uint.Parse(maxContains, CultureInfo.InvariantCulture);
159+
}
160+
}
161+
},
162+
{
163+
"minContains",
164+
(o, n, _, _) =>
165+
{
166+
var minContains = n.GetScalarValue();
167+
if (minContains != null)
168+
{
169+
o.MinContains = uint.Parse(minContains, CultureInfo.InvariantCulture);
170+
}
171+
}
172+
},
147173
{
148174
"unevaluatedProperties",
149175
(o, n, t, c) =>

test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiSchemaTests.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,13 @@ public async Task ParseBasicV31SchemaShouldSucceed()
4545
Items = new OpenApiSchema
4646
{
4747
Type = JsonSchemaType.String
48-
}
48+
},
49+
Contains = new OpenApiSchema
50+
{
51+
Type = JsonSchemaType.String
52+
},
53+
MinContains = 1,
54+
MaxContains = 5
4955
},
5056
["vegetables"] = new OpenApiSchema
5157
{

test/Microsoft.OpenApi.Readers.Tests/V31Tests/Samples/OpenApiSchema/jsonSchema.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
"type": "array",
99
"items": {
1010
"type": "string"
11-
}
11+
},
12+
"contains": {
13+
"type": "string"
14+
},
15+
"minContains": 1,
16+
"maxContains": 5
1217
},
1318
"vegetables": {
1419
"type": "array"

test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiSchemaTests.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ public async Task ParseBasicV32SchemaShouldSucceed()
4444
Items = new OpenApiSchema
4545
{
4646
Type = JsonSchemaType.String
47-
}
47+
},
48+
Contains = new OpenApiSchema
49+
{
50+
Type = JsonSchemaType.String
51+
},
52+
MinContains = 1,
53+
MaxContains = 5
4854
},
4955
["vegetables"] = new OpenApiSchema
5056
{

0 commit comments

Comments
 (0)