From db478c34522a0f4899777f42b72327b6ffc53b3b Mon Sep 17 00:00:00 2001 From: mmews-n4 Date: Wed, 3 May 2023 14:50:21 +0200 Subject: [PATCH] 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 --- README.md | 3 +- bin/configs/n4js-petstore.yaml | 9 + docs/generators.md | 1 + docs/generators/n4js.md | 244 +++++++ .../codegen/languages/N4jsClientCodegen.java | 668 ++++++++++++++++++ .../org.openapitools.codegen.CodegenConfig | 1 + .../main/resources/n4js/ApiHelper.mustache | 119 ++++ .../src/main/resources/n4js/README.mustache | 55 ++ .../src/main/resources/n4js/api.mustache | 53 ++ .../main/resources/n4js/licenseInfo.mustache | 11 + .../src/main/resources/n4js/model.mustache | 45 ++ .../n4js/N4jsClientCodegenOptionsTest.java | 46 ++ .../N4jsClientCodegenOptionsProvider.java | 39 + .../petstore/n4js/.openapi-generator-ignore | 23 + .../petstore/n4js/.openapi-generator/FILES | 11 + .../petstore/n4js/.openapi-generator/VERSION | 1 + samples/client/petstore/n4js/README.md | 48 ++ .../client/petstore/n4js/api/ApiHelper.n4js | 113 +++ samples/client/petstore/n4js/api/PetApi.n4js | 223 ++++++ .../client/petstore/n4js/api/StoreApi.n4js | 115 +++ samples/client/petstore/n4js/api/UserApi.n4js | 213 ++++++ .../petstore/n4js/model/ApiResponse.n4jsd | 21 + .../client/petstore/n4js/model/Category.n4jsd | 20 + .../client/petstore/n4js/model/Order.n4jsd | 28 + samples/client/petstore/n4js/model/Pet.n4jsd | 30 + samples/client/petstore/n4js/model/Tag.n4jsd | 20 + samples/client/petstore/n4js/model/User.n4jsd | 30 + 27 files changed, 2189 insertions(+), 1 deletion(-) create mode 100644 bin/configs/n4js-petstore.yaml create mode 100644 docs/generators/n4js.md create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java create mode 100644 modules/openapi-generator/src/main/resources/n4js/ApiHelper.mustache create mode 100644 modules/openapi-generator/src/main/resources/n4js/README.mustache create mode 100644 modules/openapi-generator/src/main/resources/n4js/api.mustache create mode 100644 modules/openapi-generator/src/main/resources/n4js/licenseInfo.mustache create mode 100644 modules/openapi-generator/src/main/resources/n4js/model.mustache create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/n4js/N4jsClientCodegenOptionsTest.java create mode 100644 modules/openapi-generator/src/test/java/org/openapitools/codegen/options/N4jsClientCodegenOptionsProvider.java create mode 100644 samples/client/petstore/n4js/.openapi-generator-ignore create mode 100644 samples/client/petstore/n4js/.openapi-generator/FILES create mode 100644 samples/client/petstore/n4js/.openapi-generator/VERSION create mode 100644 samples/client/petstore/n4js/README.md create mode 100644 samples/client/petstore/n4js/api/ApiHelper.n4js create mode 100644 samples/client/petstore/n4js/api/PetApi.n4js create mode 100644 samples/client/petstore/n4js/api/StoreApi.n4js create mode 100644 samples/client/petstore/n4js/api/UserApi.n4js create mode 100644 samples/client/petstore/n4js/model/ApiResponse.n4jsd create mode 100644 samples/client/petstore/n4js/model/Category.n4jsd create mode 100644 samples/client/petstore/n4js/model/Order.n4jsd create mode 100644 samples/client/petstore/n4js/model/Pet.n4jsd create mode 100644 samples/client/petstore/n4js/model/Tag.n4jsd create mode 100644 samples/client/petstore/n4js/model/User.n4jsd diff --git a/README.md b/README.md index b4a67aac55b..41bce3dc892 100644 --- a/README.md +++ b/README.md @@ -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 | | diff --git a/bin/configs/n4js-petstore.yaml b/bin/configs/n4js-petstore.yaml new file mode 100644 index 00000000000..06ab7c93e1b --- /dev/null +++ b/bin/configs/n4js-petstore.yaml @@ -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" diff --git a/docs/generators.md b/docs/generators.md index c9b2665c4a0..276a9cb0475 100644 --- a/docs/generators.md +++ b/docs/generators.md @@ -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) diff --git a/docs/generators/n4js.md b/docs/generators/n4js.md new file mode 100644 index 00000000000..5eb04776280 --- /dev/null +++ b/docs/generators/n4js.md @@ -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 + + + +## RESERVED WORDS + + + +## 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 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java new file mode 100644 index 00000000000..154fcb5bee8 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/N4jsClientCodegen.java @@ -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 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(); + 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 names, ComposedSchema composedSchema) { + List types = getTypesFromSchemas(composedSchema.getAnyOf()); + return String.join(" | ", types); + } + + @Override + public String toOneOfName(List names, ComposedSchema composedSchema) { + List types = getTypesFromSchemas(composedSchema.getOneOf()); + return String.join(" | ", types); + } + + @Override + public String toAllOfName(List names, ComposedSchema composedSchema) { + List 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 getTypesFromSchemas(List schemas) { + List 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 importsToBeAddedTo, IJsonSchemaValidationProperties type) { + Set imports = type.getImports(importContainerType, importBaseType, generatorMetadata.getFeatureSet()); + Set 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 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 postProcessAllModels(Map objs) { + objs = super.postProcessAllModels(objs); + for (String modelName : objs.keySet()) { + ModelsMap modelsMap = objs.get(modelName); + + // imports + List> imports = modelsMap.getImports(); + ArrayList> n4jsImports = new ArrayList>(); + modelsMap.put("n4jsimports", n4jsImports); + String className = modelsMap.get("classname").toString(); + for (Map imp : imports) { + Map 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 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 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 responses2xx = new LinkedHashMap<>(); + Map 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> iter = operations.getImports().iterator(); + while (iter.hasNext()) { + Map 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 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 toN4jsImports(String className, Map objs, Map 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 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(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(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 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 values) { + List 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("/*", "/_*"); + } +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 15bc7e37b7a..4d5a896bcb4 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -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 diff --git a/modules/openapi-generator/src/main/resources/n4js/ApiHelper.mustache b/modules/openapi-generator/src/main/resources/n4js/ApiHelper.mustache new file mode 100644 index 00000000000..698d543fffc --- /dev/null +++ b/modules/openapi-generator/src/main/resources/n4js/ApiHelper.mustache @@ -0,0 +1,119 @@ + +/** + * Implemented by client + */ +export public interface ~ApiExecuterI { + public async exec( + method: string, + path: string, + pathParams: ~Object+, + queryParams: ~Object+, + headerParams: ~Object+, + payloadContentType: string, + body: any+) : Promise>; +} + +export public interface ~ApiError { + 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 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 async exec( + method: string, + path: string, + pathParams: ~Object+, + queryParams: ~Object+, + headerParams: ~Object+, + payloadContentType: string, + body: any+ + ): Promise> { + + 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.fetchExec(url, { + method, + headers, + body, + }); + } + + protected async fetchExec(url: string, reqInit: RequestInit): Promise> { + 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 async handleError(resp: Response, body): Promise> { + throw {body: body}; + } +} +{{/generateDefaultApiExecuter}} diff --git a/modules/openapi-generator/src/main/resources/n4js/README.mustache b/modules/openapi-generator/src/main/resources/n4js/README.mustache new file mode 100644 index 00000000000..9a177e0e4fb --- /dev/null +++ b/modules/openapi-generator/src/main/resources/n4js/README.mustache @@ -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} { + + return null; + } + } +``` diff --git a/modules/openapi-generator/src/main/resources/n4js/api.mustache b/modules/openapi-generator/src/main/resources/n4js/api.mustache new file mode 100644 index 00000000000..d48b57d3af5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/n4js/api.mustache @@ -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}} + * {{¬es}} + {{/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}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/n4js/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/n4js/licenseInfo.mustache new file mode 100644 index 00000000000..2864dc5adf5 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/n4js/licenseInfo.mustache @@ -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. + */ diff --git a/modules/openapi-generator/src/main/resources/n4js/model.mustache b/modules/openapi-generator/src/main/resources/n4js/model.mustache new file mode 100644 index 00000000000..3bf06e10841 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/n4js/model.mustache @@ -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}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/n4js/N4jsClientCodegenOptionsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/n4js/N4jsClientCodegenOptionsTest.java new file mode 100644 index 00000000000..b2e02054140 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/n4js/N4jsClientCodegenOptionsTest.java @@ -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)); + } +} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/N4jsClientCodegenOptionsProvider.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/N4jsClientCodegenOptionsProvider.java new file mode 100644 index 00000000000..917c951f0b4 --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/options/N4jsClientCodegenOptionsProvider.java @@ -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 createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + 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; + } +} + diff --git a/samples/client/petstore/n4js/.openapi-generator-ignore b/samples/client/petstore/n4js/.openapi-generator-ignore new file mode 100644 index 00000000000..7484ee590a3 --- /dev/null +++ b/samples/client/petstore/n4js/.openapi-generator-ignore @@ -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 diff --git a/samples/client/petstore/n4js/.openapi-generator/FILES b/samples/client/petstore/n4js/.openapi-generator/FILES new file mode 100644 index 00000000000..f216c446733 --- /dev/null +++ b/samples/client/petstore/n4js/.openapi-generator/FILES @@ -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 diff --git a/samples/client/petstore/n4js/.openapi-generator/VERSION b/samples/client/petstore/n4js/.openapi-generator/VERSION new file mode 100644 index 00000000000..ba8a874deab --- /dev/null +++ b/samples/client/petstore/n4js/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.6.0-SNAPSHOT \ No newline at end of file diff --git a/samples/client/petstore/n4js/README.md b/samples/client/petstore/n4js/README.md new file mode 100644 index 00000000000..f7d1f439a6f --- /dev/null +++ b/samples/client/petstore/n4js/README.md @@ -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} { + + return null; + } + } +``` diff --git a/samples/client/petstore/n4js/api/ApiHelper.n4js b/samples/client/petstore/n4js/api/ApiHelper.n4js new file mode 100644 index 00000000000..340ed3ba251 --- /dev/null +++ b/samples/client/petstore/n4js/api/ApiHelper.n4js @@ -0,0 +1,113 @@ + +/** + * Implemented by client + */ +export public interface ~ApiExecuterI { + public async exec( + method: string, + path: string, + pathParams: ~Object+, + queryParams: ~Object+, + headerParams: ~Object+, + payloadContentType: string, + body: any+) : Promise>; +} + +export public interface ~ApiError { + 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 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 async exec( + method: string, + path: string, + pathParams: ~Object+, + queryParams: ~Object+, + headerParams: ~Object+, + payloadContentType: string, + body: any+ + ): Promise> { + + 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.fetchExec(url, { + method, + headers, + body, + }); + } + + protected async fetchExec(url: string, reqInit: RequestInit): Promise> { + 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 async handleError(resp: Response, body): Promise> { + throw {body: body}; + } +} diff --git a/samples/client/petstore/n4js/api/PetApi.n4js b/samples/client/petstore/n4js/api/PetApi.n4js new file mode 100644 index 00000000000..9ffcf762ab3 --- /dev/null +++ b/samples/client/petstore/n4js/api/PetApi.n4js @@ -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> { + checkRequiredParams('addPet', { 'pet': pet }); + + const _pathParams = { }; + const _queryParams = { }; + const _headerParams = { }; + const _body = cleanCopyBody(pet, 'id', 'category', 'name', 'photoUrls', 'tags', 'status'); + + return await fe.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> { + 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> { + checkRequiredParams('findPetsByStatus', { 'status': status }); + + const _pathParams = { }; + const _queryParams = { + 'status': status }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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> { + checkRequiredParams('findPetsByTags', { 'tags': tags }); + + const _pathParams = { }; + const _queryParams = { + 'tags': tags }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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> { + checkRequiredParams('getPetById', { 'petId': petId }); + + const _pathParams = { + 'petId': petId }; + const _queryParams = { }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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> { + checkRequiredParams('updatePet', { 'pet': pet }); + + const _pathParams = { }; + const _queryParams = { }; + const _headerParams = { }; + const _body = cleanCopyBody(pet, 'id', 'category', 'name', 'photoUrls', 'tags', 'status'); + + return await fe.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> { + 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 { + checkRequiredParams('uploadFile', { 'petId': petId, }); + + const _pathParams = { + 'petId': petId }; + const _queryParams = { }; + const _headerParams = { }; + const _body = undefined; + + return await fe.exec( + 'POST', '/v2' + '/pet/{petId}/uploadImage', + _pathParams, _queryParams, _headerParams, + undefined, + _body + ); +} diff --git a/samples/client/petstore/n4js/api/StoreApi.n4js b/samples/client/petstore/n4js/api/StoreApi.n4js new file mode 100644 index 00000000000..1bb6d9f98e8 --- /dev/null +++ b/samples/client/petstore/n4js/api/StoreApi.n4js @@ -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> { + 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> { + checkRequiredParams('getOrderById', { 'orderId': orderId }); + + const _pathParams = { + 'orderId': orderId }; + const _queryParams = { }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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> { + checkRequiredParams('placeOrder', { 'order': order }); + + const _pathParams = { }; + const _queryParams = { }; + const _headerParams = { }; + const _body = cleanCopyBody(order, 'id', 'petId', 'quantity', 'shipDate', 'status', 'complete'); + + return await fe.exec( + 'POST', '/v2' + '/store/order', + _pathParams, _queryParams, _headerParams, + undefined, + _body + ); +} diff --git a/samples/client/petstore/n4js/api/UserApi.n4js b/samples/client/petstore/n4js/api/UserApi.n4js new file mode 100644 index 00000000000..077fa6e30ab --- /dev/null +++ b/samples/client/petstore/n4js/api/UserApi.n4js @@ -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 { + 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 { + 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 { + 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> { + 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> { + checkRequiredParams('getUserByName', { 'username': username }); + + const _pathParams = { + 'username': username }; + const _queryParams = { }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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> { + checkRequiredParams('loginUser', { 'username': username, 'password': password }); + + const _pathParams = { }; + const _queryParams = { + 'username': username, + 'password': password }; + const _headerParams = { }; + const _body = undefined; + + return await fe.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 { + 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> { + 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 + ); +} diff --git a/samples/client/petstore/n4js/model/ApiResponse.n4jsd b/samples/client/petstore/n4js/model/ApiResponse.n4jsd new file mode 100644 index 00000000000..e1469ff41d5 --- /dev/null +++ b/samples/client/petstore/n4js/model/ApiResponse.n4jsd @@ -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; +} diff --git a/samples/client/petstore/n4js/model/Category.n4jsd b/samples/client/petstore/n4js/model/Category.n4jsd new file mode 100644 index 00000000000..5744a608344 --- /dev/null +++ b/samples/client/petstore/n4js/model/Category.n4jsd @@ -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; +} diff --git a/samples/client/petstore/n4js/model/Order.n4jsd b/samples/client/petstore/n4js/model/Order.n4jsd new file mode 100644 index 00000000000..6c595697890 --- /dev/null +++ b/samples/client/petstore/n4js/model/Order.n4jsd @@ -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; +} diff --git a/samples/client/petstore/n4js/model/Pet.n4jsd b/samples/client/petstore/n4js/model/Pet.n4jsd new file mode 100644 index 00000000000..85ecd5e73a5 --- /dev/null +++ b/samples/client/petstore/n4js/model/Pet.n4jsd @@ -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"; +} diff --git a/samples/client/petstore/n4js/model/Tag.n4jsd b/samples/client/petstore/n4js/model/Tag.n4jsd new file mode 100644 index 00000000000..47d9eafee4c --- /dev/null +++ b/samples/client/petstore/n4js/model/Tag.n4jsd @@ -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; +} diff --git a/samples/client/petstore/n4js/model/User.n4jsd b/samples/client/petstore/n4js/model/User.n4jsd new file mode 100644 index 00000000000..93f6dc76c26 --- /dev/null +++ b/samples/client/petstore/n4js/model/User.n4jsd @@ -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; +}