[aspnetcore] Support async tasks and some code cleanups (#2629)

* Add first cutasync support and small cleanups

* Add apiPackage, modePacke to use for namespaces, for library don't generate wwwroot and dont make model class partial and default to no swashbuckle,  , workarodun empty string cliOption

* Update docs

* Don't add async for library

* Fix generated program for async and task

* Default models names space should be <package>.Models

* Remove commented out code

* Remove unnecessary code, fix a comparison and add an else fo an if statment.

* Update docs
This commit is contained in:
Amit Joshi 2019-04-19 12:32:04 -04:00 committed by William Cheng
parent 89eb603c17
commit 1e4f4ab9f2
7 changed files with 125 additions and 38 deletions

View File

@ -23,7 +23,11 @@ sidebar_label: aspnetcore
|useCollection|Deserialize array types to Collection&lt;T&gt; instead of List&lt;T&gt;.| |false|
|returnICollection|Return ICollection&lt;T&gt; instead of the concrete type.| |false|
|useSwashbuckle|Uses the Swashbuckle.AspNetCore NuGet package for documentation.| |true|
|isLibrary|Is the build a library| |false|
|classModifier|Class Modifier can be empty, abstract| ||
|operationModifier|Operation Modifier can be virtual, abstract or partial| |virtual|
|buildTarget|Target to build an application or library| |program|
|generateBody|Generates method body.| |true|
|operationIsAsync|Set methods to async or sync.| |false|
|operationResultTask|Set methods result to Task&lt;&gt;.| |false|
|modelClassModifier|Model Class Modifier can be nothing or partial| |partial|

View File

@ -20,6 +20,7 @@ package org.openapitools.codegen.languages;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.URLPathUtils;
import org.slf4j.Logger;
@ -39,13 +40,17 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
public static final String ASPNET_CORE_VERSION = "aspnetCoreVersion";
public static final String CLASS_MODIFIER = "classModifier";
public static final String OPERATION_MODIFIER = "operationModifier";
public static final String OPERATION_IS_ASYNC = "operationIsAsync";
public static final String OPERATION_RESULT_TASK = "operationResultTask";
public static final String GENERATE_BODY = "generateBody";
public static final String BUILD_TARGET = "buildTarget";
public static final String MODEL_CLASS_MODIFIER = "modelClassModifier";
public static final String PROJECT_SDK = "projectSdk";
public static final String SDK_WEB = "Microsoft.NET.Sdk.Web";
public static final String SDK_LIB = "Microsoft.NET.Sdk";
public static final String COMPATIBILITY_VERSION = "compatibilityVersion";
public static final String IS_LIBRARY = "isLibrary";
private String packageGuid = "{" + randomUUID().toString().toUpperCase(Locale.ROOT) + "}";
@ -55,13 +60,18 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
private boolean useSwashbuckle = true;
protected int serverPort = 8080;
protected String serverHost = "0.0.0.0";
private CliOption aspnetCoreVersion = new CliOption(ASPNET_CORE_VERSION, "ASP.NET Core version: 2.2 (default), 2.1, 2.0 (deprecated)");
protected CliOption aspnetCoreVersion = new CliOption(ASPNET_CORE_VERSION, "ASP.NET Core version: 2.2 (default), 2.1, 2.0 (deprecated)");
; // default to 2.1
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");
private boolean generateBody = true;
private CliOption buildTarget = new CliOption("buildTarget", "Target to build an application or library");
private String projectSdk = SDK_WEB;
private String compatibilityVersion = "Version_2_1";
private boolean operationIsAsync = false;
private boolean operationResultTask = false;
private boolean isLibrary = false;
public AspNetCoreServerCodegen() {
super();
@ -160,6 +170,10 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
"Uses the Swashbuckle.AspNetCore NuGet package for documentation.",
useSwashbuckle);
addSwitch(IS_LIBRARY,
"Is the build a library",
isLibrary);
classModifier.addEnum("", "Keep class default with no modifier");
classModifier.addEnum("abstract", "Make class abstract");
classModifier.setDefault("");
@ -182,6 +196,21 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
"Generates method body.",
generateBody);
addSwitch(OPERATION_IS_ASYNC,
"Set methods to async or sync.",
operationIsAsync);
addSwitch(OPERATION_RESULT_TASK,
"Set methods result to Task<>.",
operationResultTask);
modelClassModifier.setType("String");
modelClassModifier.addEnum("", "Keep model class default with no modifier");
modelClassModifier.addEnum("partial", "Make model class partial");
modelClassModifier.setDefault("partial");
modelClassModifier.setOptValue(modelClassModifier.getDefault());
addOption(modelClassModifier.getOpt(), modelClassModifier.getDescription(), modelClassModifier.getOptValue());
}
@Override
@ -210,34 +239,36 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
@Override
public void processOpts() {
super.processOpts();
boolean isLibrary = false;
if (additionalProperties.containsKey(CodegenConstants.OPTIONAL_PROJECT_GUID)) {
setPackageGuid((String) additionalProperties.get(CodegenConstants.OPTIONAL_PROJECT_GUID));
}
additionalProperties.put("packageGuid", packageGuid);
if (additionalProperties.containsKey(USE_SWASHBUCKLE)) {
useSwashbuckle = convertPropertyToBooleanAndWriteBack(USE_SWASHBUCKLE);
} else {
additionalProperties.put(USE_SWASHBUCKLE, useSwashbuckle);
}
// CHeck for the modifiers etc.
// The order of the checks is important.
isLibrary = setBuildTarget();
setBuildTarget();
setClassModifier();
setOperationModifier();
setModelClassModifier();
setUseSwashbuckle();
setOperationIsAsync();
// CHeck for class modifier if not present set the default value.
additionalProperties.put(PROJECT_SDK, projectSdk);
additionalProperties.put("dockerTag", packageName.toLowerCase(Locale.ROOT));
apiPackage = packageName + ".Controllers";
modelPackage = packageName + ".Models";
if (!additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) {
apiPackage = packageName + ".Controllers";
additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
}
if (!additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) {
modelPackage = packageName + ".Models";
additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
}
String packageFolder = sourceFolder + File.separator + packageName;
@ -259,8 +290,6 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
supportingFiles.add(new SupportingFile("Program.mustache", packageFolder, "Program.cs"));
supportingFiles.add(new SupportingFile("Properties" + File.separator + "launchSettings.json",
packageFolder + File.separator + "Properties", "launchSettings.json"));
} else {
supportingFiles.add(new SupportingFile("Project.nuspec.mustache", packageFolder, packageName + ".nuspec"));
// wwwroot files.
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "README.md", packageFolder + File.separator + "wwwroot", "README.md"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "index.html", packageFolder + File.separator + "wwwroot", "index.html"));
@ -268,6 +297,8 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "openapi-original.mustache",
packageFolder + File.separator + "wwwroot", "openapi-original.json"));
} else {
supportingFiles.add(new SupportingFile("Project.nuspec.mustache", packageFolder, packageName + ".nuspec"));
}
@ -342,15 +373,23 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
private void setCliOption(CliOption cliOption) throws IllegalArgumentException {
if (additionalProperties.containsKey(cliOption.getOpt())) {
cliOption.setOptValue(additionalProperties.get(cliOption.getOpt()).toString());
if (cliOption.getOptValue() == null) {
cliOption.setOptValue(cliOption.getDefault());
throw new IllegalArgumentException(cliOption.getOpt() + ": Invalid value '" + additionalProperties.get(cliOption.getOpt()).toString() + "'" +
". " + cliOption.getDescription());
// TODO Hack - not sure why the empty strings become boolean.
Object obj = additionalProperties.get(cliOption.getOpt());
if (!SchemaTypeUtil.BOOLEAN_TYPE.equals(cliOption.getType())) {
if (obj instanceof Boolean) {
obj = "";
additionalProperties.put(cliOption.getOpt(), obj);
}
}
cliOption.setOptValue(obj.toString());
} else {
additionalProperties.put(cliOption.getOpt(), cliOption.getOptValue());
}
if (cliOption.getOptValue() == null) {
cliOption.setOptValue(cliOption.getDefault());
throw new IllegalArgumentException(cliOption.getOpt() + ": Invalid value '" + additionalProperties.get(cliOption.getOpt()).toString() + "'" +
". " + cliOption.getDescription());
}
}
private void setClassModifier() {
@ -362,8 +401,6 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
operationModifier.setOptValue(classModifier.getOptValue());
additionalProperties.put(OPERATION_MODIFIER, operationModifier.getOptValue());
LOGGER.warn("classModifier is " + classModifier.getOptValue() + " so forcing operatonModifier to " + operationModifier.getOptValue());
} else {
setCliOption(operationModifier);
}
}
@ -382,15 +419,28 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
}
}
private boolean setBuildTarget() {
boolean isLibrary = false;
private void setModelClassModifier() {
setCliOption(modelClassModifier);
// If operation modifier is abstract then dont generate any body
if (isLibrary) {
modelClassModifier.setOptValue("");
additionalProperties.put(MODEL_CLASS_MODIFIER, modelClassModifier.getOptValue());
LOGGER.warn("buildTarget is " + buildTarget.getOptValue() + " so removing any modelClassModifier ");
}
}
private void setBuildTarget() {
setCliOption(buildTarget);
if ("library".equals(buildTarget.getOptValue())) {
isLibrary = true;
projectSdk = SDK_LIB;
additionalProperties.put(CLASS_MODIFIER, "abstract");
} else {
isLibrary = false;
projectSdk = SDK_WEB;
}
return isLibrary;
additionalProperties.put(IS_LIBRARY, isLibrary);
}
private void setAspnetCoreVersion(String packageFolder) {
@ -407,4 +457,29 @@ public class AspNetCoreServerCodegen extends AbstractCSharpCodegen {
}
additionalProperties.put(COMPATIBILITY_VERSION, compatibilityVersion);
}
private void setUseSwashbuckle() {
if (isLibrary) {
LOGGER.warn("buildTarget is " + buildTarget.getOptValue() + " so changing default isLibrary to false ");
useSwashbuckle = false;
} else {
useSwashbuckle = true;
}
if (additionalProperties.containsKey(USE_SWASHBUCKLE)) {
useSwashbuckle = convertPropertyToBooleanAndWriteBack(USE_SWASHBUCKLE);
} else {
additionalProperties.put(USE_SWASHBUCKLE, useSwashbuckle);
}
}
private void setOperationIsAsync() {
if (isLibrary) {
operationIsAsync = false;
additionalProperties.put(OPERATION_IS_ASYNC, operationIsAsync);
} else if (additionalProperties.containsKey(OPERATION_IS_ASYNC)) {
operationIsAsync = convertPropertyToBooleanAndWriteBack(OPERATION_IS_ASYNC);
} else {
additionalProperties.put(OPERATION_IS_ASYNC, operationIsAsync);
}
}
}

View File

@ -5,6 +5,9 @@
<TargetFramework>netcoreapp{{aspnetCoreVersion}}</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PreserveCompilationContext>true</PreserveCompilationContext>
{{#isLibrary}}
<OutputType>Library</OutputType>
{{/isLibrary}}
<AssemblyName>{{packageName}}</AssemblyName>
<PackageId>{{packageName}}</PackageId>
</PropertyGroup>

View File

@ -17,7 +17,7 @@ Windows:
```
build.bat
```
{{^isLibrary}}
## Run in Docker
```
@ -25,3 +25,4 @@ cd {{sourceFolder}}/{{packageName}}
docker build -t {{dockerTag}} .
docker run -p 5000:8080 {{dockerTag}}
```
{{/isLibrary}}

View File

@ -44,7 +44,7 @@ namespace {{packageName}}
services
.AddMvc()
{{#compatibilityVersion}}
.SetCompatibilityVersion(CompatibilityVersion.{{compatibilityVersion}})
.SetCompatibilityVersion (CompatibilityVersion.{{compatibilityVersion}})
{{/compatibilityVersion}}
.AddJsonOptions(opts =>
{
@ -109,7 +109,7 @@ namespace {{packageName}}
// c.SwaggerEndpoint("/openapi-original.json", "{{#appName}}{{{appName}}}{{/appName}}{{^appName}}{{packageName}}{{/appName}} Original");
}){{/useSwashbuckle}};
if (env.IsDevelopment())
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}

View File

@ -1,21 +1,24 @@
{{>partial_header}}
using System;
using System.Collections.Generic;
using System.Collections.Generic;{{#operationResultTask}}
using System.Threading.Tasks;
{{/operationResultTask}}
using Microsoft.AspNetCore.Mvc;{{#useSwashbuckle}}
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.SwaggerGen;{{/useSwashbuckle}}
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.SwaggerGen;{{/useSwashbuckle}}{{^isLibrary}}
using Newtonsoft.Json;{{/isLibrary}}
using System.ComponentModel.DataAnnotations;
using {{packageName}}.Attributes;
using {{packageName}}.Models;
using {{modelPackage}};
namespace {{packageName}}.Controllers
namespace {{apiPackage}}
{ {{#operations}}
/// <summary>
/// {{description}}
/// </summary>{{#description}}
[Description("{{description}}")]{{/description}}
public {{classModifier}} class {{classname}}Controller : ControllerBase
[ApiController]
public {{#classModifier}}{{classModifier}} {{/classModifier}}class {{classname}}Controller : ControllerBase
{ {{#operation}}
/// <summary>
/// {{#summary}}{{summary}}{{/summary}}
@ -27,8 +30,9 @@ namespace {{packageName}}.Controllers
[Route("{{{basePathWithoutHost}}}{{{path}}}")]
[ValidateModelState]{{#useSwashbuckle}}
[SwaggerOperation("{{operationId}}")]{{#responses}}{{#dataType}}
[SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}
public {{operationModifier}} IActionResult {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{^generateBody}};{{/generateBody}}
[SwaggerResponse(statusCode: {{code}}, type: typeof({{&dataType}}), description: "{{message}}")]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}{{^useSwashbuckle}}{{#responses}}{{#dataType}}
[ProducesResponseType(statusCode: {{code}}, type: typeof({{&dataType}}))]{{/dataType}}{{^dataType}}{{/dataType}}{{/responses}}{{/useSwashbuckle}}
public {{operationModifier}} {{#operationResultTask}}{{#operationIsAsync}}async {{/operationIsAsync}}Task<{{/operationResultTask}}IActionResult{{#operationResultTask}}>{{/operationResultTask}} {{operationId}}({{#allParams}}{{>pathParam}}{{>queryParam}}{{>bodyParam}}{{>formParam}}{{>headerParam}}{{#hasMore}}, {{/hasMore}}{{/allParams}}){{^generateBody}};{{/generateBody}}
{{#generateBody}}
{ {{#responses}}
{{#dataType}}
@ -47,7 +51,7 @@ namespace {{packageName}}.Controllers
{{#isListCollection}}{{>listReturn}}{{/isListCollection}}{{^isListCollection}}{{#isMapContainer}}{{>mapReturn}}{{/isMapContainer}}{{^isMapContainer}}{{>objectReturn}}{{/isMapContainer}}{{/isListCollection}}
{{!TODO: defaultResponse, examples, auth, consumes, produces, nickname, externalDocs, imports, security}}
//TODO: Change the data returned
return new ObjectResult(example);{{/returnType}}{{^returnType}}
return {{#operationResultTask}}Task.FromResult<IActionResult>({{/operationResultTask}}new ObjectResult(example){{#operationResultTask}}){{/operationResultTask}};{{/returnType}}{{^returnType}}
throw new NotImplementedException();{{/returnType}}
}
{{/generateBody}}

View File

@ -9,13 +9,13 @@ using Newtonsoft.Json;
{{#models}}
{{#model}}
namespace {{packageName}}.Models
namespace {{modelPackage}}
{ {{#isEnum}}{{>enumClass}}{{/isEnum}}{{^isEnum}}
/// <summary>
/// {{description}}
/// </summary>
[DataContract]
public partial class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}}IEquatable<{{classname}}>
public {{#modelClassModifier}}{{modelClassModifier}} {{/modelClassModifier}} class {{classname}} : {{#parent}}{{{parent}}}, {{/parent}}IEquatable<{{classname}}>
{ {{#vars}}{{#isEnum}}{{>enumClass}}{{/isEnum}}{{#items.isEnum}}{{#items}}{{>enumClass}}{{/items}}{{/items.isEnum}}
/// <summary>
/// {{^description}}Gets or Sets {{{name}}}{{/description}}{{#description}}{{description}}{{/description}}