Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions IOpenApiPrimitive.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

namespace Microsoft.OpenApi.Any
{
/// <summary>
/// Primitive type.
/// </summary>
public enum PrimitiveType
{
/// <summary>
/// Integer
/// </summary>
Integer,

/// <summary>
/// Long
/// </summary>
Long,

/// <summary>
/// Float
/// </summary>
Float,

/// <summary>
/// Double
/// </summary>
Double,

/// <summary>
/// String
/// </summary>
String,

/// <summary>
/// Byte
/// </summary>
Byte,

/// <summary>
/// Binary
/// </summary>
Binary,

/// <summary>
/// Boolean
/// </summary>
Boolean,

/// <summary>
/// Date
/// </summary>
Date,

/// <summary>
/// DateTime
/// </summary>
DateTime,

/// <summary>
/// Time
/// </summary>
TimeSpan,

/// <summary>
/// Password
/// </summary>
Password
}

/// <summary>
/// Base interface for the Primitive type.
/// </summary>
public interface IOpenApiPrimitive : IOpenApiAny
{
/// <summary>
/// Primitive type.
/// </summary>
PrimitiveType PrimitiveType { get; }
}
}
284 changes: 284 additions & 0 deletions OpenApiAnyConverter.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Converts the <see cref="OpenApiString"/>s in the given <see cref="IOpenApiAny"/>
/// into the appropriate <see cref="IOpenApiPrimitive"/> type based on the given <see cref="OpenApiSchema"/>.
/// For those strings that the schema does not specify the type for, convert them into
/// the most specific type based on the value.
/// </summary>
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;
}
}
}
Loading