Typescript+Axios: Separate model and api classfiles and package (#2005)

* Typescript 3.2

* Typescript spread operator

* Add vendor extension to the operation

* Remove url.URLSearchParams

* Generate form data in API

* Make axios scripts executable

* Reran generator

* Generate sample code

* Codegen having the model and api extra flag

* Revert to 2.4 Typescript

* COLLECTION_FORMAT.{{collectionFormat}} everywhere for consistency

* Consistency on the CollectionFormats, comment on the vendor extension

* Throw exception if api and model packages are not given

* Templates splitting api and models

* Post process the operations in the the process

* Beginning to get the axios tests up

* Ensure-up-to-date doesnt include TS/Axios tests

* Docs update for Typescript/Axios

* Merge master

* Api tests re-run

* Add windows bat file
This commit is contained in:
Michael van Niekerk
2019-03-05 10:44:33 +02:00
committed by William Cheng
parent cc1fe6eebf
commit caf404d857
47 changed files with 3768 additions and 1330 deletions

View File

@@ -20,6 +20,7 @@ package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
@@ -36,11 +37,14 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
public static final String NPM_REPOSITORY = "npmRepository";
public static final String SNAPSHOT = "snapshot";
public static final String WITH_INTERFACES = "withInterfaces";
public static final String SEPARATE_MODELS_AND_API = "withSeparateModelsAndApi";
protected String npmName = null;
protected String npmVersion = "1.0.0";
protected String npmRepository = null;
private String tsModelPackage = "";
public TypeScriptAxiosClientCodegen() {
super();
@@ -56,6 +60,7 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
this.cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json"));
this.cliOptions.add(new CliOption(SNAPSHOT, "When setting this property to true the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(WITH_INTERFACES, "Setting this property to true will generate interfaces next to the default class implementations.", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
this.cliOptions.add(new CliOption(SEPARATE_MODELS_AND_API, "Put the model and api in separate folders and in separate classes", SchemaTypeUtil.BOOLEAN_TYPE).defaultValue(Boolean.FALSE.toString()));
}
@Override
@@ -92,19 +97,57 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
this.npmRepository = npmRepository;
}
private static String getRelativeToRoot(String path) {
StringBuilder sb = new StringBuilder();
int slashCount = path.split("/").length;
if (slashCount == 0) {
sb.append("./");
} else {
for (int i = 0; i < slashCount; ++i) {
sb.append("../");
}
}
return sb.toString();
}
@Override
public void processOpts() {
super.processOpts();
tsModelPackage = modelPackage.replaceAll("\\.", "/");
String tsApiPackage = apiPackage.replaceAll("\\.", "/");
String modelRelativeToRoot = getRelativeToRoot(tsModelPackage);
String apiRelativeToRoot = getRelativeToRoot(tsApiPackage);
additionalProperties.put("tsModelPackage", tsModelPackage);
additionalProperties.put("tsApiPackage", tsApiPackage);
additionalProperties.put("apiRelativeToRoot", apiRelativeToRoot);
additionalProperties.put("modelRelativeToRoot", modelRelativeToRoot);
supportingFiles.add(new SupportingFile("index.mustache", "", "index.ts"));
supportingFiles.add(new SupportingFile("baseApi.mustache", "", "base.ts"));
supportingFiles.add(new SupportingFile("api.mustache", "", "api.ts"));
supportingFiles.add(new SupportingFile("configuration.mustache", "", "configuration.ts"));
supportingFiles.add(new SupportingFile("custom.d.mustache", "", "custom.d.ts"));
supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh"));
supportingFiles.add(new SupportingFile("gitignore", "", ".gitignore"));
boolean separateModelsAndApi = (boolean)additionalProperties.getOrDefault(SEPARATE_MODELS_AND_API, false);
if (separateModelsAndApi) {
modelTemplateFiles.put("model.mustache", ".ts");
apiTemplateFiles.put("apiInner.mustache", ".ts");
supportingFiles.add(new SupportingFile("modelIndex.mustache", tsModelPackage, "index.ts"));
}
if (additionalProperties.containsKey(NPM_NAME)) {
addNpmPackageGeneration();
}
boolean emptyModelOrApi = separateModelsAndApi && StringUtils.isAnyBlank(modelPackage, apiPackage);
if (emptyModelOrApi) {
throw new RuntimeException("apiPackage and modelPackage must be defined");
}
}
@Override
@@ -128,8 +171,8 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
@Override
public Map<String, Object> postProcessOperationsWithModels(Map<String, Object> objs, List<Object> allModels) {
objs = super.postProcessOperationsWithModels(objs, allModels);
Map<String, Object> vals = (Map<String, Object>) objs.get("operations");
List<CodegenOperation> operations = (List<CodegenOperation>) vals.get("operation");
Map<String, Object> vals = (Map<String, Object>) objs.getOrDefault("operations", new HashMap<>());
List<CodegenOperation> operations = (List<CodegenOperation>) vals.getOrDefault("operation", new ArrayList<>());
/*
Filter all the operations that are multipart/form-data operations and set the vendor extension flag
'multipartFormData' for the template to work with.
@@ -147,6 +190,37 @@ public class TypeScriptAxiosClientCodegen extends AbstractTypeScriptClientCodege
addImport(codegenModel, codegenModel.additionalPropertiesType);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
Map<String, Object> ret = super.postProcessModels(objs);
// Deduce the model file name in kebab case
List<Map<String, Object>> models = (List<Map<String, Object>>) ret.get("models");
for (Map<String, Object> m : models) {
CodegenModel model = (CodegenModel) m.get("model");
model.classFilename = model.classname.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT);
}
// Apply the model file name to the imports as well
for (Map<String, String> m : (List<Map<String, String>>) ret.get("imports")) {
String javaImport = m.get("import").substring(modelPackage.length() + 1);
String tsImport = tsModelPackage + "/" + javaImport;
m.put("tsImport", tsImport);
m.put("class", javaImport);
m.put("filename", javaImport.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT));
}
return ret;
}
@Override
public String toModelFilename(String name) {
return super.toModelFilename(name).replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT);
}
@Override
public String toApiFilename(String name) {
return super.toApiFilename(name).replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase(Locale.ROOT);
}
private void addNpmPackageGeneration() {
if (additionalProperties.containsKey(NPM_NAME)) {
this.setNpmName(additionalProperties.get(NPM_NAME).toString());