Skip to content

Commit d2fb8ba

Browse files
committed
For value nodes, we first load OpenApiAny in as OpenApiString, then we'll try to convert.
- If type exists, then respect the type. If data conflicts with the type, throw an error. - If type doesn't exist, use best effort.
1 parent e8f55b1 commit d2fb8ba

File tree

10 files changed

+516
-70
lines changed

10 files changed

+516
-70
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using Microsoft.OpenApi.Any;
7+
using Microsoft.OpenApi.Models;
8+
9+
namespace Microsoft.OpenApi.Readers.ParseNodes
10+
{
11+
internal class AnyFieldMap<T> : Dictionary<string, AnyFieldMapParameter<T>>
12+
{
13+
14+
}
15+
16+
internal class AnyFieldMapParameter<T>
17+
{
18+
public AnyFieldMapParameter(
19+
Func<T, IOpenApiAny> propertyGetter,
20+
Func<T, IOpenApiAny, IOpenApiAny> propertySetter,
21+
Func<T, OpenApiSchema> schemaGetter)
22+
{
23+
this.PropertyGetter = propertyGetter;
24+
this.PropertySetter = propertySetter;
25+
this.SchemaGetter = schemaGetter;
26+
}
27+
28+
public Func<T, IOpenApiAny> PropertyGetter { get; }
29+
30+
public Func<T, IOpenApiAny, IOpenApiAny> PropertySetter { get; }
31+
32+
public Func<T, OpenApiSchema> SchemaGetter { get; }
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using Microsoft.OpenApi.Any;
7+
using Microsoft.OpenApi.Models;
8+
9+
namespace Microsoft.OpenApi.Readers.ParseNodes
10+
{
11+
internal class AnyListFieldMap<T> : Dictionary<string, AnyListFieldMapParameter<T>>
12+
{
13+
14+
}
15+
16+
internal class AnyListFieldMapParameter<T>
17+
{
18+
public AnyListFieldMapParameter(
19+
Func<T, IList<IOpenApiAny>> propertyGetter,
20+
Func<T, IList<IOpenApiAny>, IList<IOpenApiAny>> propertySetter,
21+
Func<T, OpenApiSchema> schemaGetter)
22+
{
23+
this.PropertyGetter = propertyGetter;
24+
this.PropertySetter = propertySetter;
25+
this.SchemaGetter = schemaGetter;
26+
}
27+
28+
public Func<T, IList<IOpenApiAny>> PropertyGetter { get; }
29+
30+
public Func<T, IList<IOpenApiAny>, IList<IOpenApiAny>> PropertySetter { get; }
31+
32+
public Func<T, OpenApiSchema> SchemaGetter { get; }
33+
}
34+
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
using System;
2+
using System.Linq;
3+
using Microsoft.OpenApi.Any;
4+
using Microsoft.OpenApi.Exceptions;
5+
using Microsoft.OpenApi.Models;
6+
7+
namespace Microsoft.OpenApi.Readers.ParseNodes
8+
{
9+
internal static class OpenApiStringConverter
10+
{
11+
public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny)
12+
{
13+
if ( !(openApiAny is OpenApiString))
14+
{
15+
return openApiAny;
16+
}
17+
18+
var value = ((OpenApiString)openApiAny).Value;
19+
20+
if (value == null || value == "null")
21+
{
22+
return new OpenApiNull();
23+
}
24+
25+
if (value == "true")
26+
{
27+
return new OpenApiBoolean(true);
28+
}
29+
30+
if (value == "false")
31+
{
32+
return new OpenApiBoolean(false);
33+
}
34+
35+
if (int.TryParse(value, out var intValue))
36+
{
37+
return new OpenApiInteger(intValue);
38+
}
39+
40+
if (long.TryParse(value, out var longValue))
41+
{
42+
return new OpenApiLong(longValue);
43+
}
44+
45+
if (double.TryParse(value, out var doubleValue))
46+
{
47+
return new OpenApiDouble(doubleValue);
48+
}
49+
50+
if (DateTimeOffset.TryParse(value, out var dateTimeValue))
51+
{
52+
return new OpenApiDateTime(dateTimeValue);
53+
}
54+
55+
// if we can't identify the type of value, return it as string.
56+
return new OpenApiString(value);
57+
}
58+
59+
public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiSchema schema)
60+
{
61+
var type = schema.Type;
62+
var format = schema.Format;
63+
64+
if (openApiAny is OpenApiArray openApiArray)
65+
{
66+
var newArray = new OpenApiArray();
67+
foreach (var element in openApiArray)
68+
{
69+
newArray.Add(GetSpecificOpenApiAny(element, schema.Items));
70+
}
71+
72+
return newArray;
73+
}
74+
75+
if (openApiAny is OpenApiObject openApiObject)
76+
{
77+
var newObject = new OpenApiObject();
78+
79+
foreach (var key in openApiObject.Keys.ToList())
80+
{
81+
newObject[key] = GetSpecificOpenApiAny(openApiObject[key], schema.AdditionalProperties);
82+
}
83+
84+
return newObject;
85+
}
86+
87+
if (!(openApiAny is OpenApiString))
88+
{
89+
return openApiAny;
90+
}
91+
92+
if (type == null && format == null)
93+
{
94+
GetSpecificOpenApiAny(openApiAny);
95+
}
96+
97+
var value = ((OpenApiString)openApiAny).Value;
98+
99+
if (value == null || value == "null")
100+
{
101+
return new OpenApiNull();
102+
}
103+
104+
if (type == "integer" && format == "int32")
105+
{
106+
if (int.TryParse(value, out var intValue))
107+
{
108+
return new OpenApiInteger(intValue);
109+
}
110+
else
111+
{
112+
throw new OpenApiException("The value is not compatible with the given type and format.");
113+
}
114+
}
115+
116+
if (type == "integer" && format == "int64")
117+
{
118+
if (long.TryParse(value, out var longValue))
119+
{
120+
return new OpenApiLong(longValue);
121+
}
122+
else
123+
{
124+
throw new OpenApiException("The value is not compatible with the given type and format.");
125+
}
126+
}
127+
128+
if (type == "integer")
129+
{
130+
if (long.TryParse(value, out var longValue))
131+
{
132+
return new OpenApiLong(longValue);
133+
}
134+
else
135+
{
136+
throw new OpenApiException("The value is not compatible with the given type and format.");
137+
}
138+
}
139+
140+
if (type == "number" && format == "float")
141+
{
142+
if ( float.TryParse(value, out var floatValue))
143+
{
144+
return new OpenApiFloat(floatValue);
145+
}
146+
else
147+
{
148+
throw new OpenApiException("The value is not compatible with the given type and format.");
149+
}
150+
}
151+
152+
if (type == "number" && format == "double" )
153+
{
154+
if (double.TryParse(value, out var doubleValue))
155+
{
156+
return new OpenApiDouble(doubleValue);
157+
}
158+
else
159+
{
160+
throw new OpenApiException("The value is not compatible with the given type and format.");
161+
}
162+
}
163+
164+
if (type == "number")
165+
{
166+
if (double.TryParse(value, out var doubleValue))
167+
{
168+
return new OpenApiDouble(doubleValue);
169+
}
170+
else
171+
{
172+
throw new OpenApiException("The value is not compatible with the given type and format.");
173+
}
174+
}
175+
176+
if (type == "string" && format == "byte")
177+
{
178+
if ( byte.TryParse(value, out var byteValue))
179+
{
180+
return new OpenApiByte(byteValue);
181+
}
182+
else
183+
{
184+
throw new OpenApiException("The value is not compatible with the given type and format.");
185+
}
186+
}
187+
188+
// TODO: Parse byte array to OpenApiBinary type.
189+
190+
if (type == "string" && format == "date")
191+
{
192+
if (DateTime.TryParse(value, out var dateValue))
193+
{
194+
return new OpenApiDate(dateValue.Date);
195+
}
196+
else
197+
{
198+
throw new OpenApiException("The value is not compatible with the given type and format.");
199+
}
200+
}
201+
202+
if (type == "string" && format == "date-time")
203+
{
204+
if (DateTime.TryParse(value, out var dateTimeValue))
205+
{
206+
return new OpenApiDateTime(dateTimeValue);
207+
}
208+
else
209+
{
210+
throw new OpenApiException("The value is not compatible with the given type and format.");
211+
}
212+
}
213+
214+
if (type == "string" && format == "password")
215+
{
216+
return new OpenApiPassword(value);
217+
}
218+
219+
if (type == "string")
220+
{
221+
return new OpenApiString(value);
222+
}
223+
224+
if (type == "boolean")
225+
{
226+
if (bool.TryParse(value, out var booleanValue))
227+
{
228+
return new OpenApiBoolean(booleanValue);
229+
}
230+
else
231+
{
232+
throw new OpenApiException("The value is not compatible with the given type and format.");
233+
}
234+
}
235+
236+
throw new OpenApiException("The specified type is not supported.");
237+
}
238+
}
239+
}

src/Microsoft.OpenApi.Readers/ParseNodes/ValueNode.cs

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System;
55
using Microsoft.OpenApi.Any;
6-
using Microsoft.OpenApi.Exceptions;
76
using Microsoft.OpenApi.Readers.Exceptions;
87
using SharpYaml.Serialization;
98

@@ -27,7 +26,7 @@ public ValueNode(ParsingContext context, OpenApiDiagnostic diagnostic, YamlNode
2726
public override string GetScalarValue()
2827
{
2928
return _node.Value;
30-
}
29+
}
3130

3231
/// <summary>
3332
/// Create a <see cref="IOpenApiPrimitive"/>
@@ -36,47 +35,6 @@ public override string GetScalarValue()
3635
public override IOpenApiAny CreateAny()
3736
{
3837
var value = GetScalarValue();
39-
40-
if (value == null || value == "null")
41-
{
42-
return new OpenApiNull();
43-
}
44-
45-
if (value == "true")
46-
{
47-
return new OpenApiBoolean(true);
48-
}
49-
50-
if (value == "false")
51-
{
52-
return new OpenApiBoolean(false);
53-
}
54-
55-
if (int.TryParse(value, out var intValue))
56-
{
57-
return new OpenApiInteger(intValue);
58-
}
59-
60-
if (long.TryParse(value, out var longValue))
61-
{
62-
return
63-
new OpenApiLong(
64-
longValue);
65-
}
66-
67-
if (double.TryParse(value, out var dblValue))
68-
{
69-
return
70-
new OpenApiDouble(
71-
dblValue); // Note(darrmi): This may be better as decimal. Further investigation required.
72-
}
73-
74-
if (DateTimeOffset.TryParse(value, out var datetimeValue))
75-
{
76-
return new OpenApiDateTime(datetimeValue);
77-
}
78-
79-
// if we can't identify the type of value, return it as string.
8038
return new OpenApiString(value);
8139
}
8240
}

0 commit comments

Comments
 (0)