Move the functions to OpenApiSchemaGenerator and add unit test for the Enum type schema

This commit is contained in:
Sam Xu 2017-11-30 11:09:02 -08:00
parent 033d166490
commit 5ee41ab4df
11 changed files with 289 additions and 127 deletions

View file

@ -3,8 +3,11 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Collections.Generic;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.OData.Properties;
using Microsoft.OpenApi.Models;
namespace Microsoft.OpenApi.OData.Generator
@ -39,7 +42,7 @@ namespace Microsoft.OpenApi.OData.Generator
case EdmSchemaElementKind.TypeDefinition: // Type definition
{
IEdmType reference = (IEdmType)element;
schemas.Add(reference.FullTypeName(), context.Model.CreateEdmTypeSchema(reference));
schemas.Add(reference.FullTypeName(), context.CreateEdmTypeSchema(reference));
}
break;
}
@ -49,5 +52,165 @@ namespace Microsoft.OpenApi.OData.Generator
return schemas;
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmType"/>.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="edmType">The Edm type.</param>
/// <returns>The created <see cref="OpenApiSchema"/>.</returns>
public static OpenApiSchema CreateEdmTypeSchema(this ODataContext context, IEdmType edmType)
{
if (context == null)
{
throw Error.ArgumentNull(nameof(context));
}
if (edmType == null)
{
throw Error.ArgumentNull(nameof(edmType));
}
switch (edmType.TypeKind)
{
case EdmTypeKind.Complex: // complex type
case EdmTypeKind.Entity: // entity type
return context.CreateStructuredTypeSchema((IEdmStructuredType)edmType, true);
case EdmTypeKind.Enum: // enum type
return context.CreateEnumTypeSchema((IEdmEnumType)edmType);
case EdmTypeKind.TypeDefinition: // type definition
return context.CreateTypeDefinitionSchema((IEdmTypeDefinition)edmType);
case EdmTypeKind.None:
default:
throw Error.NotSupported(String.Format(SRResource.NotSupportedEdmTypeKind, edmType.TypeKind));
}
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmStructuredType"/>.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="structuredType">The Edm structured type.</param>
/// <param name="processBase">The process the based.</param>
/// <returns>The created <see cref="OpenApiSchema"/>.</returns>
public static OpenApiSchema CreateStructuredTypeSchema(this ODataContext context, IEdmStructuredType structuredType, bool processBase)
{
if (processBase && structuredType.BaseType != null)
{
// A structured type with a base type is represented as a Schema Object
// that contains the keyword allOf whose value is an array with two items:
return new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
// 1. a JSON Reference to the Schema Object of the base type
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = structuredType.BaseType.FullTypeName()
}
},
// 2. a Schema Object describing the derived type
context.CreateStructuredTypeSchema(structuredType, false)
}
};
}
else
{
// A structured type without a base type is represented as a Schema Object of type object
return new OpenApiSchema
{
Title = (structuredType as IEdmSchemaElement).Name,
Type = "object",
// Each structural property and navigation property is represented
// as a name/value pair of the standard OpenAPI properties object.
Properties = context.CreateStructuredTypePropertiesSchema(structuredType),
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the structured type.
// However, ODL doesn't support the Core.Description on structure type.
};
}
}
/// <summary>
/// Create a <see cref="OpenApiSchema"/> for a <see cref="IEdmEnumType"/>.
/// An enumeration type is represented as a Schema Object of type string containing the OpenAPI Specification enum keyword.
/// Its value is an array that contains a string with the member name for each enumeration member.
/// </summary>
/// <param name="context">The OData context.</param>
/// <param name="enumType">The Edm enum type.</param>
/// <returns>The created <see cref="OpenApiSchema"/>.</returns>
public static OpenApiSchema CreateEnumTypeSchema(this ODataContext context, IEdmEnumType enumType)
{
if (context == null)
{
throw Error.ArgumentNull(nameof(context));
}
if (enumType == null)
{
throw Error.ArgumentNull(nameof(enumType));
}
OpenApiSchema schema = new OpenApiSchema
{
// An enumeration type is represented as a Schema Object of type string
Type = "string",
// containing the OpenAPI Specification enum keyword.
Enum = new List<IOpenApiAny>(),
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the enumeration type.
Description = context.Model.GetDescriptionAnnotation(enumType)
};
// Enum value is an array that contains a string with the member name for each enumeration member.
foreach (IEdmEnumMember member in enumType.Members)
{
schema.Enum.Add(new OpenApiString(member.Name));
}
schema.Title = enumType.Name;
return schema;
}
// 4.6.1.1 Properties
public static IDictionary<string, OpenApiSchema> CreateStructuredTypePropertiesSchema(this ODataContext context, IEdmStructuredType structuredType)
{
// The name is the property name, the value is a Schema Object describing the allowed values of the property.
IDictionary<string, OpenApiSchema> properties = new Dictionary<string, OpenApiSchema>();
// structure properties
foreach (var property in structuredType.DeclaredStructuralProperties())
{
// OpenApiSchema propertySchema = property.Type.CreateSchema();
// propertySchema.Default = property.DefaultValueString != null ? new OpenApiString(property.DefaultValueString) : null;
properties.Add(property.Name, property.CreatePropertySchema());
}
// navigation properties
foreach (var property in structuredType.DeclaredNavigationProperties())
{
OpenApiSchema propertySchema = property.Type.CreateSchema();
properties.Add(property.Name, propertySchema);
}
return properties;
}
public static OpenApiSchema CreateTypeDefinitionSchema(this ODataContext context, IEdmTypeDefinition typeDefinition)
{
return typeDefinition?.UnderlyingType?.CreateSchema();
}
}
}

View file

@ -18,125 +18,6 @@ namespace Microsoft.OpenApi.OData.Generator
{
private static string _externalResource = "https://raw.githubusercontent.com/oasis-tcs/odata-openapi/master/examples/odata-definitions.json";
public static OpenApiSchema CreateEdmTypeSchema(this IEdmModel model, IEdmType definition)
{
switch (definition.TypeKind)
{
case EdmTypeKind.Complex: // complex type
case EdmTypeKind.Entity: // entity type
return model.CreateStructuredTypeSchema((IEdmStructuredType)definition, true);
case EdmTypeKind.Enum: // enum type
return model.CreateEnumTypeSchema((IEdmEnumType)definition);
case EdmTypeKind.TypeDefinition: // type definition
return model.CreateTypeDefinitionSchema((IEdmTypeDefinition)definition);
case EdmTypeKind.None:
default:
throw Error.NotSupported(String.Format("Not supported {0} type kind.", definition.TypeKind));
}
}
public static OpenApiSchema CreateStructuredTypeSchema(this IEdmModel model, IEdmStructuredType structuredType, bool processBase)
{
if (processBase && structuredType.BaseType != null)
{
// A structured type with a base type is represented as a Schema Object
// that contains the keyword allOf whose value is an array with two items:
return new OpenApiSchema
{
AllOf = new List<OpenApiSchema>
{
// 1. a JSON Reference to the Schema Object of the base type
new OpenApiSchema
{
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = structuredType.BaseType.FullTypeName()
}
},
// 2. a Schema Object describing the derived type
model.CreateStructuredTypeSchema(structuredType, false)
}
};
}
else
{
// A structured type without a base type is represented as a Schema Object of type object
return new OpenApiSchema
{
Title = (structuredType as IEdmSchemaElement).Name,
Type = "object",
// Each structural property and navigation property is represented
// as a name/value pair of the standard OpenAPI properties object.
Properties = model.CreateStrucutredTypePropertiesSchema(structuredType),
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the structured type.
// However, ODL doesn't support the Core.Description on structure type.
};
}
}
// 4.6.1.1 Properties
public static IDictionary<string, OpenApiSchema> CreateStrucutredTypePropertiesSchema(this IEdmModel model, IEdmStructuredType structuredType)
{
// The name is the property name, the value is a Schema Object describing the allowed values of the property.
IDictionary<string, OpenApiSchema> properties = new Dictionary<string, OpenApiSchema>();
// structure properties
foreach (var property in structuredType.DeclaredStructuralProperties())
{
// OpenApiSchema propertySchema = property.Type.CreateSchema();
// propertySchema.Default = property.DefaultValueString != null ? new OpenApiString(property.DefaultValueString) : null;
properties.Add(property.Name, property.CreatePropertySchema());
}
// navigation properties
foreach (var property in structuredType.DeclaredNavigationProperties())
{
OpenApiSchema propertySchema = property.Type.CreateSchema();
properties.Add(property.Name, propertySchema);
}
return properties;
}
public static OpenApiSchema CreateEnumTypeSchema(this IEdmModel model, IEdmEnumType enumType)
{
OpenApiSchema schema = new OpenApiSchema
{
// An enumeration type is represented as a Schema Object of type string
Type = "string",
// containing the OpenAPI Specification enum keyword.
Enum = new List<IOpenApiAny>(),
// It optionally can contain the field description,
// whose value is the value of the unqualified annotation Core.Description of the enumeration type.
Description = model.GetDescription(enumType)
};
// Enum value is an array that contains a string with the member name for each enumeration member.
foreach (IEdmEnumMember member in enumType.Members)
{
schema.Enum.Add(new OpenApiString(member.Name));
}
schema.Title = (enumType as IEdmSchemaElement)?.Name;
return schema;
}
public static OpenApiSchema CreateTypeDefinitionSchema(this IEdmModel model, IEdmTypeDefinition typeDefinition)
{
return typeDefinition?.UnderlyingType?.CreateSchema();
}
public static OpenApiSchema CreatePropertySchema(this IEdmStructuralProperty property)
{
if (property == null)

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<AssemblyName>Microsoft.OpenApi.OData.Reader</AssemblyName>
<RootNamespace>Microsoft.OpenApi.OData.Reader</RootNamespace>
<RootNamespace>Microsoft.OpenApi.OData</RootNamespace>
<TargetFrameworks>net46; netstandard2.0</TargetFrameworks>
<Company>Microsoft</Company>
<Product>Microsoft.OpenApi.OData.Reader</Product>

View file

@ -39,7 +39,7 @@ namespace Microsoft.OpenApi.OData.Properties {
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.OData.OpenApi.Properties.SRResource", typeof(SRResource).Assembly);
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.OpenApi.OData.Reader.Properties.SRResource", typeof(SRResource).Assembly);
resourceMan = temp;
}
return resourceMan;
@ -78,6 +78,15 @@ namespace Microsoft.OpenApi.OData.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Not supported {0} Edm type kind..
/// </summary>
internal static string NotSupportedEdmTypeKind {
get {
return ResourceManager.GetString("NotSupportedEdmTypeKind", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An error occurred while processing the OData message..
/// </summary>

View file

@ -123,6 +123,9 @@
<data name="ExtensionFieldNameMustBeginWithXMinus" xml:space="preserve">
<value>The filed name of extension doesn't begin with x-.</value>
</data>
<data name="NotSupportedEdmTypeKind" xml:space="preserve">
<value>Not supported {0} Edm type kind.</value>
</data>
<data name="OpenApiExceptionGeneralError" xml:space="preserve">
<value>An error occurred while processing the OData message.</value>
</data>

View file

@ -41,11 +41,16 @@ namespace Microsoft.OpenApi.OData.Tests
{
var model = new EdmModel();
var coreDescription = CoreVocabularyModel.DescriptionTerm;
var enumType = new EdmEnumType("DefaultNs", "Color");
var blue = enumType.AddMember("Blue", new EdmEnumMemberValue(0));
enumType.AddMember("White", new EdmEnumMemberValue(1));
model.AddElement(enumType);
var annotation = new EdmVocabularyAnnotation(enumType, coreDescription, new EdmStringConstant("Enum type 'Color' description."));
model.AddVocabularyAnnotation(annotation);
var person = new EdmEntityType("DefaultNs", "Person");
var entityId = person.AddStructuralProperty("UserName", EdmCoreModel.Instance.GetString(false));
person.AddKeys(entityId);
@ -103,8 +108,7 @@ namespace Microsoft.OpenApi.OData.Tests
entityContainer.AddElement(countriesOrRegions);
entityContainer.AddElement(me);
var coreDescription = CoreVocabularyModel.DescriptionTerm;
var annotation = new EdmVocabularyAnnotation(people, coreDescription, new EdmStringConstant("People's description."));
annotation = new EdmVocabularyAnnotation(people, coreDescription, new EdmStringConstant("People's description."));
model.AddVocabularyAnnotation(annotation);
var coreLongDescription = CoreVocabularyModel.LongDescriptionTerm;

View file

@ -0,0 +1,28 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using Microsoft.OpenApi.OData.Tests;
using Xunit;
namespace Microsoft.OpenApi.OData.Generator.Tests
{
public class ODataContextTest
{
[Fact]
public void CtorThrowArgumentNullModel()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>("model", () => new ODataContext(model: null));
}
[Fact]
public void CtorThrowArgumentNullsetting()
{
// Arrange & Act & Assert
Assert.Throws<ArgumentNullException>("settings", () => new ODataContext(EdmModelHelper.EmptyModel, settings: null));
}
}
}

View file

@ -14,8 +14,6 @@ namespace Microsoft.OpenApi.OData.Generator.Tests
{
public class OpenApiParameterGeneratorTest
{
private OpenApiConvertSettings _settings = new OpenApiConvertSettings();
[Fact]
public void CreateParametersThrowArgumentNullContext()
{

View file

@ -3,7 +3,11 @@
// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
// ------------------------------------------------------------
using System;
using System.Linq;
using Microsoft.OData.Edm;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.OData.Generator;
using Xunit;
using Xunit.Abstractions;
@ -18,6 +22,76 @@ namespace Microsoft.OpenApi.OData.Tests
_output = output;
}
[Fact]
public void CreateSchemasThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateSchemas());
}
#region EnumTypeSchema
[Fact]
public void CreateEnumTypeThrowArgumentNullContext()
{
// Arrange
ODataContext context = null;
// Act & Assert
Assert.Throws<ArgumentNullException>("context", () => context.CreateEnumTypeSchema(enumType: null));
}
[Fact]
public void CreateEnumTypeThrowArgumentNullEnumType()
{
// Arrange
ODataContext context = new ODataContext(EdmCoreModel.Instance);
// Act & Assert
Assert.Throws<ArgumentNullException>("enumType", () => context.CreateEnumTypeSchema(enumType: null));
}
[Fact]
public void CreateEnumTypeReturnCorrectSchema()
{
// Arrange
IEdmModel model = EdmModelHelper.BasicEdmModel;
ODataContext context = new ODataContext(model);
IEdmEnumType enumType = model.SchemaElements.OfType<IEdmEnumType>().First(t => t.Name == "Color");
Assert.NotNull(enumType); // Guard
// Act
var schema = context.CreateEnumTypeSchema(enumType);
// Assert
Assert.NotNull(schema);
Assert.Equal("string", schema.Type);
Assert.Equal("Enum type 'Color' description.", schema.Description);
Assert.Equal("Color", schema.Title);
Assert.NotNull(schema.Enum);
Assert.Equal(2, schema.Enum.Count);
Assert.Equal(new string[] { "Blue", "White" }, schema.Enum.Select(e => ((OpenApiString)e).Value));
// Act
string json = schema.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0_0);
// Act
Assert.NotNull(json);
Assert.Equal(@"{
""title"": ""Color"",
""enum"": [
""Blue"",
""White""
],
""type"": ""string"",
""description"": ""Enum type 'Color' description.""
}".Replace(), json);
}
#endregion
[Fact]
public void NonNullableBooleanPropertyWithDefaultValueWorks()
{

View file

@ -895,7 +895,8 @@
"Blue",
"White"
],
"type": "string"
"type": "string",
"description": "Enum type 'Color' description."
},
"DefaultNs.Person": {
"title": "Person",

View file

@ -566,6 +566,7 @@ components:
- Blue
- White
type: string
description: Enum type 'Color' description.
DefaultNs.Person:
title: Person
type: object