Skip to content

Commit 1e50bac

Browse files
authored
fix Nullable DateTime type to display as date format in specification (#208)
1 parent a7074b1 commit 1e50bac

File tree

5 files changed

+188
-2
lines changed

5 files changed

+188
-2
lines changed

src/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core/Visitors/NullableObjectTypeVisitor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations;
34
using System.Linq;
45

56
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
@@ -80,8 +81,8 @@ public override void Visit(IAcceptor acceptor, KeyValuePair<string, Type> type,
8081

8182
schema.Extensions.Add("x-ms-visibility", extension);
8283
}
84+
schema.ApplyValidationAttributes(attributes.OfType<ValidationAttribute>());
8385
}
84-
8586
instance.Schemas.Add(name, schema);
8687
}
8788

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System.Linq;
2+
using System.Net.Http;
3+
using System.Threading.Tasks;
4+
5+
using FluentAssertions;
6+
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
9+
using Newtonsoft.Json;
10+
using Newtonsoft.Json.Linq;
11+
12+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.Document.Tests
13+
{
14+
[TestClass]
15+
[TestCategory(Constants.TestCategory)]
16+
public class Get_ApplicationJson_DataType_Tests
17+
{
18+
private static HttpClient http = new HttpClient();
19+
20+
private JObject _doc;
21+
22+
[TestInitialize]
23+
public async Task Init()
24+
{
25+
var json = await http.GetStringAsync(Constants.OpenApiDocEndpoint).ConfigureAwait(false);
26+
this._doc = JsonConvert.DeserializeObject<JObject>(json);
27+
}
28+
29+
[DataTestMethod]
30+
[DataRow("/get-applicationjson-datatype", "get", "200")]
31+
public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponse(string path, string operationType, string responseCode)
32+
{
33+
var responses = this._doc["paths"][path][operationType]["responses"];
34+
35+
responses[responseCode].Should().NotBeNull();
36+
}
37+
38+
[DataTestMethod]
39+
[DataRow("/get-applicationjson-datatype", "get", "200", "application/json")]
40+
public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContentType(string path, string operationType, string responseCode, string contentType)
41+
{
42+
var content = this._doc["paths"][path][operationType]["responses"][responseCode]["content"];
43+
44+
content[contentType].Should().NotBeNull();
45+
}
46+
47+
[DataTestMethod]
48+
[DataRow("/get-applicationjson-datatype", "get", "200", "application/json", "dataTypeClass")]
49+
public void Given_OpenApiDocument_Then_It_Should_Return_OperationResponseContentTypeSchema(string path, string operationType, string responseCode, string contentType, string reference)
50+
{
51+
var content = this._doc["paths"][path][operationType]["responses"][responseCode]["content"];
52+
53+
var @ref = content[contentType]["schema"]["$ref"];
54+
55+
@ref.Value<string>().Should().Be($"#/components/schemas/{reference}");
56+
}
57+
[DataTestMethod]
58+
[DataRow("dataTypeClass", "object")]
59+
public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchema(string @ref, string refType)
60+
{
61+
var schemas = this._doc["components"]["schemas"];
62+
63+
var schema = schemas[@ref];
64+
65+
schema.Should().NotBeNull();
66+
schema.Value<string>("type").Should().Be(refType);
67+
}
68+
69+
[DataTestMethod]
70+
[DataRow("dataTypeClass", "dateTimeValue1", "string", "date-time", false)]
71+
[DataRow("dataTypeClass", "dateTimeValue2", "string", "date", false)]
72+
[DataRow("dataTypeClass", "dateTimeValue3", "string", "time", false)]
73+
[DataRow("dataTypeClass", "nullableDateTimeValue1", "string", "date-time", true)]
74+
[DataRow("dataTypeClass", "nullableDateTimeValue2", "string", "date", true)]
75+
[DataRow("dataTypeClass", "nullableDateTimeValue3", "string", "time", true)]
76+
[DataRow("dataTypeClass", "dateTimeOffsetValue1", "string", "date-time", false)]
77+
[DataRow("dataTypeClass", "dateTimeOffsetValue2", "string", "date", false)]
78+
[DataRow("dataTypeClass", "dateTimeOffsetValue3", "string", "time", false)]
79+
[DataRow("dataTypeClass", "nullableDateTimeOffsetValue1", "string", "date-time", true)]
80+
[DataRow("dataTypeClass", "nullableDateTimeOffsetValue2", "string", "date", true)]
81+
[DataRow("dataTypeClass", "nullableDateTimeOffsetValue3", "string", "time", true)]
82+
public void Given_OpenApiDocument_Then_It_Should_Return_ComponentSchemaProperty(string @ref, string propertyName, string propertyType, string propertyFormat, bool propertyNullable)
83+
{
84+
var properties = this._doc["components"]["schemas"][@ref]["properties"];
85+
86+
var value = properties[propertyName];
87+
88+
value.Should().NotBeNull();
89+
value.Value<string>("type").Should().Be(propertyType);
90+
value.Value<string>("format").Should().Be(propertyFormat);
91+
value.Value<bool>("nullable").Should().Be(propertyNullable);
92+
}
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System.Net;
2+
using System.Threading.Tasks;
3+
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Microsoft.Azure.WebJobs.Extensions.Http;
7+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
8+
using Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models;
9+
using Microsoft.Extensions.Logging;
10+
using Microsoft.OpenApi.Models;
11+
12+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp
13+
{
14+
public static class Get_ApplicationJson_DataType_HttpTrigger
15+
{
16+
[FunctionName(nameof(Get_ApplicationJson_DataType_HttpTrigger))]
17+
[OpenApiOperation(operationId: nameof(Get_ApplicationJson_DataType_HttpTrigger.Get_ApplicationJson_DataType), tags: new[] { "dataType" })]
18+
[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(DataTypeClass), Description = "The OK response")]
19+
public static async Task<IActionResult> Get_ApplicationJson_DataType(
20+
[HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "get-applicationjson-datatype")] HttpRequest req,
21+
ILogger log)
22+
{
23+
var result = new OkResult();
24+
25+
return await Task.FromResult(result).ConfigureAwait(false);
26+
}
27+
}
28+
29+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel.DataAnnotations;
4+
using System.Text;
5+
6+
namespace Microsoft.Azure.WebJobs.Extensions.OpenApi.TestApp.Models
7+
{
8+
public class DataTypeClass
9+
{
10+
[DataType(DataType.DateTime)]
11+
public DateTime DateTimeValue1 { get; set; }
12+
13+
[DataType(DataType.Date)]
14+
public DateTime DateTimeValue2 { get; set; }
15+
16+
[DataType(DataType.Time)]
17+
public DateTime DateTimeValue3 { get; set; }
18+
19+
[DataType(DataType.DateTime)]
20+
public DateTime? NullableDateTimeValue1 { get; set; }
21+
22+
[DataType(DataType.Date)]
23+
public DateTime? NullableDateTimeValue2 { get; set; }
24+
25+
[DataType(DataType.Time)]
26+
public DateTime? NullableDateTimeValue3 { get; set; }
27+
28+
[DataType(DataType.DateTime)]
29+
public DateTimeOffset DateTimeOffsetValue1 { get; set; }
30+
31+
[DataType(DataType.Date)]
32+
public DateTimeOffset DateTimeOffsetValue2 { get; set; }
33+
34+
[DataType(DataType.Time)]
35+
public DateTimeOffset DateTimeOffsetValue3 { get; set; }
36+
37+
[DataType(DataType.DateTime)]
38+
public DateTimeOffset? NullableDateTimeOffsetValue1 { get; set; }
39+
40+
[DataType(DataType.Date)]
41+
public DateTimeOffset? NullableDateTimeOffsetValue2 { get; set; }
42+
43+
[DataType(DataType.Time)]
44+
public DateTimeOffset? NullableDateTimeOffsetValue3 { get; set; }
45+
}
46+
}

test/Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Tests/Visitors/NullableObjectTypeVisitorTests.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3-
3+
using System.ComponentModel.DataAnnotations;
44
using FluentAssertions;
55

66
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Abstractions;
@@ -70,6 +70,22 @@ public void Given_Type_When_IsPayloadVisitable_Invoked_Then_It_Should_Return_Res
7070
result.Should().Be(expected);
7171
}
7272

73+
[DataTestMethod]
74+
[DataRow(DataType.DateTime, "string", "hello", "date-time")]
75+
[DataRow(DataType.Date, "string", "hello", "date")]
76+
public void Given_DataTypeAttribute_When_Visit_Invoked_Then_It_Should_Return_Result(DataType objectType, string dataType, string name, string expected)
77+
{
78+
var acceptor = new OpenApiSchemaAcceptor();
79+
var type = new KeyValuePair<string, Type>(name, typeof(DateTime?));
80+
var attribute = new DataTypeAttribute(objectType);
81+
82+
this._visitor.Visit(acceptor, type, this._strategy, attribute);
83+
84+
acceptor.Schemas.Should().ContainKey(name);
85+
acceptor.Schemas[name].Type.Should().Be(dataType);
86+
acceptor.Schemas[name].Format.Should().Be(expected);
87+
}
88+
7389
[DataTestMethod]
7490
[DataRow(typeof(DateTime?), "string", "date-time", true)]
7591
[DataRow(typeof(int?), "integer", "int32", true)]

0 commit comments

Comments
 (0)