Skip to content

Commit c3decc3

Browse files
authored
Merge for #60 by resolving conflicts (#96)
1 parent 2139ef2 commit c3decc3

File tree

13 files changed

+216
-32
lines changed

13 files changed

+216
-32
lines changed

samples/Aliencube.AzureFunctions.FunctionAppV2/SampleHttpTrigger.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ public SampleHttpTrigger(ISampleHttpFunction function)
4848
[OpenApiParameter(name: "category", In = ParameterLocation.Path, Required = true, Type = typeof(string), Summary = "The category parameter", Visibility = OpenApiVisibilityType.Advanced)]
4949
[OpenApiParameter(name: "name", In = ParameterLocation.Query, Required = true, Type = typeof(string), Summary = "The name query key", Visibility = OpenApiVisibilityType.Advanced)]
5050
[OpenApiParameter(name: "limit", In = ParameterLocation.Query, Required = false, Type = typeof(int), Description = "The number of samples to return")]
51-
[OpenApiResponseBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(SampleResponseModel), Summary = "Sample response")]
5251
[OpenApiResponseBody(statusCode: HttpStatusCode.Created, contentType: "application/json", bodyType: typeof(List<SampleItemModel>), Summary = "Sample response of a List")]
5352
[OpenApiResponseBody(statusCode: HttpStatusCode.Accepted, contentType: "application/json", bodyType: typeof(IList<SampleResponseModel>), Summary = "Sample response of a IList")]
5453
[OpenApiResponseBody(statusCode: HttpStatusCode.NonAuthoritativeInformation, contentType: "application/json", bodyType: typeof(SampleResponseModel[]), Summary = "Sample response of a Array")]
@@ -59,6 +58,8 @@ public SampleHttpTrigger(ISampleHttpFunction function)
5958
[OpenApiResponseBody(statusCode: HttpStatusCode.InternalServerError, contentType: "application/json", bodyType: typeof(List<string>), Summary = "Sample response of a List")]
6059
[OpenApiResponseBody(statusCode: HttpStatusCode.NotImplemented, contentType: "application/json", bodyType: typeof(Dictionary<string, int>), Summary = "Sample response of a Dictionary")]
6160
[OpenApiResponseBody(statusCode: HttpStatusCode.BadGateway, contentType: "application/json", bodyType: typeof(SampleGenericResponseModel<SampleResponseModel>), Summary = "Sample response of a Generic")]
61+
//[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.NoContent)]
62+
//[OpenApiResponseWithoutBody(statusCode: HttpStatusCode.RequestTimeout, Description = "Sample response with only status code", Summary = "Sample summary")]
6263
public async Task<IActionResult> GetSample(
6364
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "samples/{id:int}/categories/{category:regex(^[a-z]{{3,}}$)}")] HttpRequest req,
6465
int id,
@@ -93,4 +94,4 @@ public async Task<IActionResult> PostSample(
9394
return result;
9495
}
9596
}
96-
}
97+
}

src/Aliencube.AzureFunctions.Extensions.OpenApi/Attributes/OpenApiRequestBodyAttribute.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22

33
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Attributes
44
{
@@ -17,5 +17,10 @@ public OpenApiRequestBodyAttribute(string contentType, Type bodyType)
1717
: base(contentType, bodyType)
1818
{
1919
}
20+
21+
/// <summary>
22+
/// Gets or sets the value indicating whether the request body is required or not.
23+
/// </summary>
24+
public virtual bool Required { get; set; }
2025
}
2126
}

src/Aliencube.AzureFunctions.Extensions.OpenApi/Attributes/OpenApiResponseBodyAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
using System;
1+
using System;
22
using System.Net;
33

44
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Attributes
55
{
66
/// <summary>
77
/// This represents the attribute entity for HTTP triggers to define response body payload.
88
/// </summary>
9-
9+
[Obsolete("This class is obsolete from 2.0.0. Use OpenApiResponseWithBodyAttribute instead", error: true)]
1010
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
1111
public class OpenApiResponseBodyAttribute : OpenApiPayloadAttribute
1212
{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Net;
3+
4+
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Attributes
5+
{
6+
/// <summary>
7+
/// This represents the attribute entity for HTTP triggers to define response body payload.
8+
/// </summary>
9+
10+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
11+
public class OpenApiResponseWithBodyAttribute : OpenApiPayloadAttribute
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="OpenApiResponseWithBodyAttribute"/> class.
15+
/// </summary>
16+
/// <param name="statusCode">HTTP status code.</param>
17+
/// <param name="contentType">Content type.</param>
18+
/// <param name="bodyType">Type of payload.</param>
19+
public OpenApiResponseWithBodyAttribute(HttpStatusCode statusCode, string contentType, Type bodyType)
20+
: base(contentType, bodyType)
21+
{
22+
this.StatusCode = statusCode;
23+
}
24+
25+
/// <summary>
26+
/// Gets the HTTP status code value.
27+
/// </summary>
28+
public virtual HttpStatusCode StatusCode { get; }
29+
30+
/// <summary>
31+
/// Gets or sets the summary.
32+
/// </summary>
33+
public virtual string Summary { get; set; }
34+
}
35+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Net;
3+
4+
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Attributes
5+
{
6+
/// <summary>
7+
/// This represents the attribute entity for HTTP triggers to define response body payload.
8+
/// </summary>
9+
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
10+
public class OpenApiResponseWithoutBodyAttribute : Attribute
11+
{
12+
/// <summary>
13+
/// Initializes a new instance of the <see cref="OpenApiResponseWithoutBodyAttribute"/> class.
14+
/// </summary>
15+
/// <param name="statusCode">HTTP status code.</param>
16+
public OpenApiResponseWithoutBodyAttribute(HttpStatusCode statusCode)
17+
{
18+
this.StatusCode = statusCode;
19+
}
20+
21+
/// <summary>
22+
/// Gets the HTTP status code value.
23+
/// </summary>
24+
public virtual HttpStatusCode StatusCode { get; }
25+
26+
/// <summary>
27+
/// Gets or sets the summary.
28+
/// </summary>
29+
public virtual string Summary { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets the description.
33+
/// </summary>
34+
public virtual string Description { get; set; }
35+
}
36+
}

src/Aliencube.AzureFunctions.Extensions.OpenApi/Document.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public IDocument Build(Assembly assembly, NamingStrategy namingStrategy = null)
123123

124124
operation.Parameters = this._helper.GetOpenApiParameters(method, trigger);
125125
operation.RequestBody = this._helper.GetOpenApiRequestBody(method);
126-
operation.Responses = this._helper.GetOpenApiResponseBody(method);
126+
operation.Responses = this._helper.GetOpenApiResponses(method);
127127

128128
operations[verb] = operation;
129129
item.Operations = operations;

src/Aliencube.AzureFunctions.Extensions.OpenApi/DocumentHelper.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Collections.Generic;
33
using System.Linq;
44
using System.Reflection;
@@ -125,6 +125,7 @@ public List<OpenApiParameter> GetOpenApiParameters(MethodInfo element, HttpTrigg
125125
.Select(p => p.ToOpenApiParameter())
126126
.ToList();
127127

128+
// This needs to be provided separately.
128129
if (trigger.AuthLevel != AuthorizationLevel.Anonymous)
129130
{
130131
parameters.AddOpenApiParameter<string>("code", @in: ParameterLocation.Query, required: false);
@@ -136,23 +137,51 @@ public List<OpenApiParameter> GetOpenApiParameters(MethodInfo element, HttpTrigg
136137
/// <inheritdoc />
137138
public OpenApiRequestBody GetOpenApiRequestBody(MethodInfo element, NamingStrategy namingStrategy = null)
138139
{
139-
var contents = element.GetCustomAttributes<OpenApiRequestBodyAttribute>(inherit: false)
140-
.ToDictionary(p => p.ContentType, p => p.ToOpenApiMediaType());
140+
var attributes = element.GetCustomAttributes<OpenApiRequestBodyAttribute>(inherit: false);
141+
if (!attributes.Any())
142+
{
143+
return null;
144+
}
145+
146+
var contents = attributes.ToDictionary(p => p.ContentType, p => p.ToOpenApiMediaType());
141147

142148
if (contents.Any())
143149
{
144-
return new OpenApiRequestBody() { Content = contents };
150+
return new OpenApiRequestBody()
151+
{
152+
Content = contents,
153+
Required = attributes.First().Required
154+
};
145155
}
146156

147157
return null;
148158
}
149159

150160
/// <inheritdoc />
161+
[Obsolete("This method is obsolete from 2.0.0. Use GetOpenApiResponses instead", error: true)]
151162
public OpenApiResponses GetOpenApiResponseBody(MethodInfo element, NamingStrategy namingStrategy = null)
152163
{
153-
var responses = element.GetCustomAttributes<OpenApiResponseBodyAttribute>(inherit: false)
154-
.ToDictionary(p => ((int)p.StatusCode).ToString(), p => p.ToOpenApiResponse(namingStrategy))
155-
.ToOpenApiResponses();
164+
return this.GetOpenApiResponses(element, namingStrategy);
165+
166+
//var responses = element.GetCustomAttributes<OpenApiResponseBodyAttribute>(inherit: false)
167+
// .ToDictionary(p => ((int)p.StatusCode).ToString(), p => p.ToOpenApiResponse(namingStrategy))
168+
// .ToOpenApiResponses();
169+
170+
//return responses;
171+
}
172+
173+
/// <inheritdoc />
174+
public OpenApiResponses GetOpenApiResponses(MethodInfo element, NamingStrategy namingStrategy = null)
175+
{
176+
var responsesWithBody = element.GetCustomAttributes<OpenApiResponseWithBodyAttribute>(inherit: false)
177+
.Select(p => new { StatusCode = p.StatusCode, Response = p.ToOpenApiResponse(namingStrategy) });
178+
179+
var responsesWithoutBody = element.GetCustomAttributes<OpenApiResponseWithoutBodyAttribute>(inherit: false)
180+
.Select(p => new { StatusCode = p.StatusCode, Response = p.ToOpenApiResponse(namingStrategy) });
181+
182+
var responses = responsesWithBody.Concat(responsesWithoutBody)
183+
.ToDictionary(p => ((int)p.StatusCode).ToString(), p => p.Response)
184+
.ToOpenApiResponses();
156185

157186
return responses;
158187
}
@@ -162,7 +191,7 @@ public Dictionary<string, OpenApiSchema> GetOpenApiSchemas(List<MethodInfo> elem
162191
{
163192
var requests = elements.SelectMany(p => p.GetCustomAttributes<OpenApiRequestBodyAttribute>(inherit: false))
164193
.Select(p => p.BodyType);
165-
var responses = elements.SelectMany(p => p.GetCustomAttributes<OpenApiResponseBodyAttribute>(inherit: false))
194+
var responses = elements.SelectMany(p => p.GetCustomAttributes<OpenApiResponseWithBodyAttribute>(inherit: false))
166195
.Select(p => p.BodyType);
167196
var types = requests.Union(responses)
168197
.Select(p => p.IsOpenApiArray() || p.IsOpenApiDictionary() ? p.GetOpenApiSubType() : p)
@@ -204,4 +233,4 @@ private string FilterRoute(string route)
204233
return string.Join("/", segments);
205234
}
206235
}
207-
}
236+
}

src/Aliencube.AzureFunctions.Extensions.OpenApi/Extensions/OpenApiResponseBodyAttributeExtensions.cs renamed to src/Aliencube.AzureFunctions.Extensions.OpenApi/Extensions/OpenApiResponseWithBodyAttributeExtensions.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22

33
using Aliencube.AzureFunctions.Extensions.OpenApi.Attributes;
44

@@ -10,24 +10,24 @@
1010
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Extensions
1111
{
1212
/// <summary>
13-
/// This represents the extension entity for <see cref="OpenApiResponseBodyAttribute"/>.
13+
/// This represents the extension entity for <see cref="OpenApiResponseWithBodyAttribute"/>.
1414
/// </summary>
15-
public static class OpenApiResponseBodyAttributeExtensions
15+
public static class OpenApiResponseWithBodyAttributeExtensions
1616
{
1717
/// <summary>
18-
/// Converts <see cref="OpenApiResponseBodyAttribute"/> to <see cref="OpenApiResponse"/>.
18+
/// Converts <see cref="OpenApiResponseWithBodyAttribute"/> to <see cref="OpenApiResponse"/>.
1919
/// </summary>
20-
/// <param name="attribute"><see cref="OpenApiResponseBodyAttribute"/> instance.</param>
20+
/// <param name="attribute"><see cref="OpenApiResponseWithBodyAttribute"/> instance.</param>
2121
/// <param name="namingStrategy"><see cref="NamingStrategy"/> instance to create the JSON schema from .NET Types.</param>
2222
/// <returns><see cref="OpenApiResponse"/> instance.</returns>
23-
public static OpenApiResponse ToOpenApiResponse(this OpenApiResponseBodyAttribute attribute, NamingStrategy namingStrategy = null)
23+
public static OpenApiResponse ToOpenApiResponse(this OpenApiResponseWithBodyAttribute attribute, NamingStrategy namingStrategy = null)
2424
{
2525
attribute.ThrowIfNullOrDefault();
2626

2727
var description = string.IsNullOrWhiteSpace(attribute.Description)
2828
? $"Payload of {attribute.BodyType.GetOpenApiDescription()}"
2929
: attribute.Description;
30-
var mediaType = attribute.ToOpenApiMediaType<OpenApiResponseBodyAttribute>(namingStrategy);
30+
var mediaType = attribute.ToOpenApiMediaType<OpenApiResponseWithBodyAttribute>(namingStrategy);
3131
var content = new Dictionary<string, OpenApiMediaType>()
3232
{
3333
{ attribute.ContentType, mediaType }
@@ -48,4 +48,4 @@ public static OpenApiResponse ToOpenApiResponse(this OpenApiResponseBodyAttribut
4848
return response;
4949
}
5050
}
51-
}
51+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Aliencube.AzureFunctions.Extensions.OpenApi.Attributes;
2+
3+
using Microsoft.OpenApi.Any;
4+
using Microsoft.OpenApi.Models;
5+
6+
using Newtonsoft.Json.Serialization;
7+
8+
namespace Aliencube.AzureFunctions.Extensions.OpenApi.Extensions
9+
{
10+
/// <summary>
11+
/// This represents the extension entity for <see cref="OpenApiResponseWithoutBodyAttribute"/>.
12+
/// </summary>
13+
public static class OpenApiResponseWithoutBodyAttributeExtensions
14+
{
15+
/// <summary>
16+
/// Converts <see cref="OpenApiResponseWithoutBodyAttribute"/> to <see cref="OpenApiResponse"/>.
17+
/// </summary>
18+
/// <param name="attribute"><see cref="OpenApiResponseWithoutBodyAttribute"/> instance.</param>
19+
/// <param name="namingStrategy"><see cref="NamingStrategy"/> instance to create the JSON schema from .NET Types.</param>
20+
/// <returns><see cref="OpenApiResponse"/> instance.</returns>
21+
public static OpenApiResponse ToOpenApiResponse(this OpenApiResponseWithoutBodyAttribute attribute, NamingStrategy namingStrategy = null)
22+
{
23+
attribute.ThrowIfNullOrDefault();
24+
25+
var description = string.IsNullOrWhiteSpace(attribute.Description)
26+
? "No description"
27+
: attribute.Description;
28+
var response = new OpenApiResponse()
29+
{
30+
Description = description,
31+
};
32+
33+
if (!string.IsNullOrWhiteSpace(attribute.Summary))
34+
{
35+
var summary = new OpenApiString(attribute.Summary);
36+
37+
response.Extensions.Add("x-ms-summary", summary);
38+
}
39+
40+
return response;
41+
}
42+
}
43+
}

src/Aliencube.AzureFunctions.Extensions.OpenApi/Extensions/OpenApiResponsesExtensions.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System;
21
using System.Collections.Generic;
32

43
using Microsoft.OpenApi.Models;
@@ -28,4 +27,4 @@ public static OpenApiResponses ToOpenApiResponses(this Dictionary<string, OpenAp
2827
return responses;
2928
}
3029
}
31-
}
30+
}

0 commit comments

Comments
 (0)