Skip to content

Commit

Permalink
Implement Asp.Net Core 3.0/3.1 generator (#6009) (#6025)
Browse files Browse the repository at this point in the history
* Minor changes to 2.1 templates to make them work or improve documentation

* Support for ASP.NET Core 3.0 and 3.1

* Update aspnetcore test scripts and results

* Update generated documentation

* update doc

* Update modules/openapi-generator/src/main/resources/aspnetcore/3.0/Startup.mustache

Co-Authored-By: Tatsuro Shibamura <me@shibayan.jp>

* Update modules/openapi-generator/src/main/resources/aspnetcore/3.0/Startup.mustache

Co-Authored-By: Tatsuro Shibamura <me@shibayan.jp>

Co-authored-by: William Cheng <wing328hk@gmail.com>
Co-authored-by: Tatsuro Shibamura <me@shibayan.jp>
  • Loading branch information
3 people authored May 3, 2020
1 parent f8a7475 commit e4cbaa7
Show file tree
Hide file tree
Showing 81 changed files with 5,139 additions and 51 deletions.
32 changes: 32 additions & 0 deletions bin/aspnetcore3-petstore-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash

SCRIPT="$0"
echo "# START SCRIPT: $SCRIPT"

while [ -h "$SCRIPT" ] ; do
ls=`ls -ld "$SCRIPT"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
SCRIPT="$link"
else
SCRIPT=`dirname "$SCRIPT"`/"$link"
fi
done

if [ ! -d "${APP_DIR}" ]; then
APP_DIR=`dirname "$SCRIPT"`/..
APP_DIR=`cd "${APP_DIR}"; pwd`
fi

executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"

if [ ! -f "$executable" ]
then
mvn -B clean package
fi

# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="generate -g aspnetcore -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -t modules/openapi-generator/src/main/resources/aspnetcore/3.0/ -o samples/server/petstore/aspnetcore3 --additional-properties packageGuid={3C799344-F285-4669-8FD5-7ED9B795D5C5} --additional-properties aspnetCoreVersion=3.0 $@"

java $JAVA_OPTS -jar $executable $ags
10 changes: 10 additions & 0 deletions bin/windows/aspnetcore3-petstore-server.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
set executable=.\modules\openapi-generator-cli\target\openapi-generator-cli.jar

If Not Exist %executable% (
mvn clean package
)

REM set JAVA_OPTS=%JAVA_OPTS% -Xmx1024M -DloggerPath=conf/log4j.properties
set ags=generate -i modules\openapi-generator\src\test\resources\2_0\petstore.yaml -g aspnetcore -o samples\server\petstore\aspnetcore3\ --additional-properties packageGuid={3C799344-F285-4669-8FD5-7ED9B795D5C5} --additional-properties aspnetCoreVersion=3.0

java %JAVA_OPTS% -jar %executable% %ags%
10 changes: 5 additions & 5 deletions docs/generators/aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sidebar_label: aspnetcore

| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|aspnetCoreVersion|ASP.NET Core version: 3.0 (preview4 only), 2.2, 2.1, 2.0 (deprecated)| |2.2|
|aspnetCoreVersion|ASP.NET Core version: 3.1, 3.0, 2.2, 2.1, 2.0 (deprecated)| |2.2|
|buildTarget|Target to build an application or library| |program|
|classModifier|Class Modifier can be empty, abstract| ||
|compatibilityVersion|ASP.Net Core CompatibilityVersion| |Version_2_2|
Expand All @@ -16,7 +16,7 @@ sidebar_label: aspnetcore
|licenseName|The name of the license| |NoLicense|
|licenseUrl|The URL of the license| |http://localhost|
|modelClassModifier|Model Class Modifier can be nothing or partial| |partial|
|newtonsoftVersion|Version for Microsoft.AspNetCore.Mvc.NewtonsoftJson for ASP.NET Core 3.0+| |3.0.0-preview5-19227-01|
|newtonsoftVersion|Version for Microsoft.AspNetCore.Mvc.NewtonsoftJson for ASP.NET Core 3.0+| |3.0.0|
|operationIsAsync|Set methods to async or sync (default).| |false|
|operationModifier|Operation Modifier can be virtual, abstract or partial| |virtual|
|operationResultTask|Set methods result to Task&lt;&gt;.| |false|
Expand All @@ -29,11 +29,11 @@ sidebar_label: aspnetcore
|returnICollection|Return ICollection&lt;T&gt; instead of the concrete type.| |false|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|sourceFolder|source folder for generated code| |src|
|swashbuckleVersion|Swashbucke version: 3.0.0, 4.0.0| |3.0.0|
|swashbuckleVersion|Swashbuckle version: 3.0.0, 4.0.0, 5.0.0| |3.0.0|
|useCollection|Deserialize array types to Collection&lt;T&gt; instead of List&lt;T&gt;.| |false|
|useDateTimeOffset|Use DateTimeOffset to model date-time properties| |false|
|useDefaultRouting|Use default routing for the ASP.NET Core version. For 3.0 turn off default because it is not yet supported.| |true|
|useFrameworkReference|Use frameworkReference for ASP.NET Core 3.0+ and PackageReference ASP.NET Core 2.2 or earlier.| |false|
|useDefaultRouting|Use default routing for the ASP.NET Core version.| |true|
|useFrameworkReference|Use frameworkReference for ASP.NET Core 3.0+ and PackageReference ASP.NET Core 2.2 or earlier.| |false|
|useNewtonsoft|Uses the Newtonsoft JSON library.| |true|
|useSwashbuckle|Uses the Swashbuckle.AspNetCore NuGet package for documentation.| |true|

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
public static final String NEWTONSOFT_VERSION = "newtonsoftVersion";

private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}";
private String userSecretsGuid = randomUUID().toString();

@SuppressWarnings("hiding")
protected Logger LOGGER = LoggerFactory.getLogger(AspNetCoreServerCodegen.class);

private boolean useSwashbuckle = true;
protected int serverPort = 8080;
protected String serverHost = "0.0.0.0";
protected CliOption swashbuckleVersion = new CliOption(SWASHBUCKLE_VERSION, "Swashbucke version: 3.0.0, 4.0.0");
; // default to 2.1
protected CliOption aspnetCoreVersion = new CliOption(ASPNET_CORE_VERSION, "ASP.NET Core version: 3.0 (preview4 only), 2.2, 2.1, 2.0 (deprecated)");
protected CliOption swashbuckleVersion = new CliOption(SWASHBUCKLE_VERSION, "Swashbuckle version: 3.0.0, 4.0.0, 5.0.0");
protected CliOption aspnetCoreVersion = new CliOption(ASPNET_CORE_VERSION, "ASP.NET Core version: 3.1, 3.0, 2.2, 2.1, 2.0 (deprecated)");
private CliOption classModifier = new CliOption(CLASS_MODIFIER, "Class Modifier can be empty, abstract");
private CliOption operationModifier = new CliOption(OPERATION_MODIFIER, "Operation Modifier can be virtual, abstract or partial");
private CliOption modelClassModifier = new CliOption(MODEL_CLASS_MODIFIER, "Model Class Modifier can be nothing or partial");
Expand All @@ -83,7 +83,7 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
private boolean useFrameworkReference = false;
private boolean useNewtonsoft = true;
private boolean useDefaultRouting = true;
private String newtonsoftVersion = "3.0.0-preview5-19227-01";
private String newtonsoftVersion = "3.0.0";

public AspNetCoreServerCodegen() {
super();
Expand Down Expand Up @@ -186,10 +186,11 @@ public AspNetCoreServerCodegen() {

addOption(COMPATIBILITY_VERSION, "ASP.Net Core CompatibilityVersion", compatibilityVersion);

aspnetCoreVersion.addEnum("2.0", "ASP.NET COre 2.0");
aspnetCoreVersion.addEnum("2.0", "ASP.NET Core 2.0");
aspnetCoreVersion.addEnum("2.1", "ASP.NET Core 2.1");
aspnetCoreVersion.addEnum("2.2", "ASP.NET Core 2.2");
aspnetCoreVersion.addEnum("3.0", "ASP.NET Core 3.0");
aspnetCoreVersion.addEnum("3.1", "ASP.NET Core 3.1");
aspnetCoreVersion.setDefault("2.2");
aspnetCoreVersion.setOptValue(aspnetCoreVersion.getDefault());
addOption(aspnetCoreVersion.getOpt(), aspnetCoreVersion.getDescription(), aspnetCoreVersion.getOptValue());
Expand Down Expand Up @@ -227,7 +228,7 @@ public AspNetCoreServerCodegen() {
isLibrary);

addSwitch(USE_FRAMEWORK_REFERENCE,
"Use frameworkReference for ASP.NET Core 3.0+ and PackageReference ASP.NET Core 2.2 or earlier.",
"Use frameworkReference for ASP.NET Core 3.0+ and PackageReference ASP.NET Core 2.2 or earlier.",
useFrameworkReference);

addSwitch(USE_NEWTONSOFT,
Expand All @@ -239,7 +240,7 @@ public AspNetCoreServerCodegen() {
newtonsoftVersion);

addSwitch(USE_DEFAULT_ROUTING,
"Use default routing for the ASP.NET Core version. For 3.0 turn off default because it is not yet supported.",
"Use default routing for the ASP.NET Core version.",
useDefaultRouting);

addOption(CodegenConstants.ENUM_NAME_SUFFIX,
Expand All @@ -263,7 +264,7 @@ public AspNetCoreServerCodegen() {
addOption(operationModifier.getOpt(), operationModifier.getDescription(), operationModifier.getOptValue());

buildTarget.addEnum("program", "Generate code for a standalone server");
buildTarget.addEnum("library", "Generate code for a server abstract class lbrary");
buildTarget.addEnum("library", "Generate code for a server abstract class library");
buildTarget.setDefault("program");
buildTarget.setOptValue(buildTarget.getDefault());
addOption(buildTarget.getOpt(), buildTarget.getDescription(), buildTarget.getOptValue());
Expand All @@ -286,7 +287,6 @@ public AspNetCoreServerCodegen() {
modelClassModifier.setDefault("partial");
modelClassModifier.setOptValue(modelClassModifier.getDefault());
addOption(modelClassModifier.getOpt(), modelClassModifier.getDescription(), modelClassModifier.getOptValue());

}

@Override
Expand Down Expand Up @@ -320,6 +320,7 @@ public void processOpts() {
setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID));
}
additionalProperties.put("packageGuid", packageGuid);
additionalProperties.put("userSecretsGuid", userSecretsGuid);

if (!additionalProperties.containsKey(NEWTONSOFT_VERSION)) {
additionalProperties.put(NEWTONSOFT_VERSION, newtonsoftVersion);
Expand Down Expand Up @@ -367,6 +368,9 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("gitignore", packageFolder, ".gitignore"));
supportingFiles.add(new SupportingFile("validateModel.mustache", packageFolder + File.separator + "Attributes", "ValidateModelStateAttribute.cs"));
supportingFiles.add(new SupportingFile("typeConverter.mustache", packageFolder + File.separator + "Converters", "CustomEnumConverter.cs"));
if (aspnetCoreVersion.getOptValue().startsWith("3.")) {
supportingFiles.add(new SupportingFile("OpenApi" + File.separator + "TypeExtensions.mustache", packageFolder + File.separator + "OpenApi", "TypeExtensions.cs"));
}
supportingFiles.add(new SupportingFile("Project.csproj.mustache", packageFolder, packageName + ".csproj"));
if (!isLibrary) {
supportingFiles.add(new SupportingFile("Dockerfile.mustache", packageFolder, "Dockerfile"));
Expand Down Expand Up @@ -532,19 +536,34 @@ private void setBuildTarget() {

private void setAspnetCoreVersion(String packageFolder) {
setCliOption(aspnetCoreVersion);
if ("2.0".equals(aspnetCoreVersion.getOptValue())) {
embeddedTemplateDir = templateDir = "aspnetcore/2.0";

if (aspnetCoreVersion.getOptValue().startsWith("3.")) {
compatibilityVersion = null;
} else if ("2.0".equals(aspnetCoreVersion.getOptValue())) {
supportingFiles.add(new SupportingFile("web.config", packageFolder, "web.config"));
LOGGER.info("ASP.NET core version: 2.0");
compatibilityVersion = null;
} else {
// default, do nothing
LOGGER.info("ASP.NET core version: " + aspnetCoreVersion.getOptValue());
compatibilityVersion = "Version_" + aspnetCoreVersion.getOptValue().replace(".", "_");
}
LOGGER.info("ASP.NET core version: " + aspnetCoreVersion.getOptValue());
embeddedTemplateDir = templateDir = "aspnetcore/" + determineTemplateVersion(aspnetCoreVersion.getOptValue());
additionalProperties.put(COMPATIBILITY_VERSION, compatibilityVersion);
}

private String determineTemplateVersion(String frameworkVersion) {
switch (frameworkVersion) {
case "3.1":
return "3.0";

case "2.2":
return "2.1";

default:
return frameworkVersion;
}
}

private void setUseSwashbuckle() {
if (isLibrary) {
LOGGER.warn("buildTarget is " + buildTarget.getOptValue() + " so changing default isLibrary to false ");
Expand Down Expand Up @@ -572,7 +591,7 @@ private void setOperationIsAsync() {

private void setIsFramework() {
if (aspnetCoreVersion.getOptValue().startsWith("3.")) {// default, do nothing
LOGGER.warn("ASP.NET core version is " + aspnetCoreVersion.getOptValue() + " so changing to use frameworkReference instead of packageReference ");
LOGGER.warn("ASP.NET core version is " + aspnetCoreVersion.getOptValue() + " so changing to use frameworkReference instead of packageReference ");
useFrameworkReference = true;
additionalProperties.put(USE_FRAMEWORK_REFERENCE, useFrameworkReference);
} else {
Expand All @@ -582,7 +601,6 @@ private void setIsFramework() {
additionalProperties.put(USE_FRAMEWORK_REFERENCE, useFrameworkReference);
}
}

}

private void setUseNewtonsoft() {
Expand Down Expand Up @@ -617,8 +635,8 @@ private void setSwashbuckleVersion() {
setCliOption(swashbuckleVersion);

if (aspnetCoreVersion.getOptValue().startsWith("3.")) {
LOGGER.warn("ASP.NET core version is " + aspnetCoreVersion.getOptValue() + " so changing default Swashbuckle version to 4.0.0.");
swashbuckleVersion.setOptValue("4.0.0");
LOGGER.warn("ASP.NET core version is " + aspnetCoreVersion.getOptValue() + " so changing default Swashbuckle version to 5.0.0.");
swashbuckleVersion.setOptValue("5.0.0");
additionalProperties.put(SWASHBUCKLE_VERSION, swashbuckleVersion.getOptValue());
} else {
// default, do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,39 @@ using System.Threading.Tasks;

namespace {{packageName}}.Authentication
{
/// <summary>
/// A requirement that an ApiKey must be present.
/// </summary>
public class ApiKeyRequirement : IAuthorizationRequirement
{
public IReadOnlyList<string> ApiKeys { get; set; }

public string PolicyName { get; set; }
/// <summary>
/// Get the list of api keys
/// </summary>
public IReadOnlyList<string> ApiKeys { get; }

/// <summary>
/// Get the policy name,
/// </summary>
public string PolicyName { get; }

/// <summary>
/// Create a new instance of the <see cref="ApiKeyRequirement"/> class.
/// </summary>
/// <param name="apiKeys"></param>
/// <param name="policyName"></param>
public ApiKeyRequirement(IEnumerable<string> apiKeys, string policyName)
{
ApiKeys = apiKeys?.ToList() ?? new List<string>();
PolicyName = policyName;
}
}

/// <summary>
/// Enforce that an api key is present.
/// </summary>
public class ApiKeyRequirementHandler : AuthorizationHandler<ApiKeyRequirement>
{
/// <copydoc cref="AuthorizationHandler{T}.HandleRequirementAsync" />
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ApiKeyRequirement requirement)
{
SucceedRequirementIfApiKeyPresentAndValid(context, requirement);
Expand All @@ -32,27 +49,22 @@ namespace {{packageName}}.Authentication
private void SucceedRequirementIfApiKeyPresentAndValid(AuthorizationHandlerContext context, ApiKeyRequirement requirement)
{
{{#authMethods}}{{#isApiKey}}
{{#-first}}
if (context.Resource is AuthorizationFilterContext authorizationFilterContext)
{
var apiKey = "";
{{/-first}}
{{#isKeyInHeader}}
apiKey = authorizationFilterContext.HttpContext.Request.Headers["{{keyParamName}}"].FirstOrDefault();
var apiKey = authorizationFilterContext.HttpContext.Request.Headers["{{keyParamName}}"].FirstOrDefault();
{{/isKeyInHeader}}
{{#isKeyInQuery}}
apiKey = authorizationFilterContext.HttpContext.Request.Query["{{keyParamName}}"].FirstOrDefault();
var apiKey = authorizationFilterContext.HttpContext.Request.Query["{{keyParamName}}"].FirstOrDefault();
{{/isKeyInQuery}}
{{#isKeyInCookie}}
apiKey = authorizationFilterContext.HttpContext.Request.Cookies["{{keyParamName}}"] ?? null;
var apiKey = authorizationFilterContext.HttpContext.Request.Cookies["{{keyParamName}}"] ?? null;
{{/isKeyInCookie}}
if (requirement.PolicyName == "{{name}}" && apiKey != null && requirement.ApiKeys.Any(requiredApiKey => apiKey == requiredApiKey))
{
context.Succeed(requirement);
}
{{#-last}}
}
{{/-last}}
{{/isApiKey}}{{/authMethods}}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@
{{/useSwashbuckle}}
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.4" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# {{packageName}} - ASP.NET Core 2.0 Server
# {{packageName}} - ASP.NET Core {{aspnetCoreVersion}} Server

{{#appDescriptionWithNewLines}}
{{{appDescriptionWithNewLines}}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,13 @@ namespace {{packageName}}
{
{{#authMethods}}
{{#isApiKey}}
{{#-first}}
services.AddTransient<IAuthorizationHandler, ApiKeyRequirementHandler>();
services.AddAuthorization(authConfig =>
{
{{/-first}}
authConfig.AddPolicy("{{name}}",
policyBuilder => policyBuilder
.AddRequirements(new ApiKeyRequirement(new[] { "my-secret-key" },"{{name}}")));
{{#-last}}
});
{{/-last}}
{{/isApiKey}}
{{/authMethods}}

Expand All @@ -81,7 +77,7 @@ namespace {{packageName}}
{
Version = "{{#version}}{{{version}}}{{/version}}{{^version}}v1{{/version}}",
Title = "{{#appName}}{{{appName}}}{{/appName}}{{^appName}}{{packageName}}{{/appName}}",
Description = "{{#appName}}{{{appName}}}{{/appName}}{{^appName}}{{packageName}}{{/appName}} (ASP.NET Core 2.0)",
Description = "{{#appName}}{{{appName}}}{{/appName}}{{^appName}}{{packageName}}{{/appName}} (ASP.NET Core {{aspnetCoreVersion}})",
Contact = new Contact()
{
Name = "{{#infoName}}{{{infoName}}}{{/infoName}}{{^infoName}}OpenAPI-Generator Contributors{{/infoName}}",
Expand Down
Loading

0 comments on commit e4cbaa7

Please sign in to comment.