Skip to content

Commit 83aaf31

Browse files
committed
fix: nullable information for number types
Signed-off-by: Vincent Biret <[email protected]>
1 parent f17b72f commit 83aaf31

File tree

2 files changed

+92
-65
lines changed

2 files changed

+92
-65
lines changed

src/Microsoft.OpenApi.OData.Reader/Generator/OpenApiEdmTypeSchemaGenerator.cs

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ public static OpenApiSchema CreateEdmTypeSchema(this ODataContext context, IEdmT
3333
Utils.CheckArgumentNull(edmTypeReference, nameof(edmTypeReference));
3434

3535
switch (edmTypeReference.TypeKind())
36-
{
37-
// Collection-valued structural and navigation are represented as Schema Objects of type array.
38-
// The value of the items keyword is a Schema Object specifying the type of the items.
36+
{
37+
// Collection-valued structural and navigation are represented as Schema Objects of type array.
38+
// The value of the items keyword is a Schema Object specifying the type of the items.
3939
case EdmTypeKind.Collection:
4040

4141
IEdmTypeReference typeRef = edmTypeReference.AsCollection().ElementType();
4242
OpenApiSchema schema;
43-
schema = typeRef.TypeKind() == EdmTypeKind.Complex || typeRef.TypeKind() == EdmTypeKind.Entity
44-
? context.CreateStructuredTypeSchema(typeRef.AsStructured(), true)
45-
: context.CreateEdmTypeSchema(typeRef);
46-
43+
schema = typeRef.TypeKind() == EdmTypeKind.Complex || typeRef.TypeKind() == EdmTypeKind.Entity
44+
? context.CreateStructuredTypeSchema(typeRef.AsStructured(), true)
45+
: context.CreateEdmTypeSchema(typeRef);
46+
4747
return new OpenApiSchema
4848
{
4949
Type = "array",
@@ -133,7 +133,8 @@ public static OpenApiSchema CreateSchema(this ODataContext context, IEdmPrimitiv
133133
}
134134

135135
// Nullable properties are marked with the keyword nullable and a value of true.
136-
schema.Nullable = primitiveType.IsNullable ? true : false;
136+
// oneOf and anyOf don't have type, so they can't be set to nullable, entries are nullable
137+
schema.Nullable = schema.AnyOf == null && schema.OneOf == null && primitiveType.IsNullable;
137138
}
138139

139140
return schema;
@@ -182,8 +183,8 @@ public static OpenApiSchema CreateSchema(this ODataContext context, IEdmPrimitiv
182183
{
183184
schema.OneOf = new List<OpenApiSchema>
184185
{
185-
new OpenApiSchema { Type = Constants.NumberType, Format = Constants.DecimalFormat },
186-
new OpenApiSchema { Type = Constants.StringType },
186+
new OpenApiSchema { Type = Constants.NumberType, Format = Constants.DecimalFormat, Nullable = true },
187+
new OpenApiSchema { Type = Constants.StringType, Nullable = true },
187188
};
188189
}
189190
else
@@ -211,8 +212,8 @@ public static OpenApiSchema CreateSchema(this ODataContext context, IEdmPrimitiv
211212
case EdmPrimitiveTypeKind.Single: // single
212213
schema.OneOf = new List<OpenApiSchema>
213214
{
214-
new OpenApiSchema { Type = Constants.NumberType, Format = "float" },
215-
new OpenApiSchema { Type = Constants.StringType },
215+
new OpenApiSchema { Type = Constants.NumberType, Format = "float", Nullable = true },
216+
new OpenApiSchema { Type = Constants.StringType, Nullable = true },
216217
new OpenApiSchema
217218
{
218219
UnresolvedReference = true,
@@ -246,8 +247,8 @@ public static OpenApiSchema CreateSchema(this ODataContext context, IEdmPrimitiv
246247
{
247248
schema.OneOf = new List<OpenApiSchema>
248249
{
249-
new OpenApiSchema { Type = Constants.IntegerType, Format = Constants.Int64Format },
250-
new OpenApiSchema { Type = Constants.StringType }
250+
new OpenApiSchema { Type = Constants.IntegerType, Format = Constants.Int64Format, Nullable = true },
251+
new OpenApiSchema { Type = Constants.StringType, Nullable = true }
251252
};
252253
}
253254
else
@@ -469,47 +470,47 @@ private static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEd
469470
private static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredTypeReference typeReference, bool isTypeCollection = false)
470471
{
471472
Debug.Assert(context != null);
472-
Debug.Assert(typeReference != null);
473-
474-
OpenApiSchema schema = new OpenApiSchema();
475-
476-
// AnyOf will only be valid openApi for version 3
477-
// otherwise the reference should be set directly
478-
// as per OASIS documentation for openApi version 2
479-
// Collections of structured types cannot be nullable
480-
if (typeReference.IsNullable && !isTypeCollection &&
481-
(context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0))
482-
{
483-
schema.Reference = null;
484-
schema.AnyOf = new List<OpenApiSchema>
485-
{
486-
new OpenApiSchema
487-
{
488-
UnresolvedReference = true,
489-
Reference = new OpenApiReference
490-
{
491-
Type = ReferenceType.Schema,
492-
Id = typeReference.Definition.FullTypeName()
493-
}
494-
},
495-
new OpenApiSchema
496-
{
497-
Type = "object",
498-
Nullable = true
499-
}
500-
};
501-
}
502-
else
503-
{
504-
schema.Type = null;
505-
schema.AnyOf = null;
506-
schema.Reference = new OpenApiReference
507-
{
508-
Type = ReferenceType.Schema,
509-
Id = typeReference.Definition.FullTypeName()
510-
};
511-
schema.UnresolvedReference = true;
512-
schema.Nullable = typeReference.IsNullable;
473+
Debug.Assert(typeReference != null);
474+
475+
OpenApiSchema schema = new OpenApiSchema();
476+
477+
// AnyOf will only be valid openApi for version 3
478+
// otherwise the reference should be set directly
479+
// as per OASIS documentation for openApi version 2
480+
// Collections of structured types cannot be nullable
481+
if (typeReference.IsNullable && !isTypeCollection &&
482+
(context.Settings.OpenApiSpecVersion >= OpenApiSpecVersion.OpenApi3_0))
483+
{
484+
schema.Reference = null;
485+
schema.AnyOf = new List<OpenApiSchema>
486+
{
487+
new OpenApiSchema
488+
{
489+
UnresolvedReference = true,
490+
Reference = new OpenApiReference
491+
{
492+
Type = ReferenceType.Schema,
493+
Id = typeReference.Definition.FullTypeName()
494+
}
495+
},
496+
new OpenApiSchema
497+
{
498+
Type = "object",
499+
Nullable = true
500+
}
501+
};
502+
}
503+
else
504+
{
505+
schema.Type = null;
506+
schema.AnyOf = null;
507+
schema.Reference = new OpenApiReference
508+
{
509+
Type = ReferenceType.Schema,
510+
Id = typeReference.Definition.FullTypeName()
511+
};
512+
schema.UnresolvedReference = true;
513+
schema.Nullable = typeReference.IsNullable;
513514
}
514515

515516
return schema;

test/Microsoft.OpenAPI.OData.Reader.Tests/Generator/OpenApiEdmTypeSchemaGeneratorTest.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,20 @@ public void CreateEdmTypeSchemaReturnSchemaForDecimal(bool isNullable, bool IEEE
401401
Assert.Null(schema.Type);
402402
Assert.NotNull(schema.OneOf);
403403
Assert.Equal(2, schema.OneOf.Count);
404-
Assert.Equal(new[] { "number", "string" }, schema.OneOf.Select(a => a.Type));
404+
var numberSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("number", StringComparison.OrdinalIgnoreCase));
405+
Assert.NotNull(numberSchema);
406+
Assert.True(numberSchema.Nullable);
407+
var stringSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("string", StringComparison.OrdinalIgnoreCase));
408+
Assert.NotNull(stringSchema);
409+
Assert.True(stringSchema.Nullable);
410+
Assert.False(schema.Nullable);
405411
}
406412
else
407413
{
408414
Assert.Equal("number", schema.Type);
409415
Assert.Null(schema.OneOf);
416+
Assert.Equal(isNullable, schema.Nullable);
410417
}
411-
412-
Assert.Equal(isNullable, schema.Nullable);
413418
}
414419

415420
[Theory]
@@ -439,15 +444,20 @@ public void CreateEdmTypeSchemaReturnSchemaForInt64(bool isNullable, bool IEEE75
439444
Assert.Null(schema.Type);
440445
Assert.NotNull(schema.OneOf);
441446
Assert.Equal(2, schema.OneOf.Count);
442-
Assert.Equal(new[] { "integer", "string" }, schema.OneOf.Select(a => a.Type));
447+
var integerSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("integer", StringComparison.OrdinalIgnoreCase));
448+
Assert.NotNull(integerSchema);
449+
Assert.True(integerSchema.Nullable);
450+
var stringSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("string", StringComparison.OrdinalIgnoreCase));
451+
Assert.NotNull(stringSchema);
452+
Assert.True(stringSchema.Nullable);
453+
Assert.False(schema.Nullable);
443454
}
444455
else
445456
{
446457
Assert.Equal("integer", schema.Type);
447458
Assert.Null(schema.AnyOf);
459+
Assert.Equal(isNullable, schema.Nullable);
448460
}
449-
450-
Assert.Equal(isNullable, schema.Nullable);
451461
}
452462

453463
[Theory]
@@ -502,8 +512,16 @@ public void CreateEdmTypeSchemaReturnSchemaForDouble(bool isNullable)
502512
// & Assert
503513
Assert.Null(schema.Type);
504514

505-
Assert.Equal("double", schema.OneOf.FirstOrDefault(x => !string.IsNullOrEmpty(x.Format))?.Format);
506-
Assert.Equal(isNullable, schema.Nullable);
515+
var numberSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("number", StringComparison.OrdinalIgnoreCase));
516+
Assert.NotNull(numberSchema);
517+
Assert.True(numberSchema.Nullable);
518+
Assert.Equal("double", numberSchema.Format, StringComparer.OrdinalIgnoreCase);
519+
520+
var stringSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("string", StringComparison.OrdinalIgnoreCase));
521+
Assert.NotNull(stringSchema);
522+
Assert.True(stringSchema.Nullable);
523+
524+
Assert.False(schema.Nullable);
507525

508526
Assert.Null(schema.AnyOf);
509527

@@ -528,8 +546,16 @@ public void CreateEdmTypeSchemaReturnSchemaForSingle(bool isNullable)
528546
// & Assert
529547
Assert.Null(schema.Type);
530548

531-
Assert.Equal("float", schema.OneOf.FirstOrDefault(x => !string.IsNullOrEmpty(x.Format))?.Format);
532-
Assert.Equal(isNullable, schema.Nullable);
549+
var numberSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("number", StringComparison.OrdinalIgnoreCase));
550+
Assert.NotNull(numberSchema);
551+
Assert.True(numberSchema.Nullable);
552+
Assert.Equal("float", numberSchema.Format, StringComparer.OrdinalIgnoreCase);
553+
554+
var stringSchema = schema.OneOf.FirstOrDefault(x => x.Type.Equals("string", StringComparison.OrdinalIgnoreCase));
555+
Assert.NotNull(stringSchema);
556+
Assert.True(stringSchema.Nullable);
557+
558+
Assert.False(schema.Nullable);
533559

534560
Assert.Null(schema.AnyOf);
535561

0 commit comments

Comments
 (0)