Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/generators/aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ sidebar_label: aspnetcore
|useCollection|Deserialize array types to Collection<T> instead of List<T>.| |false|
|returnICollection|Return ICollection<T> 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<>.| |false|
|modelClassModifier|Model Class Modifier can be nothing or partial| |partial|
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand All @@ -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) + "}";

Expand All @@ -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();
Expand Down Expand Up @@ -160,6 +170,10 @@ public AspNetCoreServerCodegen() {
"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("");
Expand All @@ -182,6 +196,21 @@ public AspNetCoreServerCodegen() {
"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
Expand Down Expand Up @@ -210,34 +239,36 @@ public void preprocessOpenAPI(OpenAPI openAPI) {
@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;

Expand All @@ -259,15 +290,15 @@ public void processOpts() {
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"));
supportingFiles.add(new SupportingFile("wwwroot" + File.separator + "web.config", packageFolder + File.separator + "wwwroot", "web.config"));

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"));
}


Expand Down Expand Up @@ -342,15 +373,23 @@ public String getNullableType(Schema p, String type) {

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() {
Expand All @@ -362,8 +401,6 @@ private void setClassModifier() {
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);
}
}

Expand All @@ -382,15 +419,28 @@ private void setOperationModifier() {
}
}

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) {
Expand All @@ -407,4 +457,29 @@ private void setAspnetCoreVersion(String packageFolder) {
}
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ Windows:
```
build.bat
```

{{^isLibrary}}
## Run in Docker

```
cd {{sourceFolder}}/{{packageName}}
docker build -t {{dockerTag}} .
docker run -p 5000:8080 {{dockerTag}}
```
{{/isLibrary}}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ namespace {{packageName}}
services
.AddMvc()
{{#compatibilityVersion}}
.SetCompatibilityVersion(CompatibilityVersion.{{compatibilityVersion}})
.SetCompatibilityVersion (CompatibilityVersion.{{compatibilityVersion}})
{{/compatibilityVersion}}
.AddJsonOptions(opts =>
{
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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}}
Expand All @@ -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}}
Expand All @@ -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}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down