Skip to content

Commit

Permalink
Explicitly specified string must stay a string
Browse files Browse the repository at this point in the history
  • Loading branch information
VitaliyKurokhtin committed Nov 18, 2019
1 parent 578e587 commit 27a2e96
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 157 deletions.
255 changes: 104 additions & 151 deletions src/Microsoft.OpenApi.Readers/ParseNodes/OpenApiAnyConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,95 +6,19 @@
using System.Linq;
using System.Text;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
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 most specific <see cref="IOpenApiPrimitive"/> type based on the value.
/// </summary>
public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny)
{
if (openApiAny is OpenApiArray openApiArray)
{
var newArray = new OpenApiArray();
foreach (var element in openApiArray)
{
newArray.Add(GetSpecificOpenApiAny(element));
}

return newArray;
}

if (openApiAny is OpenApiObject openApiObject)
{
var newObject = new OpenApiObject();

foreach (var key in openApiObject.Keys.ToList())
{
newObject[key] = GetSpecificOpenApiAny(openApiObject[key]);
}

return newObject;
}

if ( !(openApiAny is OpenApiString))
{
return openApiAny;
}

var value = ((OpenApiString)openApiAny).Value;

if (value == null || value == "null")
{
return new OpenApiNull();
}

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);
}

// if we can't identify the type of value, return it as string.
return new OpenApiString(value);
}

/// <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)
public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiSchema schema = null)
{
if (openApiAny is OpenApiArray openApiArray)
{
Expand All @@ -113,9 +37,9 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS

foreach (var key in openApiObject.Keys.ToList())
{
if ( schema != null && schema.Properties != null && schema.Properties.ContainsKey(key) )
if (schema?.Properties != null && schema.Properties.TryGetValue(key, out var property))
{
newObject[key] = GetSpecificOpenApiAny(openApiObject[key], schema.Properties[key]);
newObject[key] = GetSpecificOpenApiAny(openApiObject[key], property);
}
else
{
Expand All @@ -126,133 +50,162 @@ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiS
return newObject;
}

if (!(openApiAny is OpenApiString))
if (!(openApiAny is OpenApiString) || ((OpenApiString)openApiAny).IsExplicit())
{
return openApiAny;
}

if (schema?.Type == null)
{
return GetSpecificOpenApiAny(openApiAny);
}

var type = schema.Type;
var format = schema.Format;

var value = ((OpenApiString)openApiAny).Value;

if (value == null || value == "null")
{
return new OpenApiNull();
}

if (type == "integer" && format == "int32")
if (schema?.Type == null)
{
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
if (value == "true")
{
return new OpenApiInteger(intValue);
return new OpenApiBoolean(true);
}
}

if (type == "integer" && format == "int64")
{
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
if (value == "false")
{
return new OpenApiLong(longValue);
return new OpenApiBoolean(false);
}
}

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))
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
{
return new OpenApiFloat(floatValue);
return new OpenApiLong(longValue);
}
}

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))
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
{
return new OpenApiDouble(doubleValue);
return new OpenApiDateTime(dateTimeValue);
}
}

if (type == "string" && format == "byte")
else
{
try
var type = schema.Type;
var format = schema.Format;

if (type == "integer" && format == "int32")
{
return new OpenApiByte(Convert.FromBase64String(value));
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
{
return new OpenApiInteger(intValue);
}
}
catch(FormatException)
{ }
}

// binary
if (type == "string" && format == "binary")
{
try
if (type == "integer" && format == "int64")
{
return new OpenApiBinary(Encoding.UTF8.GetBytes(value));
if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
{
return new OpenApiLong(longValue);
}
}
catch(EncoderFallbackException)
{ }
}

if (type == "string" && format == "date")
{
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue))
if (type == "integer")
{
return new OpenApiDate(dateValue.Date);
if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
{
return new OpenApiInteger(intValue);
}
}
}

if (type == "string" && format == "date-time")
{
if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
if (type == "number" && format == "float")
{
return new OpenApiDateTime(dateTimeValue);
if (float.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var floatValue))
{
return new OpenApiFloat(floatValue);
}
}
}

if (type == "string" && format == "password")
{
return new OpenApiPassword(value);
}
if (type == "number" && format == "double")
{
if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
{
return new OpenApiDouble(doubleValue);
}
}

if (type == "string")
{
return new OpenApiString(value);
}
if (type == "number")
{
if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
{
return new OpenApiDouble(doubleValue);
}
}

if (type == "boolean")
{
if (bool.TryParse(value, out var booleanValue))
if (type == "string" && format == "byte")
{
try
{
return new OpenApiByte(Convert.FromBase64String(value));
}
catch (FormatException)
{ }
}

// binary
if (type == "string" && format == "binary")
{
return new OpenApiBoolean(booleanValue);
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 == "password")
{
return new OpenApiPassword(value);
}

if (type == "string")
{
return new OpenApiString(value);
}

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 new OpenApiString(value);
return openApiAny;
}
}
}
5 changes: 2 additions & 3 deletions src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Globalization;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Readers.Exceptions;
using SharpYaml;
using SharpYaml.Serialization;

namespace Microsoft.OpenApi.Readers.ParseNodes
Expand Down Expand Up @@ -36,7 +35,7 @@ public override string GetScalarValue()
public override IOpenApiAny CreateAny()
{
var value = GetScalarValue();
return new OpenApiString(value);
return new OpenApiString(value, this._node.Style == ScalarStyle.SingleQuoted || this._node.Style == ScalarStyle.DoubleQuoted || this._node.Style == ScalarStyle.Literal || this._node.Style == ScalarStyle.Folded);
}
}
}
13 changes: 12 additions & 1 deletion src/Microsoft.OpenApi/Any/OpenApiString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,29 @@ namespace Microsoft.OpenApi.Any
/// </summary>
public class OpenApiString : OpenApiPrimitive<string>
{
private bool isExplicit;

/// <summary>
/// Initializes the <see cref="OpenApiString"/> class.
/// </summary>
/// <param name="value"></param>
public OpenApiString(string value)
public OpenApiString(string value, bool isExplicit = false)
: base(value)
{
this.isExplicit = isExplicit;
}

/// <summary>
/// The primitive class this object represents.
/// </summary>
public override PrimitiveType PrimitiveType { get; } = PrimitiveType.String;

/// <summary>
/// True if string was specified explicitly by the means of double quotes, single quotes, or literal or folded style.
/// </summary>
public bool IsExplicit()
{
return this.isExplicit;
}
}
}
Loading

0 comments on commit 27a2e96

Please sign in to comment.