Skip to content

Commit 20d863d

Browse files
authored
Merge pull request #9529 from swagger-api/aspnet_core_2_2_version_support
aspnet core 2 2 version support
2 parents 5fa2eb9 + 119d32c commit 20d863d

File tree

94 files changed

+7864
-97
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+7864
-97
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
SCRIPT="$0"
4+
5+
while [ -h "$SCRIPT" ] ; do
6+
ls=`ls -ld "$SCRIPT"`
7+
link=`expr "$ls" : '.*-> \(.*\)$'`
8+
if expr "$link" : '/.*' > /dev/null; then
9+
SCRIPT="$link"
10+
else
11+
SCRIPT=`dirname "$SCRIPT"`/"$link"
12+
fi
13+
done
14+
15+
if [ ! -d "${APP_DIR}" ]; then
16+
APP_DIR=`dirname "$SCRIPT"`/..
17+
APP_DIR=`cd "${APP_DIR}"; pwd`
18+
fi
19+
20+
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
21+
22+
if [ ! -f "$executable" ]
23+
then
24+
mvn clean package
25+
fi
26+
27+
# if you've executed sbt assembly previously it will use that instead.
28+
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
29+
ags="$@ generate -l aspnetcore -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -o samples/server/petstore/aspnetcore-interface-controller --additional-properties packageGuid={3C799344-F285-4669-8FD5-7ED9B795D5C5} --additional-properties interface-controller=true"
30+
31+
java $JAVA_OPTS -jar $executable $ags
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env bash
2+
3+
SCRIPT="$0"
4+
5+
while [ -h "$SCRIPT" ] ; do
6+
ls=`ls -ld "$SCRIPT"`
7+
link=`expr "$ls" : '.*-> \(.*\)$'`
8+
if expr "$link" : '/.*' > /dev/null; then
9+
SCRIPT="$link"
10+
else
11+
SCRIPT=`dirname "$SCRIPT"`/"$link"
12+
fi
13+
done
14+
15+
if [ ! -d "${APP_DIR}" ]; then
16+
APP_DIR=`dirname "$SCRIPT"`/..
17+
APP_DIR=`cd "${APP_DIR}"; pwd`
18+
fi
19+
20+
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
21+
22+
if [ ! -f "$executable" ]
23+
then
24+
mvn clean package
25+
fi
26+
27+
# if you've executed sbt assembly previously it will use that instead.
28+
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
29+
ags="$@ generate -l aspnetcore -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -o samples/server/petstore/aspnetcore-interface-only --additional-properties packageGuid={3C799344-F285-4669-8FD5-7ED9B795D5C5} --additional-properties interface-only=true"
30+
31+
java $JAVA_OPTS -jar $executable $ags

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: 108 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,44 @@ 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+
148+
additionalProperties.put("aspNetCoreVersion", aspNetCoreVersion);
149+
106150
additionalProperties.put("packageGuid", packageGuid);
107151

108152
additionalProperties.put("dockerTag", this.packageName.toLowerCase());
109153

110154
apiPackage = packageName + ".Controllers";
111155
modelPackage = packageName + ".Models";
112156

113-
String packageFolder = sourceFolder + File.separator + packageName;
114-
115157
supportingFiles.add(new SupportingFile("NuGet.Config", "", "NuGet.Config"));
116158
supportingFiles.add(new SupportingFile("build.sh.mustache", "", "build.sh"));
117159
supportingFiles.add(new SupportingFile("build.bat.mustache", "", "build.bat"));
@@ -122,12 +164,9 @@ public void processOpts() {
122164
supportingFiles.add(new SupportingFile("appsettings.json", packageFolder, "appsettings.json"));
123165

124166
supportingFiles.add(new SupportingFile("Startup.mustache", packageFolder, "Startup.cs"));
125-
supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs"));
126167
supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs"));
127168
supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config"));
128169

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

133172
supportingFiles.add(new SupportingFile("Filters" + File.separator + "BasePathFilter.mustache", packageFolder + File.separator + "Filters", "BasePathFilter.cs"));
@@ -162,6 +201,16 @@ public String apiFileFolder() {
162201
return outputFolder + File.separator + sourceFolder + File.separator + packageName + File.separator + "Controllers";
163202
}
164203

204+
@Override
205+
public String apiFilename(String templateName, String tag) {
206+
boolean isInterface = templateName.equalsIgnoreCase("icontroller.mustache");
207+
String suffix = apiTemplateFiles().get(templateName);
208+
if (isInterface) {
209+
return apiFileFolder() + "/I" + toApiFilename(tag) + suffix;
210+
}
211+
return apiFileFolder() + '/' + toApiFilename(tag) + suffix;
212+
}
213+
165214
@Override
166215
public String modelFileFolder() {
167216
return outputFolder + File.separator + sourceFolder + File.separator + packageName + File.separator + "Models";
@@ -204,4 +253,57 @@ public Mustache.Compiler processCompiler(Mustache.Compiler compiler) {
204253
// To avoid unexpected behaviors when options are passed programmatically such as { "useCollection": "" }
205254
return super.processCompiler(compiler).emptyStringIsFalse(true);
206255
}
256+
257+
@Override
258+
public List<CodegenSecurity> fromSecurity(Map<String, SecuritySchemeDefinition> schemes) {
259+
final List<CodegenSecurity> securities = super.fromSecurity(schemes);
260+
if (securities == null || securities.isEmpty()) {
261+
return securities;
262+
}
263+
boolean hasBasic = false;
264+
boolean hasBearer = false;
265+
boolean hasApiKey = false;
266+
for (int index = 0; index < securities.size(); index++) {
267+
final CodegenSecurity codegenSecurity = securities.get(index);
268+
if (codegenSecurity.isBasic) {
269+
hasBasic = true;
270+
}
271+
if (codegenSecurity.isApiKey) {
272+
hasApiKey = true;
273+
}
274+
}
275+
final String packageFolder = sourceFolder + File.separator + packageName;
276+
if (hasBasic) {
277+
supportingFiles.add(new SupportingFile("Security/BasicAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "BasicAuthenticationHandler.cs"));
278+
}
279+
if (hasBearer) {
280+
supportingFiles.add(new SupportingFile("Security/BearerAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "BearerAuthenticationHandler.cs"));
281+
}
282+
if (hasApiKey) {
283+
supportingFiles.add(new SupportingFile("Security/ApiKeyAuthenticationHandler.mustache", packageFolder + File.separator + "Security", "ApiKeyAuthenticationHandler.cs"));
284+
}
285+
return securities;
286+
}
287+
288+
private void addInterfaceControllerTemplate(boolean interfaceOnly, boolean interfaceController) {
289+
if (interfaceController) {
290+
apiTemplateFiles.put("icontroller.mustache", ".cs");
291+
additionalProperties.put("interfaceController", Boolean.TRUE);
292+
}
293+
if (interfaceOnly) {
294+
apiTemplateFiles.clear();
295+
apiTemplateFiles.put("icontroller.mustache", ".cs");
296+
}
297+
}
298+
299+
private void setAspNetCoreVersion(String optionValue) {
300+
if (StringUtils.isBlank(optionValue)) {
301+
return;
302+
}
303+
this.aspNetCoreVersion = optionValue;
304+
if (!this.aspNetCoreVersion.equals("2.0") && !this.aspNetCoreVersion.equals("2.1") && !this.aspNetCoreVersion.equals("2.2")) {
305+
LOGGER.error("version '" + this.aspNetCoreVersion + "' is not supported, switching to default version: '" + DEFAULT_ASP_NET_CORE_VERSION + "'");
306+
this.aspNetCoreVersion = DEFAULT_ASP_NET_CORE_VERSION;
307+
}
308+
}
207309
}
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: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
/// <summary>
24+
/// {{#summary}}{{summary}}{{/summary}}
25+
/// </summary>
26+
{{#notes}}/// <remarks>{{notes}}</remarks>{{/notes}}{{#allParams}}
27+
/// <param name="{{paramName}}">{{description}}</param>{{/allParams}}{{#responses}}
28+
/// <response code="{{code}}">{{message}}</response>{{/responses}}
29+
[{{httpMethod}}]
30+
[Route("{{{basePathWithoutHost}}}{{{path}}}")]
31+
{{#authMethods}}
32+
{{#-first}}
33+
{{#isBasic}}
34+
[Authorize(AuthenticationSchemes = BasicAuthenticationHandler.SchemeName)]
35+
{{/isBasic}}
36+
{{#isBearer}}
37+
[Authorize(AuthenticationSchemes = BearerAuthenticationHandler.SchemeName)]
38+
{{/isBearer}}
39+
{{#isApiKey}}
40+
[Authorize(AuthenticationSchemes = ApiKeyAuthenticationHandler.SchemeName)]
41+
{{/isApiKey}}
42+
{{/-first}}
43+
{{/authMethods}}
44+
[ValidateModelState]
45+
[SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}}
46+
[SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}
47+
public virtual IActionResult {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}})
48+
{ {{#responses}}
49+
{{#dataType}}
50+
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
51+
// return StatusCode({{code}}, default({{&dataType}}));
52+
{{/dataType}}
53+
{{^dataType}}
54+
//TODO: Uncomment the next line to return response {{code}} or use other options such as return this.NotFound(), return this.BadRequest(..), ...
55+
// return StatusCode({{code}});
56+
{{/dataType}}{{/responses}}
57+
{{#returnType}}
58+
string exampleJson = null;
59+
{{#examples}}
60+
exampleJson = "{{{example}}}";
61+
{{/examples}}
62+
{{#isListCollection}}{{>listReturn}}{{/isListCollection}}{{^isListCollection}}{{#isMapContainer}}{{>mapReturn}}{{/isMapContainer}}{{^isMapContainer}}{{>objectReturn}}{{/isMapContainer}}{{/isListCollection}}
63+
{{!TODO: defaultResponse, examples, auth, consumes, produces, nickname, externalDocs, imports, security}}
64+
//TODO: Change the data returned
65+
return new ObjectResult(example);{{/returnType}}{{^returnType}}
66+
throw new NotImplementedException();{{/returnType}}
67+
}
68+
{{/operation}}
69+
}
70+
{{/operations}}
71+
}

0 commit comments

Comments
 (0)