diff --git a/IOpenApiPrimitive.cs b/IOpenApiPrimitive.cs new file mode 100644 index 000000000..d367b0c75 --- /dev/null +++ b/IOpenApiPrimitive.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +namespace Microsoft.OpenApi.Any +{ + /// + /// Primitive type. + /// + public enum PrimitiveType + { + /// + /// Integer + /// + Integer, + + /// + /// Long + /// + Long, + + /// + /// Float + /// + Float, + + /// + /// Double + /// + Double, + + /// + /// String + /// + String, + + /// + /// Byte + /// + Byte, + + /// + /// Binary + /// + Binary, + + /// + /// Boolean + /// + Boolean, + + /// + /// Date + /// + Date, + + /// + /// DateTime + /// + DateTime, + + /// + /// Time + /// + TimeSpan, + + /// + /// Password + /// + Password + } + + /// + /// Base interface for the Primitive type. + /// + public interface IOpenApiPrimitive : IOpenApiAny + { + /// + /// Primitive type. + /// + PrimitiveType PrimitiveType { get; } + } +} diff --git a/OpenApiAnyConverter.cs b/OpenApiAnyConverter.cs new file mode 100644 index 000000000..2aa283a4c --- /dev/null +++ b/OpenApiAnyConverter.cs @@ -0,0 +1,284 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Readers.ParseNodes +{ + internal static class OpenApiAnyConverter + { + /// + /// Converts the s in the given + /// into the appropriate type based on the given . + /// For those strings that the schema does not specify the type for, convert them into + /// the most specific type based on the value. + /// + public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiSchema schema = null) + { + if (openApiAny is OpenApiArray openApiArray) + { + var newArray = new OpenApiArray(); + foreach (var element in openApiArray) + { + newArray.Add(GetSpecificOpenApiAny(element, schema?.Items)); + } + + return newArray; + } + + if (openApiAny is OpenApiObject openApiObject) + { + var newObject = new OpenApiObject(); + + foreach (var key in openApiObject.Keys.ToList()) + { + if (schema?.Properties != null && schema.Properties.TryGetValue(key, out var property)) + { + newObject[key] = GetSpecificOpenApiAny(openApiObject[key], property); + } + else + { + newObject[key] = GetSpecificOpenApiAny(openApiObject[key], schema?.AdditionalProperties); + } + } + + return newObject; + } + + if (!(openApiAny is OpenApiString)) + { + return openApiAny; + } + + var value = ((OpenApiString)openApiAny).Value; + var type = schema?.Type; + var format = schema?.Format; + + if (((OpenApiString)openApiAny).IsExplicit()) + { + // More narrow type detection for explicit strings, only check types that are passed as strings + if (schema == null) + { + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) + { + return new OpenApiDateTime(dateTimeValue); + } + } + else if (type == "string") + { + if (format == "byte") + { + try + { + return new OpenApiByte(Convert.FromBase64String(value)); + } + catch (FormatException) + { } + } + + if (format == "binary") + { + try + { + return new OpenApiBinary(Encoding.UTF8.GetBytes(value)); + } + catch (EncoderFallbackException) + { } + } + + if (format == "date") + { + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue)) + { + return new OpenApiDate(dateValue.Date); + } + } + + if (format == "date-time") + { + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) + { + return new OpenApiDateTime(dateTimeValue); + } + } + + if (format == "date-span") + { + if (TimeSpan.TryParse(value, out var timeSpanValue)) + { + return new OpenApiTimeSpan(timeSpanValue); + } + } + + if (format == "password") + { + return new OpenApiPassword(value); + } + } + + return openApiAny; + } + + if (value == null || value == "null") + { + return new OpenApiNull(); + } + + if (schema?.Type == null) + { + if (value == "true") + { + return new OpenApiBoolean(true); + } + + if (value == "false") + { + return new OpenApiBoolean(false); + } + + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + { + return new OpenApiInteger(intValue); + } + + if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue)) + { + return new OpenApiLong(longValue); + } + + if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) + { + return new OpenApiDouble(doubleValue); + } + + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) + { + return new OpenApiDateTime(dateTimeValue); + } + } + else + { + if (type == "integer" && format == "int32") + { + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + { + return new OpenApiInteger(intValue); + } + } + + if (type == "integer" && format == "int64") + { + if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue)) + { + return new OpenApiLong(longValue); + } + } + + if (type == "integer") + { + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) + { + return new OpenApiInteger(intValue); + } + } + + if (type == "number" && format == "float") + { + if (float.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var floatValue)) + { + return new OpenApiFloat(floatValue); + } + } + + if (type == "number" && format == "double") + { + if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) + { + return new OpenApiDouble(doubleValue); + } + } + + if (type == "number") + { + if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) + { + return new OpenApiDouble(doubleValue); + } + } + + if (type == "string" && format == "byte") + { + try + { + return new OpenApiByte(Convert.FromBase64String(value)); + } + catch (FormatException) + { } + } + + // binary + if (type == "string" && format == "binary") + { + try + { + return new OpenApiBinary(Encoding.UTF8.GetBytes(value)); + } + catch (EncoderFallbackException) + { } + } + + if (type == "string" && format == "date") + { + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue)) + { + return new OpenApiDate(dateValue.Date); + } + } + + if (type == "string" && format == "date-time") + { + if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) + { + return new OpenApiDateTime(dateTimeValue); + } + } + + if (type == "string" && format == "date-span") + { + if (TimeSpan.TryParse(value, out var timeSpanValue)) + { + return new OpenApiTimeSpan(timeSpanValue); + } + } + + if (type == "string" && format == "password") + { + return new OpenApiPassword(value); + } + + if (type == "string") + { + return openApiAny; + } + + if (type == "boolean") + { + if (bool.TryParse(value, out var booleanValue)) + { + return new OpenApiBoolean(booleanValue); + } + } + } + + // If data conflicts with the given type, return a string. + // This converter is used in the parser, so it does not perform any validations, + // but the validator can be used to validate whether the data and given type conflicts. + return openApiAny; + } + } +} diff --git a/OpenApiAnyConverterTests.cs b/OpenApiAnyConverterTests.cs new file mode 100644 index 000000000..4c9424642 --- /dev/null +++ b/OpenApiAnyConverterTests.cs @@ -0,0 +1,524 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers.ParseNodes; +using SharpYaml.Serialization; +using Xunit; + +namespace Microsoft.OpenApi.Readers.Tests.ParseNodes +{ + [Collection("DefaultSettings")] + public class OpenApiAnyConverterTests + { + [Fact] + public void ParseObjectAsAnyShouldSucceed() + { + var input = @" +aString: fooBar +aInteger: 10 +aDouble: 2.34 +aDateTime: 2017-01-01 +aTimeSpan: 01:00:00 +aDate: 2017-01-02 + "; + var yamlStream = new YamlStream(); + yamlStream.Load(new StringReader(input)); + var yamlNode = yamlStream.Documents.First().RootNode; + + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); + + var node = new MapNode(context, (YamlMappingNode)yamlNode); + + var anyMap = node.CreateAny(); + + var schema = new OpenApiSchema() + { + Type = "object", + Properties = + { + ["aString"] = new OpenApiSchema() + { + Type = "string" + }, + ["aInteger"] = new OpenApiSchema() + { + Type = "integer", + Format = "int32" + }, + ["aDouble"] = new OpenApiSchema() + { + Type = "number", + Format = "double" + }, + ["aDateTime"] = new OpenApiSchema() + { + Type = "string", + Format = "date-time" + }, + ["aTimeSpan"] = new OpenApiSchema() + { + Type = "string", + Format = "date-span" + }, + ["aDate"] = new OpenApiSchema() + { + Type = "string", + Format = "date" + } + } + }; + + anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema); + + diagnostic.Errors.Should().BeEmpty(); + + anyMap.Should().BeEquivalentTo( + new OpenApiObject + { + ["aString"] = new OpenApiString("fooBar"), + ["aInteger"] = new OpenApiInteger(10), + ["aDouble"] = new OpenApiDouble(2.34), + ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture)), + ["aTimeSpan"] = new OpenApiTimeSpan(TimeSpan.FromHours(1)), + ["aDate"] = new OpenApiDate(DateTimeOffset.Parse("2017-01-02", CultureInfo.InvariantCulture).Date), + }); + } + + + [Fact] + public void ParseNestedObjectAsAnyShouldSucceed() + { + var input = @" + aString: fooBar + aInteger: 10 + aArray: + - 1 + - 2 + - 3 + aNestedArray: + - aFloat: 1 + aPassword: 1234 + aArray: [abc, def] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty2: 2 + - aFloat: 1.6 + aArray: [123] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty3: 20 + aObject: + aDate: 2017-02-03 + aDouble: 2.34 + aDateTime: 2017-01-01 + "; + var yamlStream = new YamlStream(); + yamlStream.Load(new StringReader(input)); + var yamlNode = yamlStream.Documents.First().RootNode; + + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); + + var node = new MapNode(context, (YamlMappingNode)yamlNode); + + var anyMap = node.CreateAny(); + + var schema = new OpenApiSchema() + { + Type = "object", + Properties = + { + ["aString"] = new OpenApiSchema() + { + Type = "string" + }, + ["aInteger"] = new OpenApiSchema() + { + Type = "integer", + Format = "int32" + }, + ["aArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "integer", + Format = "int64" + } + }, + ["aNestedArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "object", + Properties = + { + ["aFloat"] = new OpenApiSchema() + { + Type = "number", + Format = "float" + }, + ["aPassword"] = new OpenApiSchema() + { + Type = "string", + Format = "password" + }, + ["aArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "string", + } + }, + ["aDictionary"] = new OpenApiSchema() + { + Type = "object", + AdditionalProperties = new OpenApiSchema() + { + Type = "integer", + Format = "int64" + } + } + } + } + }, + ["aObject"] = new OpenApiSchema() + { + Type = "array", + Properties = + { + ["aDate"] = new OpenApiSchema() + { + Type = "string", + Format = "date" + } + } + }, + ["aDouble"] = new OpenApiSchema() + { + Type = "number", + Format = "double" + }, + ["aDateTime"] = new OpenApiSchema() + { + Type = "string", + Format = "date-time" + } + } + }; + + anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema); + + diagnostic.Errors.Should().BeEmpty(); + + anyMap.Should().BeEquivalentTo( + new OpenApiObject + { + ["aString"] = new OpenApiString("fooBar"), + ["aInteger"] = new OpenApiInteger(10), + ["aArray"] = new OpenApiArray() + { + new OpenApiLong(1), + new OpenApiLong(2), + new OpenApiLong(3), + }, + ["aNestedArray"] = new OpenApiArray() + { + new OpenApiObject() + { + ["aFloat"] = new OpenApiFloat(1), + ["aPassword"] = new OpenApiPassword("1234"), + ["aArray"] = new OpenApiArray() + { + new OpenApiString("abc"), + new OpenApiString("def") + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiLong(1), + ["arbitraryProperty2"] = new OpenApiLong(2), + } + }, + new OpenApiObject() + { + ["aFloat"] = new OpenApiFloat((float)1.6), + ["aArray"] = new OpenApiArray() + { + new OpenApiString("123"), + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiLong(1), + ["arbitraryProperty3"] = new OpenApiLong(20), + } + } + }, + ["aObject"] = new OpenApiObject() + { + ["aDate"] = new OpenApiDate(DateTimeOffset.Parse("2017-02-03", CultureInfo.InvariantCulture).Date) + }, + ["aDouble"] = new OpenApiDouble(2.34), + ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture)) + }); + } + + + [Fact] + public void ParseNestedObjectAsAnyWithPartialSchemaShouldSucceed() + { + var input = @" + aString: fooBar + aInteger: 10 + aArray: + - 1 + - 2 + - 3 + aNestedArray: + - aFloat: 1 + aPassword: 1234 + aArray: [abc, def] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty2: 2 + - aFloat: 1.6 + aArray: [123] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty3: 20 + aObject: + aDate: 2017-02-03 + aDouble: 2.34 + aDateTime: 2017-01-01 + "; + var yamlStream = new YamlStream(); + yamlStream.Load(new StringReader(input)); + var yamlNode = yamlStream.Documents.First().RootNode; + + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); + + var node = new MapNode(context, (YamlMappingNode)yamlNode); + + var anyMap = node.CreateAny(); + + var schema = new OpenApiSchema() + { + Type = "object", + Properties = + { + ["aString"] = new OpenApiSchema() + { + Type = "string" + }, + ["aArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "integer" + } + }, + ["aNestedArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "object", + Properties = + { + ["aFloat"] = new OpenApiSchema() + { + }, + ["aPassword"] = new OpenApiSchema() + { + }, + ["aArray"] = new OpenApiSchema() + { + Type = "array", + Items = new OpenApiSchema() + { + Type = "string", + } + } + } + } + }, + ["aObject"] = new OpenApiSchema() + { + Type = "array", + Properties = + { + ["aDate"] = new OpenApiSchema() + { + Type = "string" + } + } + }, + ["aDouble"] = new OpenApiSchema() + { + }, + ["aDateTime"] = new OpenApiSchema() + { + } + } + }; + + anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema); + + diagnostic.Errors.Should().BeEmpty(); + + anyMap.Should().BeEquivalentTo( + new OpenApiObject + { + ["aString"] = new OpenApiString("fooBar"), + ["aInteger"] = new OpenApiInteger(10), + ["aArray"] = new OpenApiArray() + { + new OpenApiInteger(1), + new OpenApiInteger(2), + new OpenApiInteger(3), + }, + ["aNestedArray"] = new OpenApiArray() + { + new OpenApiObject() + { + ["aFloat"] = new OpenApiInteger(1), + ["aPassword"] = new OpenApiInteger(1234), + ["aArray"] = new OpenApiArray() + { + new OpenApiString("abc"), + new OpenApiString("def") + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiInteger(1), + ["arbitraryProperty2"] = new OpenApiInteger(2), + } + }, + new OpenApiObject() + { + ["aFloat"] = new OpenApiDouble(1.6), + ["aArray"] = new OpenApiArray() + { + new OpenApiString("123"), + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiInteger(1), + ["arbitraryProperty3"] = new OpenApiInteger(20), + } + } + }, + ["aObject"] = new OpenApiObject() + { + ["aDate"] = new OpenApiString("2017-02-03") + }, + ["aDouble"] = new OpenApiDouble(2.34), + ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture)) + }); + } + + [Fact] + public void ParseNestedObjectAsAnyWithoutUsingSchemaShouldSucceed() + { + var input = @" + aString: fooBar + aInteger: 10 + aArray: + - 1 + - 2 + - 3 + aNestedArray: + - aFloat: 1 + aPassword: 1234 + aArray: [abc, def] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty2: 2 + - aFloat: 1.6 + aArray: [123] + aDictionary: + arbitraryProperty: 1 + arbitraryProperty3: 20 + aObject: + aDate: 2017-02-03 + aDouble: 2.34 + aDateTime: 2017-01-01 + "; + var yamlStream = new YamlStream(); + yamlStream.Load(new StringReader(input)); + var yamlNode = yamlStream.Documents.First().RootNode; + + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); + + var node = new MapNode(context, (YamlMappingNode)yamlNode); + + var anyMap = node.CreateAny(); + + anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap); + + diagnostic.Errors.Should().BeEmpty(); + + anyMap.Should().BeEquivalentTo( + new OpenApiObject + { + ["aString"] = new OpenApiString("fooBar"), + ["aInteger"] = new OpenApiInteger(10), + ["aArray"] = new OpenApiArray() + { + new OpenApiInteger(1), + new OpenApiInteger(2), + new OpenApiInteger(3), + }, + ["aNestedArray"] = new OpenApiArray() + { + new OpenApiObject() + { + ["aFloat"] = new OpenApiInteger(1), + ["aPassword"] = new OpenApiInteger(1234), + ["aArray"] = new OpenApiArray() + { + new OpenApiString("abc"), + new OpenApiString("def") + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiInteger(1), + ["arbitraryProperty2"] = new OpenApiInteger(2), + } + }, + new OpenApiObject() + { + ["aFloat"] = new OpenApiDouble(1.6), + ["aArray"] = new OpenApiArray() + { + new OpenApiInteger(123), + }, + ["aDictionary"] = new OpenApiObject() + { + ["arbitraryProperty"] = new OpenApiInteger(1), + ["arbitraryProperty3"] = new OpenApiInteger(20), + } + } + }, + ["aObject"] = new OpenApiObject() + { + ["aDate"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-02-03", CultureInfo.InvariantCulture)) + }, + ["aDouble"] = new OpenApiDouble(2.34), + ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture)) + }); + } + } +} diff --git a/OpenApiPrimitive.cs b/OpenApiPrimitive.cs new file mode 100644 index 000000000..8439262f6 --- /dev/null +++ b/OpenApiPrimitive.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Text; +using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Writers; + +namespace Microsoft.OpenApi.Any +{ + /// + /// Open API primitive class. + /// + /// + public abstract class OpenApiPrimitive : IOpenApiPrimitive + { + /// + /// Initializes the class with the given value. + /// + /// + public OpenApiPrimitive(T value) + { + Value = value; + } + + /// + /// Initializes a copy of an object + /// + /// + public OpenApiPrimitive(OpenApiPrimitive openApiPrimitive) + { + Value = openApiPrimitive.Value; + } + + /// + /// The kind of . + /// + public AnyType AnyType { get; } = AnyType.Primitive; + + /// + /// The primitive class this object represents. + /// + public abstract PrimitiveType PrimitiveType { get; } + + /// + /// Value of this + /// + public T Value { get; } + + /// + /// Write out content of primitive element + /// + /// + /// + public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) + { + switch (this.PrimitiveType) + { + case PrimitiveType.Integer: + var intValue = (OpenApiInteger)(IOpenApiPrimitive)this; + writer.WriteValue(intValue.Value); + break; + + case PrimitiveType.Long: + var longValue = (OpenApiLong)(IOpenApiPrimitive)this; + writer.WriteValue(longValue.Value); + break; + + case PrimitiveType.Float: + var floatValue = (OpenApiFloat)(IOpenApiPrimitive)this; + writer.WriteValue(floatValue.Value); + break; + + case PrimitiveType.Double: + var doubleValue = (OpenApiDouble)(IOpenApiPrimitive)this; + writer.WriteValue(doubleValue.Value); + break; + + case PrimitiveType.String: + var stringValue = (OpenApiString)(IOpenApiPrimitive)this; + if (stringValue.IsRawString()) + writer.WriteRaw(stringValue.Value); + else + writer.WriteValue(stringValue.Value); + break; + + case PrimitiveType.Byte: + var byteValue = (OpenApiByte)(IOpenApiPrimitive)this; + if (byteValue.Value == null) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(Convert.ToBase64String(byteValue.Value)); + } + + break; + + case PrimitiveType.Binary: + var binaryValue = (OpenApiBinary)(IOpenApiPrimitive)this; + if (binaryValue.Value == null) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(Encoding.UTF8.GetString(binaryValue.Value)); + } + + break; + + case PrimitiveType.Boolean: + var boolValue = (OpenApiBoolean)(IOpenApiPrimitive)this; + writer.WriteValue(boolValue.Value); + break; + + case PrimitiveType.Date: + var dateValue = (OpenApiDate)(IOpenApiPrimitive)this; + writer.WriteValue(dateValue.Value); + break; + + case PrimitiveType.DateTime: + var dateTimeValue = (OpenApiDateTime)(IOpenApiPrimitive)this; + writer.WriteValue(dateTimeValue.Value); + break; + + case PrimitiveType.TimeSpan: + var timeSpanValue = (OpenApiTimeSpan)(IOpenApiPrimitive)this; + writer.WriteValue(timeSpanValue.Value); + break; + + case PrimitiveType.Password: + var passwordValue = (OpenApiPassword)(IOpenApiPrimitive)this; + writer.WriteValue(passwordValue.Value); + break; + + default: + throw new OpenApiWriterException( + string.Format( + SRResource.PrimitiveTypeNotSupported, + this.PrimitiveType)); + } + + } + } +} diff --git a/OpenApiTimeSpan.cs b/OpenApiTimeSpan.cs new file mode 100644 index 000000000..c807a6a01 --- /dev/null +++ b/OpenApiTimeSpan.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.OpenApi.Any +{ + /// + /// Open API TimeSpan + /// + public class OpenApiTimeSpan : OpenApiPrimitive + { + /// + /// Initializes the class. + /// + public OpenApiTimeSpan(TimeSpan value) + : base(value) + { + } + + /// + /// Primitive type this object represents. + /// + public override PrimitiveType PrimitiveType { get; } = PrimitiveType.TimeSpan; + } +}