Support language N4JS (2nd) (#15397)

* n4js initial commit

* incorporate feedback from user

* add tests

* fix media type in case of DELETE method

* fix media type

* some minor fixes

* options fix for booleans

* small fixes

* generated files by ./bin/utils/ensure-up-to-date

* remove String::toLowerCase due to de.thetaphi:forbiddenapis

* adjust test expectation

* fix test expectations

* fix test expectation

* add note to section 'Languages/Generators'

* remove file according to review

* replace tabs by spaces

* replace tabs by spaces (2)

* update two generated files

* remove test file

* move statement
This commit is contained in:
mmews-n4 2023-05-03 14:50:21 +02:00 committed by GitHub
parent aea7cef6a1
commit db478c3452
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 2189 additions and 1 deletions

View File

@ -269,7 +269,7 @@ If you're a nix user, you can enter OpenAPI Generator shell, by typing:
```sh
nix develop
```
It will enter a shell with Java 8 and Maven installed.
It will enter a shell with Java 8 and Maven installed.
Direnv supports automatically loading of the nix developer shell, so if you're using direnv too, type:
```sh
@ -1154,6 +1154,7 @@ If you want to join the committee, please kindly apply by sending an email to te
| Julia | @tanmaykm (2023/01) |
| Kotlin | @jimschubert (2017/09) [:heart:](https://www.patreon.com/jimschubert), @dr4ke616 (2018/08) @karismann (2019/03) @Zomzog (2019/04) @andrewemery (2019/10) @4brunu (2019/11) @yutaka0m (2020/03) |
| Lua | @daurnimator (2017/08) |
| N4JS | @mmews-n4 (2023/03) |
| Nim | |
| NodeJS/Javascript | @CodeNinjai (2017/07) @frol (2017/07) @cliffano (2017/07) |
| ObjC | |

View File

@ -0,0 +1,9 @@
generatorName: n4js
outputDir: samples/client/petstore/n4js
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/n4js
additionalProperties:
apiPackage: "api"
modelPackage: "model"
fetchExecuterConstName: "FETCH_EXEC"
fetchExecuterConstImplPath: "FetchExecuterMock"

View File

@ -44,6 +44,7 @@ The following generators are available:
* [k6 (beta)](generators/k6.md)
* [kotlin](generators/kotlin.md)
* [lua (beta)](generators/lua.md)
* [n4js](generators/n4js.md)
* [nim (beta)](generators/nim.md)
* [objc](generators/objc.md)
* [ocaml](generators/ocaml.md)

244
docs/generators/n4js.md Normal file
View File

@ -0,0 +1,244 @@
---
title: Documentation for the n4js Generator
---
## METADATA
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | n4js | pass this to the generate command after -g |
| generator stability | STABLE | |
| generator type | CLIENT | |
| generator language | Java | |
| generator default templating engine | mustache | |
| helpTxt | Generates a n4js client. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|apiNamePrefix|Prefix that will be appended to all API names ('tags'). Default: empty string. e.g. Pet => Pet.| |null|
|apiPackage|package for generated api classes| |null|
|checkRequiredParamsNotNull|Iff true null-checks are performed for required parameters.| |null|
|checkSuperfluousBodyProps|Iff true a new copy of the given body object is transmitted. This copy only contains those properties defined in its model specification.| |null|
|generateDefaultApiExecuter|Iff true a default implementation of the api executer interface is generated.| |null|
|modelPackage|package for generated models| |null|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>Array</li>
<li>Error</li>
<li>Object</li>
<li>String</li>
<li>any</li>
<li>any+</li>
<li>boolean</li>
<li>int</li>
<li>number</li>
<li>object</li>
<li>string</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
<li>abstract</li>
<li>await</li>
<li>boolean</li>
<li>break</li>
<li>byte</li>
<li>case</li>
<li>catch</li>
<li>char</li>
<li>class</li>
<li>const</li>
<li>continue</li>
<li>debugger</li>
<li>default</li>
<li>delete</li>
<li>do</li>
<li>double</li>
<li>else</li>
<li>enum</li>
<li>export</li>
<li>extends</li>
<li>false</li>
<li>final</li>
<li>finally</li>
<li>float</li>
<li>for</li>
<li>formParams</li>
<li>function</li>
<li>goto</li>
<li>headerParams</li>
<li>if</li>
<li>implements</li>
<li>import</li>
<li>in</li>
<li>instanceof</li>
<li>int</li>
<li>interface</li>
<li>let</li>
<li>long</li>
<li>native</li>
<li>new</li>
<li>null</li>
<li>package</li>
<li>private</li>
<li>protected</li>
<li>public</li>
<li>queryParameters</li>
<li>requestOptions</li>
<li>return</li>
<li>short</li>
<li>static</li>
<li>super</li>
<li>switch</li>
<li>synchronized</li>
<li>this</li>
<li>throw</li>
<li>transient</li>
<li>true</li>
<li>try</li>
<li>typeof</li>
<li>useFormData</li>
<li>var</li>
<li>varLocalDeferred</li>
<li>varLocalPath</li>
<li>void</li>
<li>volatile</li>
<li>while</li>
<li>with</li>
<li>yield</li>
</ul>
## FEATURE SET
### Client Modification Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasePath|✗|ToolingExtension
|Authorizations|✗|ToolingExtension
|UserAgent|✗|ToolingExtension
|MockServer|✗|ToolingExtension
### Data Type Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Custom|✗|OAS2,OAS3
|Int32|✓|OAS2,OAS3
|Int64|✓|OAS2,OAS3
|Float|✓|OAS2,OAS3
|Double|✓|OAS2,OAS3
|Decimal|✓|ToolingExtension
|String|✓|OAS2,OAS3
|Byte|✓|OAS2,OAS3
|Binary|✓|OAS2,OAS3
|Boolean|✓|OAS2,OAS3
|Date|✓|OAS2,OAS3
|DateTime|✓|OAS2,OAS3
|Password|✓|OAS2,OAS3
|File|✓|OAS2
|Uuid|✗|
|Array|✓|OAS2,OAS3
|Null|✗|OAS3
|AnyType|✗|OAS2,OAS3
|Object|✓|OAS2,OAS3
|Maps|✓|ToolingExtension
|CollectionFormat|✓|OAS2
|CollectionFormatMulti|✓|OAS2
|Enum|✓|OAS2,OAS3
|ArrayOfEnum|✓|ToolingExtension
|ArrayOfModel|✓|ToolingExtension
|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
|ArrayOfCollectionOfModel|✓|ToolingExtension
|ArrayOfCollectionOfEnum|✓|ToolingExtension
|MapOfEnum|✓|ToolingExtension
|MapOfModel|✓|ToolingExtension
|MapOfCollectionOfPrimitives|✓|ToolingExtension
|MapOfCollectionOfModel|✓|ToolingExtension
|MapOfCollectionOfEnum|✓|ToolingExtension
### Documentation Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Readme|✗|ToolingExtension
|Model|✓|ToolingExtension
|Api|✓|ToolingExtension
### Global Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Host|✓|OAS2,OAS3
|BasePath|✓|OAS2,OAS3
|Info|✓|OAS2,OAS3
|Schemes|✗|OAS2,OAS3
|PartialSchemes|✓|OAS2,OAS3
|Consumes|✓|OAS2
|Produces|✓|OAS2
|ExternalDocumentation|✓|OAS2,OAS3
|Examples|✓|OAS2,OAS3
|XMLStructureDefinitions|✗|OAS2,OAS3
|MultiServer|✗|OAS3
|ParameterizedServer|✗|OAS3
|ParameterStyling|✗|OAS3
|Callbacks|✓|OAS3
|LinkObjects|✗|OAS3
### Parameter Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Path|✓|OAS2,OAS3
|Query|✓|OAS2,OAS3
|Header|✓|OAS2,OAS3
|Body|✓|OAS2
|FormUnencoded|✓|OAS2
|FormMultipart|✓|OAS2
|Cookie|✓|OAS3
### Schema Support Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Simple|✓|OAS2,OAS3
|Composite|✓|OAS2,OAS3
|Polymorphism|✓|OAS2,OAS3
|Union|✗|OAS3
|allOf|✗|OAS2,OAS3
|anyOf|✗|OAS3
|oneOf|✗|OAS3
|not|✗|OAS3
### Security Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasicAuth|✓|OAS2,OAS3
|ApiKey|✓|OAS2,OAS3
|OpenIDConnect|✗|OAS3
|BearerToken|✓|OAS3
|OAuth2_Implicit|✓|OAS2,OAS3
|OAuth2_Password|✓|OAS2,OAS3
|OAuth2_ClientCredentials|✓|OAS2,OAS3
|OAuth2_AuthorizationCode|✓|OAS2,OAS3
### Wire Format Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|JSON|✓|OAS2,OAS3
|XML|✓|OAS2,OAS3
|PROTOBUF|✗|ToolingExtension
|Custom|✗|OAS2,OAS3

View File

@ -0,0 +1,668 @@
package org.openapitools.codegen.languages;
import static org.openapitools.codegen.CodegenConstants.API_NAME_PREFIX;
import static org.openapitools.codegen.CodegenConstants.API_NAME_PREFIX_DESC;
import static org.openapitools.codegen.CodegenConstants.API_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.API_PACKAGE_DESC;
import static org.openapitools.codegen.CodegenConstants.MODEL_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.MODEL_PACKAGE_DESC;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenResponse;
import org.openapitools.codegen.CodegenSecurity;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.IJsonSchemaValidationProperties;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.model.ModelMap;
import org.openapitools.codegen.model.ModelsMap;
import org.openapitools.codegen.model.OperationMap;
import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
public class N4jsClientCodegen extends DefaultCodegen implements CodegenConfig {
public static final String CHECK_REQUIRED_PARAMS_NOT_NULL = "checkRequiredParamsNotNull";
public static final String CHECK_SUPERFLUOUS_BODY_PROPS = "checkSuperfluousBodyProps";
public static final String GENERATE_DEFAULT_API_EXECUTER = "generateDefaultApiExecuter";
final Logger LOGGER = LoggerFactory.getLogger(N4jsClientCodegen.class);
final Set<String> forbiddenChars = new HashSet<>();
private boolean checkRequiredBodyPropsNotNull = true;
private boolean checkSuperfluousBodyProps = true;
private boolean generateDefaultApiExecuter = true;
public CodegenType getTag() {
return CodegenType.CLIENT;
}
public String getName() {
return "n4js";
}
public String getHelp() {
return "Generates a n4js client.";
}
public N4jsClientCodegen() {
super();
specialCharReplacements.clear();
outputFolder = "generated-code" + File.separator + "n4js";
modelTemplateFiles.put("model.mustache", ".n4jsd");
apiTemplateFiles.put("api.mustache", ".n4js");
embeddedTemplateDir = templateDir = "n4js";
apiPackage = "";
modelPackage = "";
typeMapping = new HashMap<String, String>();
typeMapping.put("Set", "Set");
typeMapping.put("set", "Set");
typeMapping.put("Array", "Array");
typeMapping.put("array", "Array");
typeMapping.put("boolean", "boolean");
typeMapping.put("string", "string");
typeMapping.put("char", "string");
typeMapping.put("float", "number");
typeMapping.put("long", "int");
typeMapping.put("short", "int");
typeMapping.put("int", "int");
typeMapping.put("integer", "int");
typeMapping.put("number", "number");
typeMapping.put("double", "number");
typeMapping.put("object", "object");
typeMapping.put("Map", "any");
typeMapping.put("map", "any");
typeMapping.put("date", "string");
typeMapping.put("DateTime", "string");
typeMapping.put("binary", "any");
typeMapping.put("File", "any");
typeMapping.put("file", "any");
typeMapping.put("ByteArray", "string");
typeMapping.put("UUID", "string");
typeMapping.put("URI", "string");
typeMapping.put("Error", "Error");
typeMapping.put("AnyType", "any");
importMapping.clear(); // not used
supportsInheritance = true;
supportsMultipleInheritance = false;
reservedWords.addAll(Arrays.asList(
// local variable names used in API methods (endpoints)
"varLocalPath", "queryParameters", "headerParams", "formParams", "useFormData", "varLocalDeferred",
"requestOptions",
// N4JS reserved words
"abstract", "await", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue",
"debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final",
"finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int",
"interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public",
"return", "short", "static", "super", "switch", "synchronized", "this", "throw", "transient", "true",
"try", "typeof", "var", "void", "volatile", "while", "with", "yield"));
languageSpecificPrimitives = new HashSet<>(Arrays.asList("string", "String", "boolean", "number", "int",
"Object", "object", "Array", "any", "any+", "Error"));
defaultIncludes.add("~Object+");
defaultIncludes.add("Object+");
forbiddenChars.add("@");
cliOptions.clear();
cliOptions.add(new CliOption(API_PACKAGE, API_PACKAGE_DESC));
cliOptions.add(new CliOption(MODEL_PACKAGE, MODEL_PACKAGE_DESC));
cliOptions.add(new CliOption(API_NAME_PREFIX, API_NAME_PREFIX_DESC));
cliOptions.add(new CliOption(CHECK_REQUIRED_PARAMS_NOT_NULL,
"Iff true null-checks are performed for required parameters."));
cliOptions.add(new CliOption(CHECK_SUPERFLUOUS_BODY_PROPS,
"Iff true a new copy of the given body object is transmitted. This copy only contains those properties defined in its model specification."));
cliOptions.add(new CliOption(GENERATE_DEFAULT_API_EXECUTER,
"Iff true a default implementation of the api executer interface is generated."));
}
@Override
public void processOpts() {
super.processOpts();
// disable since otherwise Modules/Classes are not generated iff used as
// parameters only
GlobalSettings.setProperty("skipFormModel", "false");
supportingFiles.clear();
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("ApiHelper.mustache", apiPackage, "ApiHelper.n4js"));
checkRequiredBodyPropsNotNull = processBooleanOpt(CHECK_REQUIRED_PARAMS_NOT_NULL,
checkRequiredBodyPropsNotNull);
checkSuperfluousBodyProps = processBooleanOpt(CHECK_SUPERFLUOUS_BODY_PROPS, checkSuperfluousBodyProps);
generateDefaultApiExecuter = processBooleanOpt(GENERATE_DEFAULT_API_EXECUTER, generateDefaultApiExecuter);
if (additionalProperties.get(API_PACKAGE) instanceof String) {
apiPackage = additionalProperties.get(API_PACKAGE).toString();
} else {
additionalProperties.put(API_PACKAGE, apiPackage);
}
if (additionalProperties.get(MODEL_PACKAGE) instanceof String) {
modelPackage = additionalProperties.get(MODEL_PACKAGE).toString();
} else {
additionalProperties.put(MODEL_PACKAGE, modelPackage);
}
if (additionalProperties.get(API_NAME_PREFIX) instanceof String) {
apiNamePrefix = additionalProperties.get(API_NAME_PREFIX).toString();
} else {
additionalProperties.put(API_NAME_PREFIX, apiNamePrefix);
}
}
private boolean processBooleanOpt(String OPT, boolean defaultValue) {
boolean passedValue = defaultValue;
if (additionalProperties.containsKey(OPT)) {
Object value = additionalProperties.get(OPT);
if (value instanceof Boolean) {
passedValue = (Boolean) value;
} else {
try {
passedValue = Boolean.parseBoolean(value.toString());
} catch (Exception e) {
// ignore
}
}
}
additionalProperties.put(OPT, passedValue);
return defaultValue;
}
@Override
public String toModelFilename(String name) {
String modelFilename = super.toModelFilename(name);
if (typeMapping.containsKey(modelFilename) || defaultIncludes.contains(modelFilename)) {
return modelFilename;
}
return modelFilename;
}
public boolean checkRequiredBodyPropsNotNull() {
return checkRequiredBodyPropsNotNull;
}
public boolean checkSuperfluousBodyProps() {
return checkSuperfluousBodyProps;
}
public boolean generateDefaultApiExecuter() {
return generateDefaultApiExecuter;
}
@Override
public boolean getUseInlineModelResolver() {
return false;
}
@Override
public void setOpenAPI(OpenAPI openAPI) {
super.setOpenAPI(openAPI);
typeAliases.put("object", "~Object+");
}
@Override
protected boolean isReservedWord(String word) {
// case sensitive matching
return reservedWords.contains(word);
}
@Override
public String toAnyOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getAnyOf());
return String.join(" | ", types);
}
@Override
public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getOneOf());
return String.join(" | ", types);
}
@Override
public String toAllOfName(List<String> names, ComposedSchema composedSchema) {
List<String> types = getTypesFromSchemas(composedSchema.getAllOf());
return String.join(" & ", types);
}
/**
* Extracts the list of type names from a list of schemas. Excludes `AnyType` if
* there are other valid types extracted.
*
* @param schemas list of schemas
* @return list of types
*/
@SuppressWarnings("rawtypes")
protected List<String> getTypesFromSchemas(List<Schema> schemas) {
List<Schema> filteredSchemas = schemas.size() > 1 ? schemas.stream()
.filter(schema -> !"AnyType".equals(super.getSchemaType(schema))).collect(Collectors.toList())
: schemas;
return filteredSchemas.stream().map(schema -> getTypeDeclaration(schema)).distinct()
.collect(Collectors.toList());
}
@Override
protected void addImports(Set<String> importsToBeAddedTo, IJsonSchemaValidationProperties type) {
Set<String> imports = type.getImports(importContainerType, importBaseType, generatorMetadata.getFeatureSet());
Set<String> mappedImports = new HashSet<>();
for (String imp : imports) {
String mappedImp = imp;
if (typeMapping.containsKey(imp)) {
mappedImp = typeMapping.get(imp);
} else {
mappedImp = imp;
}
mappedImports.add(mappedImp);
}
addImports(importsToBeAddedTo, mappedImports);
}
@Override
protected void addImport(Set<String> importsToBeAddedTo, String type) {
String[] parts = splitComposedType(type);
for (String s : parts) {
super.addImport(importsToBeAddedTo, s);
}
}
private String[] splitComposedType(String name) {
return name.replace(" ", "").split("[|&<>]");
}
@Override
public ModelsMap postProcessModels(ModelsMap objs) {
objs = super.postProcessModels(objs);
for (ModelMap modelMap : objs.getModels()) {
CodegenModel cgModel = modelMap.getModel();
if (cgModel.unescapedDescription != null && !cgModel.unescapedDescription.contains("\n * ")) {
cgModel.description = escapeTextWhileAllowingNewLines(cgModel.unescapedDescription.trim()).replace("\n",
"\n * ");
}
}
postProcessModelsEnum(objs); // enable enums
return objs;
}
@Override
protected void addImportsForPropertyType(CodegenModel model, CodegenProperty property) {
if (model.getIsAnyType()) {
return; // disable (unused) imports created for properties of type aliases
}
super.addImportsForPropertyType(model, property);
}
@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
objs = super.postProcessAllModels(objs);
for (String modelName : objs.keySet()) {
ModelsMap modelsMap = objs.get(modelName);
// imports
List<Map<String, String>> imports = modelsMap.getImports();
ArrayList<Map<String, String>> n4jsImports = new ArrayList<Map<String, String>>();
modelsMap.put("n4jsimports", n4jsImports);
String className = modelsMap.get("classname").toString();
for (Map<String, String> imp : imports) {
Map<String, String> n4jsImport = toN4jsImports(className, objs, imp);
if (n4jsImport != null) {
n4jsImports.add(n4jsImport);
}
}
// app description -> module documentation
adjustDescriptionWithNewLines(modelsMap);
}
return objs;
}
@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap operations, List<ModelMap> allModels) {
OperationMap objs = operations.getOperations();
boolean needImportCleanCopyBody = false;
// The api.mustache template requires all of the auth methods for the whole api
// Loop over all the operations and pick out each unique auth method
Map<String, CodegenSecurity> authMethodsMap = new HashMap<>();
for (CodegenOperation op : objs.getOperation()) {
if (op.hasAuthMethods) {
for (CodegenSecurity sec : op.authMethods) {
authMethodsMap.put(sec.name, sec);
}
}
if (op.bodyParam != null && !op.bodyParam.vars.isEmpty()) {
needImportCleanCopyBody = true;
}
if (op.responses != null && op.responses.size() > 0) {
Map<String, CodegenResponse> responses2xx = new LinkedHashMap<>();
Map<String, CodegenResponse> responses4xx = new LinkedHashMap<>();
for (CodegenResponse response : op.responses) {
if (response.is2xx) {
responses2xx.put(response.baseType, response);
}
if (response.is4xx) {
responses4xx.put(response.baseType, response);
}
}
op.vendorExtensions.put("responses2xx", new ArrayList<>(responses2xx.values()));
op.vendorExtensions.put("responses4xx", new ArrayList<>(responses4xx.values()));
}
}
operations.put("needImportCleanCopyBody", needImportCleanCopyBody);
// If there were any auth methods specified add them to the operations context
if (!authMethodsMap.isEmpty()) {
operations.put("authMethods", authMethodsMap.values());
operations.put("hasAuthMethods", true);
}
// Add additional filename information for model imports in the apis
Iterator<Map<String, String>> iter = operations.getImports().iterator();
while (iter.hasNext()) {
Map<String, String> im = iter.next();
String className = im.get("classname");
className = convertToModelName(className);
String adjClassName = typeMapping.getOrDefault(className, className);
if (needToImport(adjClassName)) {
im.put("classname", className);
im.put("filename", toModelImport(className));
} else {
iter.remove();
}
}
// app description -> module documentation
adjustDescriptionWithNewLines(additionalProperties);
return operations;
}
private String convertToModelName(String modelName) {
if (modelName == null) {
return modelName;
}
Schema<?> schema = ModelUtils.getSchema(openAPI, modelName);
if (schema == null) {
return modelName;
}
if (ModelUtils.isObjectSchema(schema)) {
return toModelFilename(modelName);
}
return modelName;
}
private void adjustDescriptionWithNewLines(Map<String, Object> map) {
if (map.containsKey("appDescriptionWithNewLines")
&& !map.get("appDescriptionWithNewLines").toString().contains("\n * ")) {
String appDescriptionWithNewLines = map.get("appDescriptionWithNewLines").toString();
appDescriptionWithNewLines = appDescriptionWithNewLines.trim().replace("\n", "\n * ");
map.put("appDescriptionWithNewLines", appDescriptionWithNewLines);
}
}
private Map<String, String> toN4jsImports(String className, Map<String, ModelsMap> objs, Map<String, String> imp) {
String modelImpName = imp.get("import");
if (modelImpName == null) {
return null;
}
String modelName = fromModelImport(modelImpName);
if (!objs.containsKey(modelName)) {
return null;
}
ModelsMap modelsMap = objs.get(modelName);
String impClassName = modelsMap.get("classname").toString();
if (impClassName == null || Objects.equals(impClassName, className)) {
return null;
}
Map<String, String> n4jsImport = new HashMap<>();
n4jsImport.put("elementname", impClassName);
n4jsImport.put("modulename", modelImpName);
return n4jsImport;
}
@Override
public String toModelImport(String name) {
if ("".equals(modelPackage())) {
return name;
} else {
return modelPackage() + "/" + name;
}
}
protected String fromModelImport(String modelImportName) {
if ("".equals(modelPackage())) {
return modelImportName;
} else if (modelImportName == null) {
return modelImportName;
} else {
if (modelImportName.startsWith(modelPackage() + "/")) {
String nameWithoutModelPackage = modelImportName.substring(1 + modelPackage().length());
if (modelNamePrefix != null && nameWithoutModelPackage.startsWith(modelNamePrefix)) {
return nameWithoutModelPackage.substring(modelNamePrefix.length());
}
return nameWithoutModelPackage;
}
return modelImportName;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
Schema<?> items = getSchemaItems((ArraySchema) p);
return getTypeDeclaration(unaliasSchema(items)) + "[]";
} else if (ModelUtils.isMapSchema(p)) {
return "~Object+";
} else if (ModelUtils.isStringSchema(p)) {
if (p.getEnum() != null) {
return enumValuesToEnumTypeUnion(p.getEnum(), "string");
}
} else if (ModelUtils.isIntegerSchema(p) || ModelUtils.isNumberSchema(p)) {
// Handle integer and double enums
if (p.getEnum() != null) {
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(p.getEnum()));
}
} else if (ModelUtils.isFileSchema(p)) {
return "File";
} else if (ModelUtils.isObjectSchema(p)
|| ModelUtils.isObjectSchema(ModelUtils.getReferencedSchema(openAPI, p))) {
String result = super.getTypeDeclaration(p);
return toModelFilename(result);
} else if (ModelUtils.isBinarySchema(p)) {
return "ArrayBuffer";
}
return super.getTypeDeclaration(p);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
protected String getParameterDataType(Parameter parameter, Schema p) {
// handle enums of various data types
if (ModelUtils.isArraySchema(p)) {
ArraySchema mp1 = (ArraySchema) p;
Schema<?> inner = mp1.getItems();
return getParameterDataType(parameter, inner) + "[]";
} else if (ModelUtils.isMapSchema(p)) {
return "~Object+";
} else if (ModelUtils.isStringSchema(p)) {
// Handle string enums
if (p.getEnum() != null) {
return enumValuesToEnumTypeUnion(p.getEnum(), "string");
}
} else if (ModelUtils.isObjectSchema(p)
|| ModelUtils.isObjectSchema(ModelUtils.getReferencedSchema(openAPI, p))) {
String result = super.getTypeDeclaration(p);
return toModelFilename(result);
} else if (ModelUtils.isIntegerSchema(p) || ModelUtils.isNumberSchema(p)) {
// Handle integer and double enums
if (p.getEnum() != null) {
return numericEnumValuesToEnumTypeUnion(new ArrayList<Number>(p.getEnum()));
}
}
return this.getTypeDeclaration(p);
}
@Override
protected String getSingleSchemaType(@SuppressWarnings("rawtypes") Schema schema) {
Schema<?> unaliasSchema = unaliasSchema(schema);
if (StringUtils.isNotBlank(unaliasSchema.get$ref())) {
String schemaName = ModelUtils.getSimpleRef(unaliasSchema.get$ref());
if (StringUtils.isNotEmpty(schemaName)) {
if (schemaMapping.containsKey(schemaName)) {
return schemaName;
}
}
}
return super.getSingleSchemaType(unaliasSchema);
}
/**
* Converts a list of strings to a literal union for representing enum values as
* a type. Example output: 'available' | 'pending' | 'sold'
*
* @param values list of allowed enum values
* @param dataType either "string" or "number"
* @return a literal union for representing enum values as a type
*/
private String enumValuesToEnumTypeUnion(List<String> values, String dataType) {
StringBuilder b = new StringBuilder();
boolean isFirst = true;
for (String value : values) {
if (!isFirst) {
b.append(" | ");
}
b.append(toEnumValue(value, dataType));
isFirst = false;
}
return b.toString();
}
/**
* Converts a list of numbers to a literal union for representing enum values as
* a type. Example output: 3 | 9 | 55
*
* @param values a list of numbers
* @return a literal union for representing enum values as a type
*/
private String numericEnumValuesToEnumTypeUnion(List<Number> values) {
List<String> stringValues = new ArrayList<>();
for (Number value : values) {
stringValues.add(value.toString());
}
return enumValuesToEnumTypeUnion(stringValues, "number");
}
@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
if (property.unescapedDescription != null && property.unescapedDescription.contains("\n")) {
property.description = escapeTextWhileAllowingNewLines(property.unescapedDescription.trim()).replace("\n",
"\n * ");
}
}
@Override
public String escapeText(String input) {
input = escapeTextWhileAllowingNewLines(input);
if (input == null) {
return input;
}
// remove \n, \r
return input.replaceAll("[\\n\\r]", " ");
}
@Override
public String escapeTextWhileAllowingNewLines(String input) {
if (input == null) {
return input;
}
// remove \t
// outer unescape to retain the original multi-byte characters
// finally escalate characters avoiding code injection
return escapeUnsafeCharacters(
StringEscapeUtils.unescapeEcmaScript(StringEscapeUtils.escapeEcmaScript(input).replace("\\/", "/"))
.replaceAll("[\\t]", " "));
}
@Override
public String escapeReservedWord(String name) {
return "_" + name;
}
@Override
public String toVarName(final String name) {
String name2 = super.toVarName(name);
for (String forbiddenChar : forbiddenChars) {
if (name2.contains(forbiddenChar)) {
return "[\"" + name2 + "\"]";
}
}
return name2;
}
@Override
public String toParamName(String name) {
String name2 = super.toParamName(name);
for (String forbiddenChar : forbiddenChars) {
if (name2.contains(forbiddenChar)) {
return "[\"" + name2 + "\"]";
}
}
return name2;
}
@Override
public String escapeQuotationMark(String input) {
// remove ', " to avoid code injection
return input.replace("\"", "").replace("'", "");
}
@Override
public String escapeUnsafeCharacters(String input) {
return input.replace("*/", "*_/").replace("/*", "/_*");
}
}

View File

@ -81,6 +81,7 @@ org.openapitools.codegen.languages.K6ClientCodegen
org.openapitools.codegen.languages.LuaClientCodegen
org.openapitools.codegen.languages.MarkdownDocumentationCodegen
org.openapitools.codegen.languages.MysqlSchemaCodegen
org.openapitools.codegen.languages.N4jsClientCodegen
org.openapitools.codegen.languages.NimClientCodegen
org.openapitools.codegen.languages.NodeJSExpressServerCodegen
org.openapitools.codegen.languages.ObjcClientCodegen

View File

@ -0,0 +1,119 @@
/**
* Implemented by client
*/
export public interface ~ApiExecuterI {
public <R, E> async exec(
method: string,
path: string,
pathParams: ~Object+,
queryParams: ~Object+,
headerParams: ~Object+,
payloadContentType: string,
body: any+) : Promise<R, Object|ApiError<E>>;
}
export public interface ~ApiError<T> {
public resultBody?: T;
}
{{#checkRequiredParamsNotNull}}
export public function checkRequiredParams(apiName: string, params: ~Object+) : void {
for (const key of Object.keys(params)) {
const arg = params[key];
if (arg == null) {
throw new Error('Required parameter ' + key + ' was null or undefined when calling ' + apiName + '.');
}
}
}
{{/checkRequiredParamsNotNull}}
{{#checkSuperfluousBodyProps}}
export public function <T> cleanCopyBody(t : T+, ...properties: string) : ~T {
const copy : ~T+ = {};
for (const prop in properties) {
copy[prop] = t.prop;
}
return copy;
}
{{/checkSuperfluousBodyProps}}
{{#generateDefaultApiExecuter}}
/**
* Default implementation of ApiExecuterI
*
* The following dependencies are necessary:
* - n4js-runtime-esnext
* - n4js-runtime-es2015
* - n4js-runtime-html5
*/
export public class FetchApiExec implements ApiExecuterI {
public apiOrigin: string;
const jsonTypes = ["application/json", "application/problem+json"];
@Override
public <R, E> async exec(
method: string,
path: string,
pathParams: ~Object+,
queryParams: ~Object+,
headerParams: ~Object+,
payloadContentType: string,
body: any+
): Promise<R, Object|ApiError<E>> {
if (pathParams) {
for (const [k, v] of Object.entries(pathParams)) {
path = path.replace(`{${k}}`, encodeURIComponent(String(v)));
}
}
const query: string[] = [];
if (queryParams) {
for (const [k, v] of Object.entries(queryParams)) {
query.push(`${k}=${encodeURIComponent(String(v))}`);
}
}
let url = `${this.apiOrigin}${path}`;
if (query.length) {
url += `?${query.join("&")}`;
}
const headers: Object+ = {};
if (payloadContentType) {
headers["content-type"] = payloadContentType;
if (this.constructor.jsonTypes.includes(payloadContentType)) {
body = JSON.stringify(body);
}
}
Object.assign(headers, headerParams);
return await this.<R,E>fetchExec(url, {
method,
headers,
body,
});
}
protected <R, E> async fetchExec(url: string, reqInit: RequestInit): Promise<R, Object|ApiError<E>> {
const resp = await fetch(url, reqInit);
if (resp.status !== 204) {
const contentType = (resp.headers.get("content-type") || "").split(";")[0];
const body = this.constructor.jsonTypes.includes(contentType)
? await resp.json()
: await resp.text();
if (!resp.ok) {
await this.handleError(resp, body);
}
return body as R;
}
return null;
}
protected <E> async handleError(resp: Response, body): Promise<undefined, ApiError<E>> {
throw {body: body};
}
}
{{/generateDefaultApiExecuter}}

View File

@ -0,0 +1,55 @@
# Documentation for {{appName}}
- API version: {{appVersion}}
{{^hideGenerationTimestamp}}
- Build date: {{generatedDate}}
{{/hideGenerationTimestamp}}
{{{appDescriptionWithNewLines}}}
{{#infoUrl}}
For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
{{/infoUrl}}
*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)*
## Getting started
Configure the following elements:
- In open-api-n4js.yaml please add under 'additionalProperties':
- property 'fetchExecuterConstName'
- property 'fetchExecuterConstImplPath'
- The generated output directory needs to be augmented with an implementing n4js file
## Example
**open-api-n4js.yaml**
```yaml
generatorName: n4js
outputDir: /working_dir/gen-n4js/
inputSpec: /working_dir/api-spec/main.yaml
templateDir: /openapi-generator/modules/openapi-generator/src/main/resources/n4js
additionalProperties:
fetchExecuterConstName: "FETCH_EXEC"
fetchExecuterConstImplPath: "FetchExecuterImpl"
```
**FetchExecuterImpl.n4js**
```typescript
import {FetchExecuterI} from "api/ApiHelper";
export public const FETCH_EXEC = new FetchExecuterMock();
export public class FetchExecuterMock implements FetchExecuterI {
@Override
public async run(
path: string,
query: ~Object=,
reqInit: ~Object= {}): ~Object with {get status() : number, json(): Promise<any>} {
return null;
}
}
```

View File

@ -0,0 +1,53 @@
{{>licenseInfo}}
import { ApiExecuterI, ApiError{{#checkRequiredParamsNotNull}}, checkRequiredParams{{/checkRequiredParamsNotNull}}{{#checkSuperfluousBodyProps}}{{#needImportCleanCopyBody}}, cleanCopyBody{{/needImportCleanCopyBody}}{{/checkSuperfluousBodyProps}} } from "{{{apiPackage}}}/ApiHelper"
{{#imports}}
import { {{classname}} } from '{{filename}}';
{{/imports}}
{{#operations}}
{{#description}}
/*
* {{&description}}
*/
{{/description}}
{{#operation}}
/**
{{#notes}}
* {{&notes}}
{{/notes}}
{{#summary}}
* @summary {{&summary}}
{{/summary}}
* @param fe Callback interface that runs the fetch query
{{#allParams}}
* @param {{paramName}} {{description}}
{{/allParams}}
{{#responses}}
* @response {{code}} [{{#dataType}}{{.}}{{/dataType}}{{^dataType}}undefined{{/dataType}}] {{message}}
{{/responses}}
*/
export public async function {{{classname}}}__{{{nickname}}}(fe : ApiExecuterI, {{#allParams}}{{{paramName}}}: {{{dataType}}}{{^required}}={{/required}}{{^-last}}, {{/-last}}{{/allParams}}) : Promise<{{#returnType}}{{.}}{{/returnType}}{{^returnType}}undefined{{/returnType}}, Object{{#vendorExtensions}}{{#responses4xx.0}} | ApiError<{{#responses4xx}}{{{baseType}}}{{^-last}} | {{/-last}}{{/responses4xx}}>{{/responses4xx.0}}{{/vendorExtensions}}> {
{{#checkRequiredParamsNotNull}} checkRequiredParams('{{{nickname}}}', { {{#allParams}}{{#required}}'{{{paramName}}}': {{{paramName}}}{{^-last}}, {{/-last}}{{/required}}{{/allParams}} });
{{/checkRequiredParamsNotNull}}
const _pathParams = { {{#pathParams}}
'{{baseName}}': {{{paramName}}}{{^-last}},{{/-last}} {{/pathParams}}};
const _queryParams = { {{#queryParams}}
'{{baseName}}': {{{paramName}}}{{^-last}},{{/-last}} {{/queryParams}}};
const _headerParams = { {{#headerParams}}
'{{baseName}}': {{{paramName}}}{{^-last}},{{/-last}} {{/headerParams}}};
const _body = {{^bodyParam}}undefined{{/bodyParam}}{{#bodyParam}}{{#vars.empty}}{{{paramName}}}{{/vars.empty}}{{^vars.empty}}{{#checkSuperfluousBodyProps}}cleanCopyBody({{{paramName}}}{{#vars}}, '{{{baseName}}}'{{/vars}}){{/checkSuperfluousBodyProps}}{{^checkSuperfluousBodyProps}}{{{paramName}}}{{/checkSuperfluousBodyProps}}{{/vars.empty}}{{/bodyParam}};
{{#returnType}}return {{/returnType}}await fe.{{#returnType}}<{{.}}, {{#vendorExtensions}}{{^responses4xx.0}}undefined{{/responses4xx.0}}{{#responses4xx.0}}{{#responses4xx}}{{{baseType}}}{{^-last}} | {{/-last}}{{/responses4xx}}{{/responses4xx.0}}{{/vendorExtensions}}>{{/returnType}}exec(
'{{httpMethod}}', '{{{basePathWithoutHost}}}' + '{{{path}}}',
_pathParams, _queryParams, _headerParams,
{{#responses2xx.0}}'{{{mediaType}}}'{{/responses2xx.0}}{{^responses2xx.0}}undefined{{/responses2xx.0}},
_body
);
}
{{/operation}}
{{/operations}}

View File

@ -0,0 +1,11 @@
/*
* {{{appName}}}
* {{{appDescriptionWithNewLines}}}
*
* {{#version}}The version of the OpenAPI document: {{{.}}}{{/version}}
* {{#infoEmail}}Contact: {{{.}}}{{/infoEmail}}
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

View File

@ -0,0 +1,45 @@
{{>licenseInfo}}
{{#models}}
{{#model}}
{{#n4jsimports}}
import { {{{elementname}}} } from '{{modulename}}';
{{/n4jsimports}}
{{#description}}
/**
* {{{.}}}
*/
{{/description}}
{{#isModel}}
export external public interface ~{{{classname}}} {{#parent}}extends {{{.}}} {{/parent}}{
{{#vars}}
{{#description}}
/**
* {{{.}}}
*/
{{/description}}
public {{{name}}}{{^required}}?{{/required}}: {{{dataType}}};
{{/vars}}
}
{{/isModel}}
{{#isAnyType}}
export external public type {{{classname}}} = {{{dataType}}};
{{/isAnyType}}
{{#isEnum}}
{{#isString}}
@StringBased
{{/isString}}
{{#isNumber}}
@NumberBased
{{/isNumber}}
export external public enum {{{classname}}} {
{{#allowableValues}}
{{#enumVars}}
{{{name}}}: {{{value}}}{{^-last}},{{/-last}}
{{/enumVars}}
{{/allowableValues}}
}
{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -0,0 +1,46 @@
package org.openapitools.codegen.n4js;
import static java.lang.Boolean.parseBoolean;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.openapitools.codegen.CodegenConstants.API_NAME_PREFIX;
import static org.openapitools.codegen.CodegenConstants.API_PACKAGE;
import static org.openapitools.codegen.CodegenConstants.MODEL_PACKAGE;
import static org.openapitools.codegen.languages.N4jsClientCodegen.CHECK_REQUIRED_PARAMS_NOT_NULL;
import static org.openapitools.codegen.languages.N4jsClientCodegen.CHECK_SUPERFLUOUS_BODY_PROPS;
import static org.openapitools.codegen.languages.N4jsClientCodegen.GENERATE_DEFAULT_API_EXECUTER;
import static org.openapitools.codegen.options.N4jsClientCodegenOptionsProvider.CHECK_REQUIRED_PARAMS_NOT_NULL__VALUE;
import static org.openapitools.codegen.options.N4jsClientCodegenOptionsProvider.CHECK_SUPERFLUOUS_BODY_PROPS__VALUE;
import static org.openapitools.codegen.options.N4jsClientCodegenOptionsProvider.GENERATE_DEFAULT_API_EXECUTER__VALUE;
import org.openapitools.codegen.AbstractOptionsTest;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.languages.N4jsClientCodegen;
import org.openapitools.codegen.options.N4jsClientCodegenOptionsProvider;
public class N4jsClientCodegenOptionsTest extends AbstractOptionsTest {
private N4jsClientCodegen codegen = mock(N4jsClientCodegen.class, mockSettings);
public N4jsClientCodegenOptionsTest() {
super(new N4jsClientCodegenOptionsProvider());
}
@Override
protected CodegenConfig getCodegenConfig() {
return codegen;
}
@Override
protected void verifyOptions() {
assertEquals(parseBoolean(CHECK_REQUIRED_PARAMS_NOT_NULL__VALUE),
codegen.additionalProperties().get(CHECK_REQUIRED_PARAMS_NOT_NULL));
assertEquals(parseBoolean(CHECK_SUPERFLUOUS_BODY_PROPS__VALUE),
codegen.additionalProperties().get(CHECK_SUPERFLUOUS_BODY_PROPS));
assertEquals(parseBoolean(GENERATE_DEFAULT_API_EXECUTER__VALUE),
codegen.additionalProperties().get(GENERATE_DEFAULT_API_EXECUTER));
assertEquals("", codegen.additionalProperties().get(API_PACKAGE));
assertEquals("", codegen.additionalProperties().get(MODEL_PACKAGE));
assertEquals("", codegen.additionalProperties().get(API_NAME_PREFIX));
}
}

View File

@ -0,0 +1,39 @@
package org.openapitools.codegen.options;
import java.util.Map;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.languages.N4jsClientCodegen;
import com.google.common.collect.ImmutableMap;
public class N4jsClientCodegenOptionsProvider implements OptionsProvider {
public static final String PROJECT_NAME_VALUE = "OpenAPI";
public static final String CHECK_REQUIRED_PARAMS_NOT_NULL__VALUE = "true";
public static final String CHECK_SUPERFLUOUS_BODY_PROPS__VALUE = "true";
public static final String GENERATE_DEFAULT_API_EXECUTER__VALUE = "true";
@Override
public String getLanguage() {
return "n4js";
}
@Override
public Map<String, String> createOptions() {
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<String, String>();
return builder
.put(N4jsClientCodegen.CHECK_REQUIRED_PARAMS_NOT_NULL, CHECK_REQUIRED_PARAMS_NOT_NULL__VALUE)
.put(N4jsClientCodegen.CHECK_SUPERFLUOUS_BODY_PROPS, CHECK_SUPERFLUOUS_BODY_PROPS__VALUE)
.put(N4jsClientCodegen.GENERATE_DEFAULT_API_EXECUTER, GENERATE_DEFAULT_API_EXECUTER__VALUE)
.put(CodegenConstants.API_PACKAGE, "")
.put(CodegenConstants.MODEL_PACKAGE, "")
.put(CodegenConstants.API_NAME_PREFIX, "")
.build();
}
@Override
public boolean isServer() {
return false;
}
}

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,11 @@
README.md
api/ApiHelper.n4js
api/PetApi.n4js
api/StoreApi.n4js
api/UserApi.n4js
model/ApiResponse.n4jsd
model/Category.n4jsd
model/Order.n4jsd
model/Pet.n4jsd
model/Tag.n4jsd
model/User.n4jsd

View File

@ -0,0 +1 @@
6.6.0-SNAPSHOT

View File

@ -0,0 +1,48 @@
# Documentation for OpenAPI Petstore
- API version: 1.0.0
This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*Automatically generated by the [OpenAPI Generator](https://openapi-generator.tech)*
## Getting started
Configure the following elements:
- In open-api-n4js.yaml please add under 'additionalProperties':
- property 'fetchExecuterConstName'
- property 'fetchExecuterConstImplPath'
- The generated output directory needs to be augmented with an implementing n4js file
## Example
**open-api-n4js.yaml**
```yaml
generatorName: n4js
outputDir: /working_dir/gen-n4js/
inputSpec: /working_dir/api-spec/main.yaml
templateDir: /openapi-generator/modules/openapi-generator/src/main/resources/n4js
additionalProperties:
fetchExecuterConstName: "FETCH_EXEC"
fetchExecuterConstImplPath: "FetchExecuterImpl"
```
**FetchExecuterImpl.n4js**
```typescript
import {FetchExecuterI} from "api/ApiHelper";
export public const FETCH_EXEC = new FetchExecuterMock();
export public class FetchExecuterMock implements FetchExecuterI {
@Override
public async run(
path: string,
query: ~Object=,
reqInit: ~Object= {}): ~Object with {get status() : number, json(): Promise<any>} {
return null;
}
}
```

View File

@ -0,0 +1,113 @@
/**
* Implemented by client
*/
export public interface ~ApiExecuterI {
public <R, E> async exec(
method: string,
path: string,
pathParams: ~Object+,
queryParams: ~Object+,
headerParams: ~Object+,
payloadContentType: string,
body: any+) : Promise<R, Object|ApiError<E>>;
}
export public interface ~ApiError<T> {
public resultBody?: T;
}
export public function checkRequiredParams(apiName: string, params: ~Object+) : void {
for (const key of Object.keys(params)) {
const arg = params[key];
if (arg == null) {
throw new Error('Required parameter ' + key + ' was null or undefined when calling ' + apiName + '.');
}
}
}
export public function <T> cleanCopyBody(t : T+, ...properties: string) : ~T {
const copy : ~T+ = {};
for (const prop in properties) {
copy[prop] = t.prop;
}
return copy;
}
/**
* Default implementation of ApiExecuterI
*
* The following dependencies are necessary:
* - n4js-runtime-esnext
* - n4js-runtime-es2015
* - n4js-runtime-html5
*/
export public class FetchApiExec implements ApiExecuterI {
public apiOrigin: string;
const jsonTypes = ["application/json", "application/problem+json"];
@Override
public <R, E> async exec(
method: string,
path: string,
pathParams: ~Object+,
queryParams: ~Object+,
headerParams: ~Object+,
payloadContentType: string,
body: any+
): Promise<R, Object|ApiError<E>> {
if (pathParams) {
for (const [k, v] of Object.entries(pathParams)) {
path = path.replace(`{${k}}`, encodeURIComponent(String(v)));
}
}
const query: string[] = [];
if (queryParams) {
for (const [k, v] of Object.entries(queryParams)) {
query.push(`${k}=${encodeURIComponent(String(v))}`);
}
}
let url = `${this.apiOrigin}${path}`;
if (query.length) {
url += `?${query.join("&")}`;
}
const headers: Object+ = {};
if (payloadContentType) {
headers["content-type"] = payloadContentType;
if (this.constructor.jsonTypes.includes(payloadContentType)) {
body = JSON.stringify(body);
}
}
Object.assign(headers, headerParams);
return await this.<R,E>fetchExec(url, {
method,
headers,
body,
});
}
protected <R, E> async fetchExec(url: string, reqInit: RequestInit): Promise<R, Object|ApiError<E>> {
const resp = await fetch(url, reqInit);
if (resp.status !== 204) {
const contentType = (resp.headers.get("content-type") || "").split(";")[0];
const body = this.constructor.jsonTypes.includes(contentType)
? await resp.json()
: await resp.text();
if (!resp.ok) {
await this.handleError(resp, body);
}
return body as R;
}
return null;
}
protected <E> async handleError(resp: Response, body): Promise<undefined, ApiError<E>> {
throw {body: body};
}
}

View File

@ -0,0 +1,223 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { ApiExecuterI, ApiError, checkRequiredParams, cleanCopyBody } from "api/ApiHelper"
import { ApiResponse } from 'model/ApiResponse';
import { Pet } from 'model/Pet';
/**
*
* @summary Add a new pet to the store
* @param fe Callback interface that runs the fetch query
* @param pet Pet object that needs to be added to the store
* @response 200 [Pet] successful operation
* @response 405 [undefined] Invalid input
*/
export public async function PetApi__addPet(fe : ApiExecuterI, pet: Pet) : Promise<Pet, Object | ApiError<>> {
checkRequiredParams('addPet', { 'pet': pet });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = cleanCopyBody(pet, 'id', 'category', 'name', 'photoUrls', 'tags', 'status');
return await fe.<Pet, >exec(
'POST', '/v2' + '/pet',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Deletes a pet
* @param fe Callback interface that runs the fetch query
* @param petId Pet id to delete
* @param apiKey
* @response 400 [undefined] Invalid pet value
*/
export public async function PetApi__deletePet(fe : ApiExecuterI, petId: int, apiKey: string=) : Promise<undefined, Object | ApiError<>> {
checkRequiredParams('deletePet', { 'petId': petId, });
const _pathParams = {
'petId': petId };
const _queryParams = { };
const _headerParams = {
'api_key': apiKey };
const _body = undefined;
await fe.exec(
'DELETE', '/v2' + '/pet/{petId}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* Multiple status values can be provided with comma separated strings
* @summary Finds Pets by status
* @param fe Callback interface that runs the fetch query
* @param status Status values that need to be considered for filter
* @response 200 [Pet[]] successful operation
* @response 400 [undefined] Invalid status value
*/
export public async function PetApi__findPetsByStatus(fe : ApiExecuterI, status: "available" | "pending" | "sold"[]) : Promise<Pet[], Object | ApiError<>> {
checkRequiredParams('findPetsByStatus', { 'status': status });
const _pathParams = { };
const _queryParams = {
'status': status };
const _headerParams = { };
const _body = undefined;
return await fe.<Pet[], >exec(
'GET', '/v2' + '/pet/findByStatus',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
* @summary Finds Pets by tags
* @param fe Callback interface that runs the fetch query
* @param tags Tags to filter by
* @response 200 [Pet[]] successful operation
* @response 400 [undefined] Invalid tag value
*/
export public async function PetApi__findPetsByTags(fe : ApiExecuterI, tags: string[]) : Promise<Pet[], Object | ApiError<>> {
checkRequiredParams('findPetsByTags', { 'tags': tags });
const _pathParams = { };
const _queryParams = {
'tags': tags };
const _headerParams = { };
const _body = undefined;
return await fe.<Pet[], >exec(
'GET', '/v2' + '/pet/findByTags',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* Returns a single pet
* @summary Find pet by ID
* @param fe Callback interface that runs the fetch query
* @param petId ID of pet to return
* @response 200 [Pet] successful operation
* @response 400 [undefined] Invalid ID supplied
* @response 404 [undefined] Pet not found
*/
export public async function PetApi__getPetById(fe : ApiExecuterI, petId: int) : Promise<Pet, Object | ApiError<>> {
checkRequiredParams('getPetById', { 'petId': petId });
const _pathParams = {
'petId': petId };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
return await fe.<Pet, >exec(
'GET', '/v2' + '/pet/{petId}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Update an existing pet
* @param fe Callback interface that runs the fetch query
* @param pet Pet object that needs to be added to the store
* @response 200 [Pet] successful operation
* @response 400 [undefined] Invalid ID supplied
* @response 404 [undefined] Pet not found
* @response 405 [undefined] Validation exception
*/
export public async function PetApi__updatePet(fe : ApiExecuterI, pet: Pet) : Promise<Pet, Object | ApiError<>> {
checkRequiredParams('updatePet', { 'pet': pet });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = cleanCopyBody(pet, 'id', 'category', 'name', 'photoUrls', 'tags', 'status');
return await fe.<Pet, >exec(
'PUT', '/v2' + '/pet',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Updates a pet in the store with form data
* @param fe Callback interface that runs the fetch query
* @param petId ID of pet that needs to be updated
* @param name Updated name of the pet
* @param status Updated status of the pet
* @response 405 [undefined] Invalid input
*/
export public async function PetApi__updatePetWithForm(fe : ApiExecuterI, petId: int, name: string=, status: string=) : Promise<undefined, Object | ApiError<>> {
checkRequiredParams('updatePetWithForm', { 'petId': petId, });
const _pathParams = {
'petId': petId };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
await fe.exec(
'POST', '/v2' + '/pet/{petId}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary uploads an image
* @param fe Callback interface that runs the fetch query
* @param petId ID of pet to update
* @param additionalMetadata Additional data to pass to server
* @param file file to upload
* @response 200 [ApiResponse] successful operation
*/
export public async function PetApi__uploadFile(fe : ApiExecuterI, petId: int, additionalMetadata: string=, file: any=) : Promise<ApiResponse, Object> {
checkRequiredParams('uploadFile', { 'petId': petId, });
const _pathParams = {
'petId': petId };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
return await fe.<ApiResponse, undefined>exec(
'POST', '/v2' + '/pet/{petId}/uploadImage',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}

View File

@ -0,0 +1,115 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { ApiExecuterI, ApiError, checkRequiredParams, cleanCopyBody } from "api/ApiHelper"
import { Order } from 'model/Order';
/**
* For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
* @summary Delete purchase order by ID
* @param fe Callback interface that runs the fetch query
* @param orderId ID of the order that needs to be deleted
* @response 400 [undefined] Invalid ID supplied
* @response 404 [undefined] Order not found
*/
export public async function StoreApi__deleteOrder(fe : ApiExecuterI, orderId: string) : Promise<undefined, Object | ApiError<>> {
checkRequiredParams('deleteOrder', { 'orderId': orderId });
const _pathParams = {
'orderId': orderId };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
await fe.exec(
'DELETE', '/v2' + '/store/order/{orderId}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* Returns a map of status codes to quantities
* @summary Returns pet inventories by status
* @param fe Callback interface that runs the fetch query
* @response 200 [~Object+] successful operation
*/
export public async function StoreApi__getInventory(fe : ApiExecuterI, ) : Promise<~Object+, Object> {
checkRequiredParams('getInventory', { });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
return await fe.<~Object+, undefined>exec(
'GET', '/v2' + '/store/inventory',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions
* @summary Find purchase order by ID
* @param fe Callback interface that runs the fetch query
* @param orderId ID of pet that needs to be fetched
* @response 200 [Order] successful operation
* @response 400 [undefined] Invalid ID supplied
* @response 404 [undefined] Order not found
*/
export public async function StoreApi__getOrderById(fe : ApiExecuterI, orderId: int) : Promise<Order, Object | ApiError<>> {
checkRequiredParams('getOrderById', { 'orderId': orderId });
const _pathParams = {
'orderId': orderId };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
return await fe.<Order, >exec(
'GET', '/v2' + '/store/order/{orderId}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Place an order for a pet
* @param fe Callback interface that runs the fetch query
* @param order order placed for purchasing the pet
* @response 200 [Order] successful operation
* @response 400 [undefined] Invalid Order
*/
export public async function StoreApi__placeOrder(fe : ApiExecuterI, order: Order) : Promise<Order, Object | ApiError<>> {
checkRequiredParams('placeOrder', { 'order': order });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = cleanCopyBody(order, 'id', 'petId', 'quantity', 'shipDate', 'status', 'complete');
return await fe.<Order, >exec(
'POST', '/v2' + '/store/order',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}

View File

@ -0,0 +1,213 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { ApiExecuterI, ApiError, checkRequiredParams, cleanCopyBody } from "api/ApiHelper"
import { User } from 'model/User';
/**
* This can only be done by the logged in user.
* @summary Create user
* @param fe Callback interface that runs the fetch query
* @param user Created user object
* @response 0 [undefined] successful operation
*/
export public async function UserApi__createUser(fe : ApiExecuterI, user: User) : Promise<undefined, Object> {
checkRequiredParams('createUser', { 'user': user });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = cleanCopyBody(user, 'id', 'username', 'firstName', 'lastName', 'email', 'password', 'phone', 'userStatus');
await fe.exec(
'POST', '/v2' + '/user',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Creates list of users with given input array
* @param fe Callback interface that runs the fetch query
* @param user List of user object
* @response 0 [undefined] successful operation
*/
export public async function UserApi__createUsersWithArrayInput(fe : ApiExecuterI, user: User[]) : Promise<undefined, Object> {
checkRequiredParams('createUsersWithArrayInput', { 'user': user });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = user;
await fe.exec(
'POST', '/v2' + '/user/createWithArray',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Creates list of users with given input array
* @param fe Callback interface that runs the fetch query
* @param user List of user object
* @response 0 [undefined] successful operation
*/
export public async function UserApi__createUsersWithListInput(fe : ApiExecuterI, user: User[]) : Promise<undefined, Object> {
checkRequiredParams('createUsersWithListInput', { 'user': user });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = user;
await fe.exec(
'POST', '/v2' + '/user/createWithList',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* This can only be done by the logged in user.
* @summary Delete user
* @param fe Callback interface that runs the fetch query
* @param username The name that needs to be deleted
* @response 400 [undefined] Invalid username supplied
* @response 404 [undefined] User not found
*/
export public async function UserApi__deleteUser(fe : ApiExecuterI, username: string) : Promise<undefined, Object | ApiError<>> {
checkRequiredParams('deleteUser', { 'username': username });
const _pathParams = {
'username': username };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
await fe.exec(
'DELETE', '/v2' + '/user/{username}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Get user by user name
* @param fe Callback interface that runs the fetch query
* @param username The name that needs to be fetched. Use user1 for testing.
* @response 200 [User] successful operation
* @response 400 [undefined] Invalid username supplied
* @response 404 [undefined] User not found
*/
export public async function UserApi__getUserByName(fe : ApiExecuterI, username: string) : Promise<User, Object | ApiError<>> {
checkRequiredParams('getUserByName', { 'username': username });
const _pathParams = {
'username': username };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
return await fe.<User, >exec(
'GET', '/v2' + '/user/{username}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Logs user into the system
* @param fe Callback interface that runs the fetch query
* @param username The user name for login
* @param password The password for login in clear text
* @response 200 [string] successful operation
* @response 400 [undefined] Invalid username/password supplied
*/
export public async function UserApi__loginUser(fe : ApiExecuterI, username: string, password: string) : Promise<string, Object | ApiError<>> {
checkRequiredParams('loginUser', { 'username': username, 'password': password });
const _pathParams = { };
const _queryParams = {
'username': username,
'password': password };
const _headerParams = { };
const _body = undefined;
return await fe.<string, >exec(
'GET', '/v2' + '/user/login',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
*
* @summary Logs out current logged in user session
* @param fe Callback interface that runs the fetch query
* @response 0 [undefined] successful operation
*/
export public async function UserApi__logoutUser(fe : ApiExecuterI, ) : Promise<undefined, Object> {
checkRequiredParams('logoutUser', { });
const _pathParams = { };
const _queryParams = { };
const _headerParams = { };
const _body = undefined;
await fe.exec(
'GET', '/v2' + '/user/logout',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}
/**
* This can only be done by the logged in user.
* @summary Updated user
* @param fe Callback interface that runs the fetch query
* @param username name that need to be deleted
* @param user Updated user object
* @response 400 [undefined] Invalid user supplied
* @response 404 [undefined] User not found
*/
export public async function UserApi__updateUser(fe : ApiExecuterI, username: string, user: User) : Promise<undefined, Object | ApiError<>> {
checkRequiredParams('updateUser', { 'username': username, 'user': user });
const _pathParams = {
'username': username };
const _queryParams = { };
const _headerParams = { };
const _body = cleanCopyBody(user, 'id', 'username', 'firstName', 'lastName', 'email', 'password', 'phone', 'userStatus');
await fe.exec(
'PUT', '/v2' + '/user/{username}',
_pathParams, _queryParams, _headerParams,
undefined,
_body
);
}

View File

@ -0,0 +1,21 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* Describes the result of uploading an image resource
*/
export external public interface ~ApiResponse {
public code?: int;
public type?: string;
public message?: string;
}

View File

@ -0,0 +1,20 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* A category for a pet
*/
export external public interface ~Category {
public id?: int;
public name?: string;
}

View File

@ -0,0 +1,28 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* An order for a pets from the pet store
*/
export external public interface ~Order {
public id?: int;
public petId?: int;
public quantity?: int;
public shipDate?: string;
/**
* Order Status
*/
public status?: "placed" | "approved" | "delivered";
public complete?: boolean;
}

View File

@ -0,0 +1,30 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { Category } from 'model/Category';
import { Tag } from 'model/Tag';
/**
* A pet for sale in the pet store
*/
export external public interface ~Pet {
public id?: int;
public category?: Category;
public name: string;
public photoUrls: string[];
public tags?: Tag[];
/**
* pet status in the store
*/
public status?: "available" | "pending" | "sold";
}

View File

@ -0,0 +1,20 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* A tag for a pet
*/
export external public interface ~Tag {
public id?: int;
public name?: string;
}

View File

@ -0,0 +1,30 @@
/*
* OpenAPI Petstore
* This is a sample server Petstore server. For this sample, you can use the api key `special-key` to test the authorization filters.
*
* The version of the OpenAPI document: 1.0.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
* A User who is purchasing from the pet store
*/
export external public interface ~User {
public id?: int;
public username?: string;
public firstName?: string;
public lastName?: string;
public email?: string;
public password?: string;
public phone?: string;
/**
* User Status
*/
public userStatus?: int;
}