Skip to content

Commit 215a936

Browse files
HugoMariofrantuma
authored andcommitted
added support for 2.2 version
1 parent 5fa2eb9 commit 215a936

File tree

11 files changed

+400
-8
lines changed

11 files changed

+400
-8
lines changed

modules/swagger-codegen/src/main/java/io/swagger/codegen/CodegenConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,8 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
230230

231231
public static final String STRIP_PACKAGE_NAME = "stripPackageName";
232232
public static final String STRIP_PACKAGE_NAME_DESC = "Whether to strip leading dot-separated packages from generated model classes";
233+
234+
public static final String ASP_NET_CORE_VERSION = "aspnetCoreVersion";
235+
public static final String INTERFACE_ONLY = "interface-only";
236+
public static final String INTERFACE_CONTROLLER = "interface-controller";
233237
}

modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/AspNetCoreServerCodegen.java

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
import io.swagger.codegen.*;
99
import io.swagger.models.*;
10+
import io.swagger.models.auth.SecuritySchemeDefinition;
1011
import io.swagger.util.Json;
1112

13+
import org.apache.commons.lang3.StringUtils;
1214
import org.slf4j.Logger;
1315
import org.slf4j.LoggerFactory;
1416

@@ -21,6 +23,8 @@
2123
public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
2224

2325
private String packageGuid = "{" + randomUUID().toString().toUpperCase() + "}";
26+
private final String DEFAULT_ASP_NET_CORE_VERSION = "2.2";
27+
private String aspNetCoreVersion;
2428

2529
@SuppressWarnings("hiding")
2630
protected Logger LOGGER = LoggerFactory.getLogger(AspNetCoreServerCodegen.class);
@@ -32,7 +36,7 @@ public AspNetCoreServerCodegen() {
3236
outputFolder = "generated-code" + File.separator + this.getName();
3337

3438
modelTemplateFiles.put("model.mustache", ".cs");
35-
apiTemplateFiles.put("controller.mustache", ".cs");
39+
aspNetCoreVersion = DEFAULT_ASP_NET_CORE_VERSION;
3640

3741
// contextually reserved words
3842
// NOTE: C# uses camel cased reserved words, while models are title cased. We don't want lowercase comparisons.
@@ -59,6 +63,18 @@ public AspNetCoreServerCodegen() {
5963
CodegenConstants.SOURCE_FOLDER_DESC,
6064
sourceFolder);
6165

66+
addOption(CodegenConstants.ASP_NET_CORE_VERSION,
67+
"aspnetcore version to use, current options are: 2.0, 2.1 and 2.2 (default)",
68+
this.aspNetCoreVersion);
69+
70+
addOption(CodegenConstants.INTERFACE_ONLY,
71+
"creates interfaces controller only",
72+
null);
73+
74+
addOption(CodegenConstants.INTERFACE_CONTROLLER,
75+
"creates interfaces and default implementation for controllers",
76+
null);
77+
6278
addOption(CodegenConstants.PRESERVE_COMMENT_NEWLINES,
6379
"Preserve newlines in comments",
6480
String.valueOf(this.preserveNewLines));
@@ -100,18 +116,42 @@ public String getHelp() {
100116
public void processOpts() {
101117
super.processOpts();
102118

119+
if (additionalProperties.containsKey("aspnetCoreVersion")) {
120+
setAspNetCoreVersion(String.valueOf(additionalProperties.get("aspnetCoreVersion")));
121+
}
122+
103123
if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) {
104124
setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID));
105125
}
126+
127+
String packageFolder = sourceFolder + File.separator + packageName;
128+
129+
boolean interfaceOnly = Boolean.valueOf(String.valueOf(additionalProperties.get(CodegenConstants.INTERFACE_ONLY)));
130+
boolean interfaceController = Boolean.valueOf(String.valueOf(additionalProperties.get(CodegenConstants.INTERFACE_CONTROLLER)));
131+
132+
if (this.aspNetCoreVersion.equals("2.0")) {
133+
apiTemplateFiles.put("controller.mustache", ".cs");
134+
addInterfaceControllerTemplate(interfaceOnly, interfaceController);
135+
136+
supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs"));
137+
supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, this.packageName + ".csproj"));
138+
supportingFiles.add(new SupportingFile("Dockerfile.mustache", packageFolder, "Dockerfile"));
139+
} else {
140+
apiTemplateFiles.put("2.1/controller.mustache", ".cs");
141+
addInterfaceControllerTemplate(interfaceOnly, interfaceController);
142+
143+
supportingFiles.add(new SupportingFile("2.1/Program.mustache", packageFolder, "Program.cs"));
144+
supportingFiles.add(new SupportingFile("2.1/Project.csproj.mustache", packageFolder, this.packageName + ".csproj"));
145+
supportingFiles.add(new SupportingFile("2.1/Dockerfile.mustache", packageFolder, "Dockerfile"));
146+
}
147+
106148
additionalProperties.put("packageGuid", packageGuid);
107149

108150
additionalProperties.put("dockerTag", this.packageName.toLowerCase());
109151

110152
apiPackage = packageName + ".Controllers";
111153
modelPackage = packageName + ".Models";
112154

113-
String packageFolder = sourceFolder + File.separator + packageName;
114-
115155
supportingFiles.add(new SupportingFile("NuGet.Config", "", "NuGet.Config"));
116156
supportingFiles.add(new SupportingFile("build.sh.mustache", "", "build.sh"));
117157
supportingFiles.add(new SupportingFile("build.bat.mustache", "", "build.bat"));
@@ -122,12 +162,9 @@ public void processOpts() {
122162
supportingFiles.add(new SupportingFile("appsettings.json", packageFolder, "appsettings.json"));
123163

124164
supportingFiles.add(new SupportingFile("Startup.mustache", packageFolder, "Startup.cs"));
125-
supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs"));
126165
supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs"));
127166
supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config"));
128167

129-
supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, this.packageName + ".csproj"));
130-
131168
supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json", packageFolder + File.separator + "Properties", "launchSettings.json"));
132169

133170
supportingFiles.add(new SupportingFile("Filters" + File.separator + "BasePathFilter.mustache", packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
@@ -204,4 +241,57 @@ public Mustache.Compiler processCompiler(Mustache.Compiler compiler) {
204241
// To avoid unexpected behaviors when options are passed programmatically such as { "useCollection": "" }
205242
return super.processCompiler(compiler).emptyStringIsFalse(true);
206243
}
244+
245+
@Override
246+
public List<CodegenSecurity> fromSecurity(Map<String, SecuritySchemeDefinition> schemes) {
247+
final List<CodegenSecurity> securities = super.fromSecurity(schemes);
248+
if (securities == null || securities.isEmpty()) {
249+
return securities;
250+
}
251+
boolean hasBasic = false;
252+
boolean hasBearer = false;
253+
boolean hasApiKey = false;
254+
for (int index = 0; index < securities.size(); index++) {
255+
final CodegenSecurity codegenSecurity = securities.get(index);
256+
if (codegenSecurity.isBasic) {
257+
hasBasic = true;
258+
}
259+
if (codegenSecurity.isApiKey) {
260+
hasApiKey = true;
261+
}
262+
}
263+
final String packageFolder = sourceFolder + File.separator + packageName;
264+
if (hasBasic) {
265+
supportingFiles.add(new SupportingFile("Security/BasicAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "BasicAuthenticationHandler.cs"));
266+
}
267+
if (hasBearer) {
268+
supportingFiles.add(new SupportingFile("Security/BearerAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "BearerAuthenticationHandler.cs"));
269+
}
270+
if (hasApiKey) {
271+
supportingFiles.add(new SupportingFile("Security/ApiKeyAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "ApiKeyAuthenticationHandler.cs"));
272+
}
273+
return securities;
274+
}
275+
276+
private void addInterfaceControllerTemplate(boolean interfaceOnly, boolean interfaceController) {
277+
if (interfaceController) {
278+
apiTemplateFiles.put("icontroller.mustache", ".cs");
279+
additionalProperties.put("interfaceController", Boolean.TRUE);
280+
}
281+
if (interfaceOnly) {
282+
apiTemplateFiles.clear();
283+
apiTemplateFiles.put("icontroller.mustache", ".cs");
284+
}
285+
}
286+
287+
private void setAspNetCoreVersion(String optionValue) {
288+
if (StringUtils.isBlank(optionValue)) {
289+
return;
290+
}
291+
this.aspNetCoreVersion = optionValue;
292+
if (!this.aspNetCoreVersion.equals("2.0") && !this.aspNetCoreVersion.equals("2.1") && !this.aspNetCoreVersion.equals("2.2")) {
293+
LOGGER.error("version '" + this.aspNetCoreVersion + "' is not supported, switching to default version: '" + DEFAULT_ASP_NET_CORE_VERSION + "'");
294+
this.aspNetCoreVersion = DEFAULT_ASP_NET_CORE_VERSION;
295+
}
296+
}
207297
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM mcr.microsoft.com/dotnet/core/sdk:{{aspNetCoreVersion}} AS build-env
2+
WORKDIR /app
3+
4+
ENV DOTNET_CLI_TELEMETRY_OPTOUT 1
5+
6+
# copy csproj and restore as distinct layers
7+
COPY *.csproj ./
8+
RUN dotnet restore
9+
10+
# copy everything else and build
11+
COPY . ./
12+
RUN dotnet publish -c Release -o out
13+
14+
# build runtime image
15+
FROM mcr.microsoft.com/dotnet/core/aspnet:{{aspNetCoreVersion}}
16+
WORKDIR /app
17+
COPY --from=build-env /app/out .
18+
ENTRYPOINT ["dotnet", "{{packageName}}.dll"]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using Microsoft.AspNetCore.Hosting;
2+
using Microsoft.AspNetCore;
3+
4+
namespace {{packageName}}
5+
{
6+
/// <summary>
7+
/// Program
8+
/// </summary>
9+
public class Program
10+
{
11+
/// <summary>
12+
/// Main
13+
/// </summary>
14+
/// <param name="args"></param>
15+
public static void Main(string[] args)
16+
{
17+
CreateWebHostBuilder(args).Build().Run();
18+
}
19+
20+
/// <summary>
21+
/// Create the web host builder.
22+
/// </summary>
23+
/// <param name="args"></param>
24+
/// <returns>IWebHostBuilder</returns>
25+
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
26+
WebHost.CreateDefaultBuilder(args)
27+
.UseStartup<Startup>();
28+
}
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<Description>{{packageName}}</Description>
4+
<Copyright>{{packageName}}</Copyright>
5+
<TargetFramework>netcoreapp{{aspNetCoreVersion}}</TargetFramework>
6+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
7+
<PreserveCompilationContext>true</PreserveCompilationContext>
8+
<AssemblyName>{{packageName}}</AssemblyName>
9+
<PackageId>{{packageName}}</PackageId>
10+
</PropertyGroup>
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.AspNetCore.App" />
13+
<PackageReference Include="Swashbuckle.AspNetCore" Version="3.0.0"/>
14+
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="3.0.0" />
15+
</ItemGroup>
16+
<ItemGroup>
17+
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
18+
</ItemGroup>
19+
</Project>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{{>partial_header}}
2+
using System;
3+
using System.Collections.Generic;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Swashbuckle.AspNetCore.Annotations;
6+
using Swashbuckle.AspNetCore.SwaggerGen;
7+
using Newtonsoft.Json;
8+
using System.ComponentModel.DataAnnotations;
9+
using {{packageName}}.Attributes;
10+
using {{packageName}}.Security;
11+
using Microsoft.AspNetCore.Authorization;
12+
using {{modelPackage}};
13+
14+
namespace {{packageName}}.Controllers
15+
{ {{#operations}}
16+
/// <summary>
17+
/// {{description}}
18+
/// </summary>{{#description}}
19+
[Description("{{description}}")]{{/description}}
20+
[ApiController]
21+
public class {{classname}}Controller : ControllerBase{{#interfaceController}}, I{{classname}}Controller{{/interfaceController}}
22+
{ {{#operation}}
23+
{{#contents}}
24+
/// <summary>
25+
/// {{#summary}}{{summary}}{{/summary}}
26+
/// </summary>{{#notes}}
27+
/// <remarks>{{notes}}</remarks>{{/notes}}{{#parameters}}
28+
/// <param name="{{paramName}}">{{description}}</param>{{/parameters}}{{#responses}}
29+
/// <response code="{{code}}">{{message}}</response>{{/responses}}
30+
[{{httpMethod}}]
31+
[Route("{{{basePathWithoutHost}}}{{{path}}}")]
32+
{{#authMethods}}
33+
{{#-first}}
34+
{{#isBasic}}
35+
[Authorize(AuthenticationSchemes = BasicAuthenticationHandler.SchemeName)]
36+
{{/isBasic}}
37+
{{#isBearer}}
38+
[Authorize(AuthenticationSchemes = BearerAuthenticationHandler.SchemeName)]
39+
{{/isBearer}}
40+
{{#isApiKey}}
41+
[Authorize(AuthenticationSchemes = ApiKeyAuthenticationHandler.SchemeName)]
42+
{{/isApiKey}}
43+
{{/-first}}
44+
{{/authMethods}}
45+
[ValidateModelState]
46+
[SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}}
47+
[SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}
48+
public virtual IActionResult {{operationId}}({{#parameters}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/parameters}})
49+
{ {{#responses}}
50+
{{#dataType}}
51+
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
52+
// return StatusCode({{code}}, default({{&dataType}}));
53+
{{/dataType}}
54+
{{^dataType}}
55+
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
56+
// return StatusCode({{code}});
57+
{{/dataType}}{{/responses}}
58+
{{#returnType}}
59+
string exampleJson = null;
60+
{{#examples}}
61+
exampleJson = "{{{example}}}";
62+
{{/examples}}
63+
{{#isListCollection}}{{>listReturn}}{{/isListCollection}}{{^isListCollection}}{{#isMapContainer}}{{>mapReturn}}{{/isMapContainer}}{{^isMapContainer}}{{>objectReturn}}{{/isMapContainer}}{{/isListCollection}}
64+
{{!TODO: defaultResponse, examples, auth, consumes, produces, nickname, externalDocs, imports, security}}
65+
//TODO: Change the data returned
66+
return new ObjectResult(example);{{/returnType}}{{^returnType}}
67+
throw new NotImplementedException();{{/returnType}}
68+
}
69+
{{/contents}}
70+
{{/operation}}
71+
}
72+
{{/operations}}
73+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System;
2+
using System.Net.Http.Headers;
3+
using System.Security.Claims;
4+
using System.Text;
5+
using System.Text.Encodings.Web;
6+
using System.Threading.Tasks;
7+
using Microsoft.AspNetCore.Authentication;
8+
using Microsoft.Extensions.Logging;
9+
using Microsoft.Extensions.Options;
10+
11+
namespace {{packageName}}.Security
12+
{
13+
/// <summary>
14+
/// class to handle api_key security.
15+
/// </summary>
16+
public class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
17+
{
18+
/// <summary>
19+
/// scheme name for authentication handler.
20+
/// </summary>
21+
public const string SchemeName = "ApiKey";
22+
23+
public ApiKeyAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
24+
{
25+
}
26+
27+
/// <summary>
28+
/// verify that require api key header exist and handle authorization.
29+
/// </summary>
30+
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
31+
{
32+
if (!Request.Headers.ContainsKey("api_key"))
33+
{
34+
return AuthenticateResult.Fail("Missing Authorization Header");
35+
}
36+
37+
// do magic here!
38+
39+
var claims = new[] {
40+
new Claim(ClaimTypes.NameIdentifier, "changeme"),
41+
new Claim(ClaimTypes.Name, "changeme"),
42+
};
43+
var identity = new ClaimsIdentity(claims, Scheme.Name);
44+
var principal = new ClaimsPrincipal(identity);
45+
var ticket = new AuthenticationTicket(principal, Scheme.Name);
46+
47+
return AuthenticateResult.Success(ticket);
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)