Skip to content

Commit 067eedf

Browse files
committed
refactor(api): replace open api packages
1 parent 7e9be12 commit 067eedf

File tree

3 files changed

+166
-134
lines changed

3 files changed

+166
-134
lines changed

src/api/Extensions/WebApplicationBuilderExtensions.cs

Lines changed: 98 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.IO;
22
using System.Net;
33
using System.Net.Http;
4-
using api.OpenApi;
54
using Autofac;
65
using Autofac.Extensions.DependencyInjection;
76
using Google.Api.Gax;
@@ -16,14 +15,13 @@
1615
using Microsoft.Extensions.Hosting;
1716
using Microsoft.Extensions.Logging;
1817
using Microsoft.Extensions.Options;
19-
using Microsoft.OpenApi.Models;
18+
using Microsoft.OpenApi;
2019
using Npgsql;
2120
using Polly;
2221
using Polly.Extensions.Http;
2322
using Polly.Retry;
2423
using Polly.Timeout;
2524
using StackExchange.Redis;
26-
using Swashbuckle.AspNetCore.SwaggerGen;
2725
using ugrc.api.Cache;
2826
using ugrc.api.Features.Converting;
2927
using ugrc.api.Features.Geocoding;
@@ -39,6 +37,7 @@
3937
using ZiggyCreatures.Caching.Fusion.Serialization.SystemTextJson;
4038

4139
namespace ugrc.api.Extensions;
40+
4241
public static class WebApplicationBuilderExtensions {
4342
public static void ConfigureConfiguration(this WebApplicationBuilder builder) {
4443
builder.Configuration
@@ -319,73 +318,112 @@ private static void AddArcGisClient(IServiceCollection services, AsyncRetryPolic
319318
}
320319
}
321320
public static void ConfigureOpenApi(this WebApplicationBuilder builder) {
322-
builder.Services.AddEndpointsApiExplorer();
323-
builder.Services.AddSwaggerGen(c => {
324-
c.EnableAnnotations();
325-
c.DescribeAllParametersInCamelCase();
326-
327-
c.DocumentFilter<TrimUrlOperationFilter>();
328-
c.CustomOperationIds(apiDesc => apiDesc.TryGetMethodInfo(out var methodInfo) ? methodInfo.Name : null);
329-
330-
c.AddSecurityDefinition("apikey", new OpenApiSecurityScheme {
331-
Type = SecuritySchemeType.ApiKey,
332-
In = ParameterLocation.Query,
333-
Name = "apiKey",
334-
Description = "A key gathered from developer.mapserv.utah.gov"
335-
});
321+
builder.Services.AddOpenApi("v1", options => {
322+
options.AddDocumentTransformer((document, _, _) => {
323+
document.Info = new() {
324+
Version = "v1",
325+
Title = "UGRC API : OpenAPI Documentation",
326+
Description = "OpenAPI Documentation",
327+
Contact = new OpenApiContact {
328+
Name = "UGRC",
329+
Email = "[email protected]",
330+
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov")
331+
},
332+
License = new OpenApiLicense {
333+
Name = "MIT",
334+
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov/blob/main/LICENSE")
335+
}
336+
};
336337

337-
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
338-
{
339-
new OpenApiSecurityScheme {
340-
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "apikey" },
338+
document.Servers = [
339+
new() {
340+
Url = "https://api.mapserv.utah.gov/api/v1",
341+
Description = "Base url for the version 1 API"
342+
}
343+
];
344+
345+
var securityScheme = new OpenApiSecurityScheme {
341346
Type = SecuritySchemeType.ApiKey,
342347
In = ParameterLocation.Query,
343348
Name = "apiKey",
344-
Description = "A key acquired from developer.mapserv.utah.gov"
345-
},
346-
Array.Empty<string>()
347-
}
348-
});
349+
Description = "A key gathered from developer.mapserv.utah.gov"
350+
};
349351

350-
c.AddServer(new OpenApiServer {
351-
Url = "https://api.mapserv.utah.gov/api/v1",
352-
Description = "Base url for the version 1 API"
353-
});
352+
document.Components ??= new OpenApiComponents();
353+
document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
354+
document.Components.SecuritySchemes!.Add("apikey", securityScheme);
354355

355-
c.SwaggerDoc("v1", new OpenApiInfo {
356-
Version = "v1",
357-
Title = "UGRC API : OpenAPI Documentation",
358-
Description = "OpenAPI Documentation",
359-
Contact = new OpenApiContact {
360-
Name = "UGRC",
361-
Email = "[email protected]",
362-
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov")
363-
},
364-
License = new OpenApiLicense {
365-
Name = "MIT",
366-
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov/blob/main/LICENSE")
367-
},
368-
});
356+
var securitySchemeReference = new OpenApiSecuritySchemeReference("apikey", document);
357+
358+
document.Security = [
359+
new() {
360+
{ securitySchemeReference, [] }
361+
}
362+
];
363+
364+
document.Tags = new HashSet<OpenApiTag> {
365+
new() { Name = "Address Geocoding", Description = "Endpoints for finding a location from an address" },
366+
new() { Name = "Milepost Geocoding", Description = "Endpoints for finding a location from a milepost" },
367+
new() { Name = "Searching", Description = "Endpoints for searching the Open SGID" },
368+
new() { Name = "Info", Description = "Endpoints for getting information about the SGID" }
369+
};
369370

370-
c.SwaggerDoc("v2", new OpenApiInfo {
371-
Version = "v2",
372-
Title = "UGRC API : OpenAPI Documentation",
373-
Description = "OpenAPI Documentation",
374-
Contact = new OpenApiContact {
375-
Name = "UGRC",
376-
Email = "[email protected]",
377-
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov")
378-
},
379-
License = new OpenApiLicense {
380-
Name = "MIT",
381-
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov/blob/main/LICENSE")
382-
}
371+
return Task.CompletedTask;
383372
});
373+
});
384374

385-
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
386-
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
375+
builder.Services.AddOpenApi("v2", options => {
376+
options.AddDocumentTransformer((document, _, _) => {
377+
document.Info = new() {
378+
Version = "v2",
379+
Title = "UGRC API : OpenAPI Documentation",
380+
Description = "OpenAPI Documentation",
381+
Contact = new OpenApiContact {
382+
Name = "UGRC",
383+
Email = "[email protected]",
384+
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov")
385+
},
386+
License = new OpenApiLicense {
387+
Name = "MIT",
388+
Url = new Uri("https://github.com/agrc/api.mapserv.utah.gov/blob/main/LICENSE")
389+
}
390+
};
391+
392+
document.Servers = [
393+
new() {
394+
Url = "https://api.mapserv.utah.gov/api/v2",
395+
Description = "Base url for the version 2 API"
396+
}
397+
];
398+
399+
var securityScheme = new OpenApiSecurityScheme {
400+
Type = SecuritySchemeType.ApiKey,
401+
In = ParameterLocation.Query,
402+
Name = "apiKey",
403+
Description = "A key gathered from developer.mapserv.utah.gov"
404+
};
387405

388-
c.IncludeXmlComments(xmlPath);
406+
document.Components ??= new OpenApiComponents();
407+
document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
408+
document.Components.SecuritySchemes!.Add("apikey", securityScheme);
409+
410+
var securitySchemeReference = new OpenApiSecuritySchemeReference("apikey", document);
411+
412+
document.Security = [
413+
new() {
414+
{ securitySchemeReference, [] }
415+
}
416+
];
417+
418+
document.Tags = new HashSet<OpenApiTag> {
419+
new() { Name = "Address Geocoding", Description = "Endpoints for finding a location from an address" },
420+
new() { Name = "Milepost Geocoding", Description = "Endpoints for finding a location from a milepost" },
421+
new() { Name = "Searching", Description = "Endpoints for searching the Open SGID" },
422+
new() { Name = "Info", Description = "Endpoints for getting information about the SGID" }
423+
};
424+
425+
return Task.CompletedTask;
426+
});
389427
});
390428
}
391429
public static void ConfigureVersioning(this WebApplicationBuilder builder)

src/api/Extensions/WebApplicationExtensions.cs

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
44
using Microsoft.AspNetCore.Mvc;
55
using Microsoft.Extensions.Hosting;
6-
using Swashbuckle.AspNetCore.SwaggerUI;
76
using ugrc.api.Features.Converting;
87
using ugrc.api.Features.Geocoding;
98
using ugrc.api.Features.Health;
@@ -12,8 +11,10 @@
1211
using ugrc.api.Features.Searching;
1312
using ugrc.api.Middleware;
1413
using ugrc.api.Models.ResponseContracts;
14+
using Microsoft.OpenApi;
1515

1616
namespace ugrc.api.Extensions;
17+
1718
public static class WebApplicationExtensions {
1819
public static void MapRoutes(this WebApplication app) {
1920
if (app.Environment.IsDevelopment()) {
@@ -52,11 +53,15 @@ public static void MapRoutes(this WebApplication app) {
5253
.AddEndpointFilter<GeocodeQuery.ValidationFilter>()
5354
.HasApiVersion(1)
5455
.HasApiVersion(2)
55-
.WithOpenApi(operation => new(operation) {
56-
OperationId = "Geocode",
57-
Summary = "Single address geocoding",
58-
Description = "Extract the x, y location from a street address and zone",
59-
Tags = [new() { Name = "Address Geocoding" }],
56+
.AddOpenApiOperationTransformer((operation, context, ct) => {
57+
operation.OperationId = "Geocode";
58+
operation.Summary = "Single address geocoding";
59+
operation.Description = "Extract the x, y location from a street address and zone";
60+
operation.Tags = new HashSet<OpenApiTagReference> {
61+
new("Address Geocoding")
62+
};
63+
64+
return Task.CompletedTask;
6065
})
6166
.Produces<ApiResponseContract<SingleGeocodeResponseContract>>(StatusCodes.Status200OK)
6267
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -77,11 +82,15 @@ public static void MapRoutes(this WebApplication app) {
7782
})
7883
.HasApiVersion(1)
7984
.HasApiVersion(2)
80-
.WithOpenApi(operation => new(operation) {
81-
OperationId = "ReverseGeocode",
82-
Summary = "Reverse Geocoding",
83-
Description = "Extract the x, y location from a street address and zone",
84-
Tags = [new() { Name = "Address Geocoding" }],
85+
.AddOpenApiOperationTransformer((operation, context, ct) => {
86+
operation.OperationId = "ReverseGeocode";
87+
operation.Summary = "Reverse Geocoding";
88+
operation.Description = "Extract the x, y location from a street address and zone";
89+
operation.Tags = new HashSet<OpenApiTagReference> {
90+
new("Address Geocoding")
91+
};
92+
93+
return Task.CompletedTask;
8594
})
8695
.Produces<ApiResponseContract<ReverseGeocodeResponseContract>>(StatusCodes.Status200OK)
8796
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -103,11 +112,15 @@ public static void MapRoutes(this WebApplication app) {
103112
.AddEndpointFilter<RouteMilepostQuery.ValidationFilter>()
104113
.HasApiVersion(1)
105114
.HasApiVersion(2)
106-
.WithOpenApi(operation => new(operation) {
107-
OperationId = "MilepostGeocode",
108-
Summary = "Milepost Geocoding",
109-
Description = "Extract the x, y location from a milepost along a route",
110-
Tags = [new() { Name = "Milepost Geocoding" }],
115+
.AddOpenApiOperationTransformer((operation, context, ct) => {
116+
operation.OperationId = "MilepostGeocode";
117+
operation.Summary = "Milepost Geocoding";
118+
operation.Description = "Extract the x, y location from a milepost along a route";
119+
operation.Tags = new HashSet<OpenApiTagReference> {
120+
new("Milepost Geocoding")
121+
};
122+
123+
return Task.CompletedTask;
111124
})
112125
.Produces<ApiResponseContract<RouteMilepostResponseContract>>(StatusCodes.Status200OK)
113126
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -128,11 +141,15 @@ public static void MapRoutes(this WebApplication app) {
128141
})
129142
.HasApiVersion(1)
130143
.HasApiVersion(2)
131-
.WithOpenApi(operation => new(operation) {
132-
OperationId = "ReverseMilepostGeocode",
133-
Summary = "Reverse Milepost Geocoding",
134-
Description = "Extract the x, y, and measure from a milepost (measure) along a route",
135-
Tags = [new() { Name = "Milepost Geocoding" }],
144+
.AddOpenApiOperationTransformer((operation, context, ct) => {
145+
operation.OperationId = "ReverseMilepostGeocode";
146+
operation.Summary = "Reverse Milepost Geocoding";
147+
operation.Description = "Extract the x, y, and measure from a milepost (measure) along a route";
148+
operation.Tags = new HashSet<OpenApiTagReference> {
149+
new("Milepost Geocoding")
150+
};
151+
152+
return Task.CompletedTask;
136153
})
137154
.Produces<ApiResponseContract<RouteMilepostResponseContract>>(StatusCodes.Status200OK)
138155
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -154,18 +171,22 @@ public static void MapRoutes(this WebApplication app) {
154171
.AddEndpointFilter<SearchQuery.ValidationFilter>()
155172
.HasApiVersion(1)
156173
.HasApiVersion(2)
157-
.WithOpenApi(operation => new(operation) {
158-
OperationId = "Search",
159-
Summary = "Search the Open SGID",
160-
Description = "Search tables and attributes within the SGID",
161-
Tags = [new() { Name = "Searching" }],
174+
.AddOpenApiOperationTransformer((operation, context, ct) => {
175+
operation.OperationId = "Search";
176+
operation.Summary = "Search the Open SGID";
177+
operation.Description = "Search tables and attributes within the SGID";
178+
operation.Tags = new HashSet<OpenApiTagReference> {
179+
new("Searching")
180+
};
181+
182+
return Task.CompletedTask;
162183
})
163184
.Produces<ApiResponseContract<SearchResponseContract>>(StatusCodes.Status200OK)
164185
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
165186
.Produces<ApiResponseContract>(StatusCodes.Status404NotFound)
166187
.Produces<ApiResponseContract>(StatusCodes.Status500InternalServerError);
167188

168-
info.MapGet("/featureclassnames", async (
189+
_ = info.MapGet("/featureclassnames", async (
169190
InformationRequestOptionsContract options,
170191
[FromServices] IMediator mediator,
171192
[FromServices] IJsonSerializerOptionsFactory factory,
@@ -178,11 +199,15 @@ public static void MapRoutes(this WebApplication app) {
178199
.AddEndpointFilter<SqlSchemaQuery.ValidationFilter>()
179200
.HasApiVersion(1)
180201
.HasApiVersion(2)
181-
.WithOpenApi(operation => new(operation) {
182-
OperationId = "FeatureClassNames",
183-
Summary = "Get all feature classes for a SGID category",
184-
Description = "Discover SGID data by viewing all available feature classes for a given category",
185-
Tags = [new() { Name = "Info" }],
202+
.AddOpenApiOperationTransformer((operation, context, ct) => {
203+
operation.OperationId = "FeatureClassNames";
204+
operation.Summary = "Get all feature classes for a SGID category";
205+
operation.Description = "Discover SGID data by viewing all available feature classes for a given category";
206+
operation.Tags = new HashSet<OpenApiTagReference> {
207+
new("Info")
208+
};
209+
210+
return Task.CompletedTask;
186211
})
187212
.Produces<ApiResponseContract<IReadOnlyCollection<string>>>(StatusCodes.Status200OK)
188213
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -203,11 +228,15 @@ public static void MapRoutes(this WebApplication app) {
203228
.AddEndpointFilter<SqlAttributeQuery.ValidationFilter>()
204229
.HasApiVersion(1)
205230
.HasApiVersion(2)
206-
.WithOpenApi(operation => new(operation) {
207-
OperationId = "FeatureClassAttributeNames",
208-
Summary = "Get all attributes for a SGID feature class",
209-
Description = "Understand SGID table available fields by viewing all of the searchable attributes",
210-
Tags = [new() { Name = "Info" }],
231+
.AddOpenApiOperationTransformer((operation, context, ct) => {
232+
operation.OperationId = "FeatureClassAttributeNames";
233+
operation.Summary = "Get all attributes for a SGID feature class";
234+
operation.Description = "Understand SGID table available fields by viewing all of the searchable attributes";
235+
operation.Tags = new HashSet<OpenApiTagReference> {
236+
new("Info")
237+
};
238+
239+
return Task.CompletedTask;
211240
})
212241
.Produces<ApiResponseContract<IReadOnlyCollection<string>>>(StatusCodes.Status200OK)
213242
.Produces<ApiResponseContract>(StatusCodes.Status400BadRequest)
@@ -229,16 +258,6 @@ public static void MapHealthChecks(this WebApplication app) {
229258
ResponseWriter = StartupHealthCheckResponseWriter.WriteText
230259
});
231260
}
232-
public static void MapOpenApi(this WebApplication app) {
233-
app.UseSwagger(c => c.RouteTemplate = "openapi/{documentName}/api.json");
234-
app.UseSwaggerUI(c => {
235-
c.DocumentTitle = "UGRC API OpenAPI Documentation";
236-
c.RoutePrefix = "openapi";
237-
c.SwaggerEndpoint("/openapi/v1/api.json", "v1");
238-
c.SwaggerEndpoint("/openapi/v2/api.json", "v2");
239-
c.SupportedSubmitMethods();
240-
c.EnableDeepLinking();
241-
c.DocExpansion(DocExpansion.List);
242-
});
243-
}
261+
public static void MapOpenApi(this WebApplication app)
262+
=> app.MapOpenApi("/openapi/{documentName}/api.json");
244263
}

0 commit comments

Comments
 (0)