From ce49fe587fe6ab4dcc2275590f49fb0b09777710 Mon Sep 17 00:00:00 2001
From: Erica Kastner
Date: Thu, 26 Mar 2020 04:02:44 -0500
Subject: [PATCH] Generator for JavaScript/Apollo Client (#5645)
Co-authored-by: William Cheng
---
README.md | 3 +-
bin/javascript-apollo-petstore.sh | 32 +
docs/generators/javascript-apollo.md | 247 ++++
.../JavascriptApolloClientCodegen.java | 1162 +++++++++++++++++
.../Javascript-Apollo/.babelrc.mustache | 33 +
.../Javascript-Apollo/ApiClient.mustache | 299 +++++
.../Javascript-Apollo/README.mustache | 226 ++++
.../resources/Javascript-Apollo/api.mustache | 88 ++
.../Javascript-Apollo/api_doc.mustache | 112 ++
.../Javascript-Apollo/api_test.mustache | 44 +
.../Javascript-Apollo/enumClass.mustache | 15 +
.../Javascript-Apollo/git_push.sh.mustache | 58 +
.../Javascript-Apollo/gitignore.mustache | 33 +
.../Javascript-Apollo/index.mustache | 58 +
.../Javascript-Apollo/licenseInfo.mustache | 12 +
.../resources/Javascript-Apollo/mocha.opts | 1 +
.../Javascript-Apollo/model.mustache | 4 +
.../Javascript-Apollo/model_doc.mustache | 26 +
.../Javascript-Apollo/model_test.mustache | 65 +
.../Javascript-Apollo/package.mustache | 29 +
.../partial_model_enum_class.mustache | 24 +
.../partial_model_generic.mustache | 82 ++
.../partial_model_inner_enum.mustache | 15 +
.../resources/Javascript-Apollo/travis.yml | 5 +
.../org.openapitools.codegen.CodegenConfig | 1 +
pom.xml | 1 +
website/i18n/en.json | 4 +
27 files changed, 2678 insertions(+), 1 deletion(-)
create mode 100755 bin/javascript-apollo-petstore.sh
create mode 100644 docs/generators/javascript-apollo.md
create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/.babelrc.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/ApiClient.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/README.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/api.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/api_doc.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/api_test.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/enumClass.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/git_push.sh.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/gitignore.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/index.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/licenseInfo.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/mocha.opts
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/model.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/model_doc.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/model_test.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/package.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_enum_class.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_generic.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_inner_enum.mustache
create mode 100644 modules/openapi-generator/src/main/resources/Javascript-Apollo/travis.yml
diff --git a/README.md b/README.md
index 97b5ddc4095..f692cef84a6 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ OpenAPI Generator allows generation of API client libraries (SDK generation), se
| | Languages/Frameworks |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, MicroProfile Rest Client), **k6**, **Kotlin**, **Lua**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x, 5.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs) |
+| **API clients** | **ActionScript**, **Ada**, **Apex**, **Bash**, **C**, **C#** (.net 2.0, 3.5 or later, .NET Standard 1.3 - 2.0, .NET Core 2.0), **C++** (cpp-restsdk, Qt5, Tizen), **Clojure**, **Dart (1.x, 2.x)**, **Elixir**, **Elm**, **Eiffel**, **Erlang**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx, Google API Client Library for Java, Rest-assured, Spring 5 Web Client, MicroProfile Rest Client), **k6**, **Kotlin**, **Lua**, **Nim**, **Node.js/JavaScript** (ES5, ES6, AngularJS with Google Closure Compiler annotations, Flow types, Apollo GraphQL DataStore), **Objective-C**, **OCaml**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust** (rust, rust-server), **Scala** (akka, http4s, scalaz, sttp, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x, 5.x), **Typescript** (AngularJS, Angular (2.x - 8.x), Aurelia, Axios, Fetch, Inversify, jQuery, Node, Rxjs) |
| **Server stubs** | **Ada**, **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed, Qt5 QHTTPEngine), **Erlang**, **F#** (Giraffe), **Go** (net/http, Gin), **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, Jersey, RestEasy, Play Framework, [PKMST](https://github.com/ProKarma-Inc/pkmst-getting-started-examples), [Vert.x](https://vertx.io/)), **Kotlin** (Spring Boot, Ktor, Vertx), **PHP** (Laravel, Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust** (rust-server), **Scala** ([Finch](https://github.com/finagle/finch), [Lagom](https://github.com/lagom/lagom), [Play](https://www.playframework.com/), Scalatra) |
| **API documentation generators** | **HTML**, **Confluence Wiki**, **Asciidoc** |
| **Configuration files** | [**Apache2**](https://httpd.apache.org/) |
@@ -803,6 +803,7 @@ Here is a list of template creators:
* Java (Rest-assured): @viclovsky
* Java (Java 11 Native HTTP client): @bbdouglas
* Javascript/NodeJS: @jfiala
+ * Javascript (Apollo DataSource): @erithmetic
* Javascript (Closure-annotated Angular) @achew22
* Javascript (Flow types) @jaypea
* JMeter: @davidkiss
diff --git a/bin/javascript-apollo-petstore.sh b/bin/javascript-apollo-petstore.sh
new file mode 100755
index 00000000000..13a20dea570
--- /dev/null
+++ b/bin/javascript-apollo-petstore.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+SCRIPT="$0"
+echo "# START SCRIPT: $SCRIPT"
+
+while [ -h "$SCRIPT" ] ; do
+ ls=`ls -ld "$SCRIPT"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ SCRIPT="$link"
+ else
+ SCRIPT=`dirname "$SCRIPT"`/"$link"
+ fi
+done
+
+if [ ! -d "${APP_DIR}" ]; then
+ APP_DIR=`dirname "$SCRIPT"`/..
+ APP_DIR=`cd "${APP_DIR}"; pwd`
+fi
+
+executable="./modules/openapi-generator-cli/target/openapi-generator-cli.jar"
+
+if [ ! -f "$executable" ]
+then
+ mvn -B clean package
+fi
+
+# if you've executed sbt assembly previously it will use that instead.
+export JAVA_OPTS="${JAVA_OPTS} -Xmx1024M -DloggerPath=conf/log4j.properties"
+ags="generate -t modules/openapi-generator/src/main/resources/Javascript-Apollo -i modules/openapi-generator/src/test/resources/2_0/petstore.yaml -g javascript-apollo -o samples/client/petstore/javascript-apollo --additional-properties appName=PetstoreClient $@"
+
+java $JAVA_OPTS -jar $executable $ags
diff --git a/docs/generators/javascript-apollo.md b/docs/generators/javascript-apollo.md
new file mode 100644
index 00000000000..3d012ca6441
--- /dev/null
+++ b/docs/generators/javascript-apollo.md
@@ -0,0 +1,247 @@
+---
+title: Config Options for javascript-apollo
+sidebar_label: javascript-apollo
+---
+
+| Option | Description | Values | Default |
+| ------ | ----------- | ------ | ------- |
+|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
+|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
+|enumNameSuffix|Suffix that will be appended to all enum names. A special 'v4-compat' value enables the backward-compatible behavior (as pre v4.2.3)| |v4-compat|
+|enumPropertyNaming|Naming convention for enum properties: 'camelCase', 'PascalCase', 'snake_case', 'UPPERCASE', and 'original'| |PascalCase|
+|modelPropertyNaming|Naming convention for the property: 'camelCase', 'PascalCase', 'snake_case' and 'original', which keeps the original name. Only change it if you provide your own run-time code for (de-)serialization of models| |original|
+|npmName|The name under which you want to publish generated npm package. Required to generate a full package| |null|
+|npmRepository|Use this property to set an url your private npmRepo in the package.json| |null|
+|npmVersion|The version of your npm package. If not provided, using the version from the OpenAPI specification file.| |1.0.0|
+|nullSafeAdditionalProps|Set to make additional properties types declare that their indexer may return undefined| |false|
+|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
+|snapshot|When setting this property to true, the version will be suffixed with -SNAPSHOT.yyyyMMddHHmm| |false|
+|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
+|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
+
+## IMPORT MAPPING
+
+| Type/Alias | Imports |
+| ---------- | ------- |
+
+
+## INSTANTIATION TYPES
+
+| Type/Alias | Instantiated By |
+| ---------- | --------------- |
+|array|Array|
+|list|Array|
+|map|Object|
+
+
+## LANGUAGE PRIMITIVES
+
+
+
Array
+
Blob
+
Date
+
File
+
Object
+
boolean
+
number
+
string
+
+
+## RESERVED WORDS
+
+
+
abstract
+
arguments
+
array
+
boolean
+
break
+
byte
+
case
+
catch
+
char
+
class
+
const
+
continue
+
date
+
debugger
+
default
+
delete
+
do
+
double
+
else
+
enum
+
eval
+
export
+
extends
+
false
+
final
+
finally
+
float
+
for
+
formparams
+
function
+
goto
+
hasownproperty
+
headerparams
+
if
+
implements
+
import
+
in
+
infinity
+
instanceof
+
int
+
interface
+
isfinite
+
isnan
+
isprototypeof
+
let
+
long
+
math
+
nan
+
native
+
new
+
null
+
number
+
object
+
package
+
private
+
protected
+
prototype
+
public
+
queryparameters
+
requestoptions
+
return
+
short
+
static
+
string
+
super
+
switch
+
synchronized
+
this
+
throw
+
throws
+
tostring
+
transient
+
true
+
try
+
typeof
+
undefined
+
useformdata
+
valueof
+
var
+
varlocaldeferred
+
varlocalpath
+
void
+
volatile
+
while
+
with
+
yield
+
+
+## FEATURE SET
+
+
+### Client Modification Feature
+| Name | Supported | Defined By |
+| ---- | --------- | ---------- |
+|BasePath|✓|ToolingExtension
+|Authorizations|✗|ToolingExtension
+|UserAgent|✗|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
+|Array|✓|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
+
+### 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/JavascriptApolloClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java
new file mode 100644
index 00000000000..c02bb74c73c
--- /dev/null
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavascriptApolloClientCodegen.java
@@ -0,0 +1,1162 @@
+/*
+ * Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
+ * Copyright 2018 SmartBear Software
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.openapitools.codegen.languages;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import io.swagger.v3.oas.models.media.ArraySchema;
+import io.swagger.v3.oas.models.media.Schema;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.openapitools.codegen.*;
+import org.openapitools.codegen.meta.features.DocumentationFeature;
+import org.openapitools.codegen.utils.ModelUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.*;
+
+import static org.openapitools.codegen.utils.OnceLogger.once;
+import static org.openapitools.codegen.utils.StringUtils.*;
+
+public class JavascriptApolloClientCodegen extends DefaultCodegen implements CodegenConfig {
+ @SuppressWarnings("hiding")
+ private static final Logger LOGGER = LoggerFactory.getLogger(JavascriptApolloClientCodegen.class);
+
+ public static final String PROJECT_NAME = "projectName";
+ public static final String MODULE_NAME = "moduleName";
+ public static final String PROJECT_DESCRIPTION = "projectDescription";
+ public static final String PROJECT_VERSION = "projectVersion";
+ public static final String USE_INHERITANCE = "useInheritance";
+ public static final String EMIT_JS_DOC = "emitJSDoc";
+ public static final String NPM_REPOSITORY = "npmRepository";
+
+ final String[][] JAVASCRIPT_SUPPORTING_FILES = new String[][]{
+ new String[]{"package.mustache", "package.json"},
+ // new String[]{"index.mustache", "src/index.js", },
+ // new String[]{"ApiClient.mustache", "src/ApiClient.js"},
+ new String[]{"git_push.sh.mustache", "git_push.sh"},
+ new String[]{"README.mustache", "README.md"},
+ new String[]{"mocha.opts", "mocha.opts"},
+ new String[]{"travis.yml", ".travis.yml"}
+ };
+
+ final String[][] JAVASCRIPT_ES6_SUPPORTING_FILES = new String[][]{
+ new String[]{"package.mustache", "package.json"},
+ // new String[]{"index.mustache", "src/index.js"},
+ // new String[]{"ApiClient.mustache", "src/ApiClient.js"},
+ new String[]{"git_push.sh.mustache", "git_push.sh"},
+ new String[]{"README.mustache", "README.md"},
+ new String[]{"mocha.opts", "mocha.opts"},
+ new String[]{"travis.yml", ".travis.yml"},
+ new String[]{".babelrc.mustache", ".babelrc"}
+ };
+
+ protected String projectName;
+ protected String moduleName;
+ protected String projectDescription;
+ protected String projectVersion;
+ protected String licenseName;
+
+ protected String invokerPackage;
+ protected String sourceFolder = "src";
+ protected boolean emitJSDoc = true;
+ protected String apiDocPath = "docs/";
+ protected String modelDocPath = "docs/";
+ protected String apiTestPath = "api/";
+ protected String modelTestPath = "model/";
+ protected String npmRepository = null;
+ private String modelPropertyNaming = "camelCase";
+
+ public JavascriptApolloClientCodegen() {
+ super();
+
+ modifyFeatureSet(features -> features.includeDocumentationFeatures(DocumentationFeature.Readme));
+
+ outputFolder = "generated-code/js";
+ modelTemplateFiles.put("model.mustache", ".js");
+ modelTestTemplateFiles.put("model_test.mustache", ".js");
+ apiTemplateFiles.put("api.mustache", ".js");
+ apiTestTemplateFiles.put("api_test.mustache", ".js");
+ // subfolder Javascript/es6
+ embeddedTemplateDir = templateDir = "Javascript-Apollo";
+ apiPackage = "api";
+ modelPackage = "model";
+ modelDocTemplateFiles.put("model_doc.mustache", ".md");
+ apiDocTemplateFiles.put("api_doc.mustache", ".md");
+
+ // default HIDE_GENERATION_TIMESTAMP to true
+ hideGenerationTimestamp = Boolean.TRUE;
+
+ // reference: http://www.w3schools.com/js/js_reserved.asp
+ setReservedWordsLowerCase(
+ Arrays.asList(
+ "abstract", "arguments", "boolean", "break", "byte",
+ "case", "catch", "char", "class", "const",
+ "continue", "debugger", "default", "delete", "do",
+ "double", "else", "enum", "eval", "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", "throws", "transient", "true",
+ "try", "typeof", "var", "void", "volatile",
+ "while", "with", "yield",
+ "Array", "Date", "eval", "function", "hasOwnProperty",
+ "Infinity", "isFinite", "isNaN", "isPrototypeOf",
+ "Math", "NaN", "Number", "Object",
+ "prototype", "String", "toString", "undefined", "valueOf")
+ );
+
+ languageSpecificPrimitives = new HashSet(
+ Arrays.asList("String", "Boolean", "Number", "Array", "Object", "Date", "File", "Blob")
+ );
+ defaultIncludes = new HashSet(languageSpecificPrimitives);
+
+ instantiationTypes.put("array", "Array");
+ instantiationTypes.put("list", "Array");
+ instantiationTypes.put("map", "Object");
+ typeMapping.clear();
+ typeMapping.put("array", "Array");
+ typeMapping.put("map", "Object");
+ typeMapping.put("List", "Array");
+ typeMapping.put("boolean", "Boolean");
+ typeMapping.put("string", "String");
+ typeMapping.put("int", "Number");
+ typeMapping.put("float", "Number");
+ typeMapping.put("number", "Number");
+ typeMapping.put("BigDecimal", "Number");
+ typeMapping.put("DateTime", "Date");
+ typeMapping.put("date", "Date");
+ typeMapping.put("long", "Number");
+ typeMapping.put("short", "Number");
+ typeMapping.put("char", "String");
+ typeMapping.put("double", "Number");
+ typeMapping.put("object", "Object");
+ typeMapping.put("integer", "Number");
+ typeMapping.put("ByteArray", "Blob");
+ typeMapping.put("binary", "File");
+ typeMapping.put("file", "File");
+ typeMapping.put("UUID", "String");
+ typeMapping.put("URI", "String");
+
+ importMapping.clear();
+
+ cliOptions.add(new CliOption(CodegenConstants.SOURCE_FOLDER, CodegenConstants.SOURCE_FOLDER_DESC).defaultValue("src"));
+ cliOptions.add(new CliOption(CodegenConstants.INVOKER_PACKAGE, CodegenConstants.INVOKER_PACKAGE_DESC));
+ cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC));
+ cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC));
+ cliOptions.add(new CliOption(PROJECT_NAME,
+ "name of the project (Default: generated from info.title or \"openapi-js-client\")"));
+ cliOptions.add(new CliOption(MODULE_NAME,
+ "module name for AMD, Node or globals (Default: generated from )"));
+ cliOptions.add(new CliOption(PROJECT_DESCRIPTION,
+ "description of the project (Default: using info.description or \"Client library of \")"));
+ cliOptions.add(new CliOption(PROJECT_VERSION,
+ "version of the project (Default: using info.version or \"1.0.0\")"));
+ cliOptions.add(new CliOption(CodegenConstants.LICENSE_NAME,
+ "name of the license the project uses (Default: using info.license.name)"));
+ cliOptions.add(new CliOption(EMIT_JS_DOC,
+ "generate JSDoc comments")
+ .defaultValue(Boolean.TRUE.toString()));
+ cliOptions.add(new CliOption(USE_INHERITANCE,
+ "use JavaScript prototype chains & delegation for inheritance")
+ .defaultValue(Boolean.TRUE.toString()));
+ cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, CodegenConstants.HIDE_GENERATION_TIMESTAMP_DESC)
+ .defaultValue(Boolean.TRUE.toString()));
+ cliOptions.add(new CliOption(CodegenConstants.MODEL_PROPERTY_NAMING, CodegenConstants.MODEL_PROPERTY_NAMING_DESC).defaultValue("camelCase"));
+ cliOptions.add(new CliOption(NPM_REPOSITORY, "Use this property to set an url your private npmRepo in the package.json"));
+ }
+
+ @Override
+ public CodegenType getTag() {
+ return CodegenType.CLIENT;
+ }
+
+ @Override
+ public String getName() {
+ return "javascript-apollo";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Generates a JavaScript client library using Apollo RESTDatasource.";
+ }
+
+ @Override
+ public void processOpts() {
+ super.processOpts();
+
+ if (StringUtils.isEmpty(System.getenv("JS_POST_PROCESS_FILE"))) {
+ LOGGER.info("Environment variable JS_POST_PROCESS_FILE not defined so the JS code may not be properly formatted. To define it, try 'export JS_POST_PROCESS_FILE=\"/usr/local/bin/js-beautify -r -f\"' (Linux/Mac)");
+ LOGGER.info("NOTE: To enable file post-processing, 'enablePostProcessFile' must be set to `true` (--enable-post-process-file for CLI).");
+ }
+
+ if (additionalProperties.containsKey(PROJECT_NAME)) {
+ setProjectName(((String) additionalProperties.get(PROJECT_NAME)));
+ }
+ if (additionalProperties.containsKey(MODULE_NAME)) {
+ setModuleName(((String) additionalProperties.get(MODULE_NAME)));
+ }
+ if (additionalProperties.containsKey(PROJECT_DESCRIPTION)) {
+ setProjectDescription(((String) additionalProperties.get(PROJECT_DESCRIPTION)));
+ }
+ if (additionalProperties.containsKey(PROJECT_VERSION)) {
+ setProjectVersion(((String) additionalProperties.get(PROJECT_VERSION)));
+ }
+ if (additionalProperties.containsKey(CodegenConstants.LICENSE_NAME)) {
+ setLicenseName(((String) additionalProperties.get(CodegenConstants.LICENSE_NAME)));
+ }
+ if (additionalProperties.containsKey(CodegenConstants.SOURCE_FOLDER)) {
+ setSourceFolder((String) additionalProperties.get(CodegenConstants.SOURCE_FOLDER));
+ }
+ if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) {
+ setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE));
+ }
+ if (additionalProperties.containsKey(USE_INHERITANCE)) {
+ setUseInheritance(convertPropertyToBooleanAndWriteBack(USE_INHERITANCE));
+ } else {
+ supportsInheritance = true;
+ supportsMixins = true;
+ }
+ if (additionalProperties.containsKey(EMIT_JS_DOC)) {
+ setEmitJSDoc(convertPropertyToBooleanAndWriteBack(EMIT_JS_DOC));
+ }
+ if (additionalProperties.containsKey(CodegenConstants.MODEL_PROPERTY_NAMING)) {
+ setModelPropertyNaming((String) additionalProperties.get(CodegenConstants.MODEL_PROPERTY_NAMING));
+ }
+ if (additionalProperties.containsKey(NPM_REPOSITORY)) {
+ setNpmRepository(((String) additionalProperties.get(NPM_REPOSITORY)));
+ }
+ }
+
+ @Override
+ public void preprocessOpenAPI(OpenAPI openAPI) {
+ super.preprocessOpenAPI(openAPI);
+
+ if (openAPI.getInfo() != null) {
+ Info info = openAPI.getInfo();
+ if (StringUtils.isBlank(projectName) && info.getTitle() != null) {
+ // when projectName is not specified, generate it from info.title
+ projectName = sanitizeName(dashize(info.getTitle()));
+ }
+ if (StringUtils.isBlank(projectVersion)) {
+ // when projectVersion is not specified, use info.version
+ projectVersion = escapeUnsafeCharacters(escapeQuotationMark(info.getVersion()));
+ }
+ if (projectDescription == null) {
+ // when projectDescription is not specified, use info.description
+ if (StringUtils.isEmpty(info.getDescription())) {
+ projectDescription = "JS API client generated by OpenAPI Generator";
+ } else {
+ projectDescription = sanitizeName(info.getDescription());
+ }
+ }
+
+ // when licenceName is not specified, use info.license
+ if (additionalProperties.get(CodegenConstants.LICENSE_NAME) == null && info.getLicense() != null) {
+ License license = info.getLicense();
+ licenseName = license.getName();
+ }
+ }
+
+ // default values
+ if (StringUtils.isBlank(projectName)) {
+ projectName = "openapi-js-client";
+ }
+ if (StringUtils.isBlank(moduleName)) {
+ moduleName = camelize(underscore(projectName));
+ }
+ if (StringUtils.isBlank(projectVersion)) {
+ projectVersion = "1.0.0";
+ }
+ if (projectDescription == null) {
+ projectDescription = "Client library of " + projectName;
+ }
+ if (StringUtils.isBlank(licenseName)) {
+ licenseName = "Unlicense";
+ }
+
+ additionalProperties.put(PROJECT_NAME, projectName);
+ additionalProperties.put(MODULE_NAME, moduleName);
+ additionalProperties.put(PROJECT_DESCRIPTION, escapeText(projectDescription));
+ additionalProperties.put(PROJECT_VERSION, projectVersion);
+ additionalProperties.put(CodegenConstants.LICENSE_NAME, licenseName);
+ additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage);
+ additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage);
+ additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage);
+ additionalProperties.put(CodegenConstants.SOURCE_FOLDER, sourceFolder);
+ additionalProperties.put(USE_INHERITANCE, supportsInheritance);
+ additionalProperties.put(EMIT_JS_DOC, emitJSDoc);
+ additionalProperties.put(NPM_REPOSITORY, npmRepository);
+
+ // make api and model doc path available in mustache template
+ additionalProperties.put("apiDocPath", apiDocPath);
+ additionalProperties.put("modelDocPath", modelDocPath);
+
+ String[][] supportingTemplateFiles = JAVASCRIPT_SUPPORTING_FILES;
+
+ for (String[] supportingTemplateFile : supportingTemplateFiles) {
+ supportingFiles.add(new SupportingFile(supportingTemplateFile[0], "", supportingTemplateFile[1]));
+ }
+
+ supportingFiles.add(new SupportingFile("index.mustache", createPath(sourceFolder, invokerPackage), "index.js"));
+ supportingFiles.add(new SupportingFile("ApiClient.mustache", createPath(sourceFolder, invokerPackage), "ApiClient.js"));
+
+ }
+
+ @Override
+ public String escapeReservedWord(String name) {
+ if (this.reservedWordsMappings().containsKey(name)) {
+ return this.reservedWordsMappings().get(name);
+ }
+ return "_" + name;
+ }
+
+ /**
+ * Concatenates an array of path segments into a path string.
+ *
+ * @param segments The path segments to concatenate. A segment may contain either of the file separator characters '\' or '/'.
+ * A segment is ignored if it is null, empty or ".".
+ * @return A path string using the correct platform-specific file separator character.
+ */
+ private String createPath(String... segments) {
+ StringBuilder buf = new StringBuilder();
+ for (String segment : segments) {
+ if (!StringUtils.isEmpty(segment) && !segment.equals(".")) {
+ if (buf.length() != 0)
+ buf.append(File.separatorChar);
+ buf.append(segment);
+ }
+ }
+ for (int i = 0; i < buf.length(); i++) {
+ char c = buf.charAt(i);
+ if ((c == '/' || c == '\\') && c != File.separatorChar)
+ buf.setCharAt(i, File.separatorChar);
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public String apiTestFileFolder() {
+ return createPath(outputFolder, "test", invokerPackage, apiTestPath);
+ }
+
+ @Override
+ public String modelTestFileFolder() {
+ return createPath(outputFolder, "test", invokerPackage, modelTestPath);
+ }
+
+ @Override
+ public String apiFileFolder() {
+ return createPath(outputFolder, sourceFolder, invokerPackage, apiPackage());
+ }
+
+ @Override
+ public String modelFileFolder() {
+ return createPath(outputFolder, sourceFolder, invokerPackage, modelPackage());
+ }
+
+ public String getInvokerPackage() {
+ return invokerPackage;
+ }
+
+ public void setInvokerPackage(String invokerPackage) {
+ this.invokerPackage = invokerPackage;
+ }
+
+ public void setSourceFolder(String sourceFolder) {
+ this.sourceFolder = sourceFolder;
+ }
+
+ public void setProjectName(String projectName) {
+ this.projectName = projectName;
+ }
+
+ public void setModuleName(String moduleName) {
+ this.moduleName = moduleName;
+ }
+
+ public void setProjectDescription(String projectDescription) {
+ this.projectDescription = projectDescription;
+ }
+
+ public void setProjectVersion(String projectVersion) {
+ this.projectVersion = projectVersion;
+ }
+
+ public void setLicenseName(String licenseName) {
+ this.licenseName = licenseName;
+ }
+
+ public void setNpmRepository(String npmRepository) {
+ this.npmRepository = npmRepository;
+ }
+
+ public void setUseInheritance(boolean useInheritance) {
+ this.supportsInheritance = useInheritance;
+ this.supportsMixins = useInheritance;
+ }
+
+ public void setEmitJSDoc(boolean emitJSDoc) {
+ this.emitJSDoc = emitJSDoc;
+ }
+
+ @Override
+ public String apiDocFileFolder() {
+ return createPath(outputFolder, apiDocPath);
+ }
+
+ @Override
+ public String modelDocFileFolder() {
+ return createPath(outputFolder, modelDocPath);
+ }
+
+ @Override
+ public String toApiDocFilename(String name) {
+ return toApiName(name);
+ }
+
+ @Override
+ public String toModelDocFilename(String name) {
+ return toModelName(name);
+ }
+
+ @Override
+ public String toApiTestFilename(String name) {
+ return toApiName(name) + ".spec";
+ }
+
+ @Override
+ public String toModelTestFilename(String name) {
+ return toModelName(name) + ".spec";
+ }
+
+ public String getModelPropertyNaming() {
+ return this.modelPropertyNaming;
+ }
+
+ private String getNameUsingModelPropertyNaming(String name) {
+ switch (CodegenConstants.MODEL_PROPERTY_NAMING_TYPE.valueOf(getModelPropertyNaming())) {
+ case original:
+ return name;
+ case camelCase:
+ return camelize(name, true);
+ case PascalCase:
+ return camelize(name);
+ case snake_case:
+ return underscore(name);
+ default:
+ throw new IllegalArgumentException("Invalid model property naming '" +
+ name + "'. Must be 'original', 'camelCase', " +
+ "'PascalCase' or 'snake_case'");
+ }
+ }
+
+ @Override
+ public String toVarName(String name) {
+ // sanitize name
+ name = sanitizeName(name); // FIXME parameter should not be assigned. Also declare it as "final"
+
+ if ("_".equals(name)) {
+ name = "_u";
+ }
+
+ // if it's all uppper case, do nothing
+ if (name.matches("^[A-Z_]*$")) {
+ return name;
+ }
+
+ // camelize (lower first character) the variable name
+ // pet_id => petId
+ name = getNameUsingModelPropertyNaming(name);
+
+ // for reserved word or word starting with number, append _
+ if (isReservedWord(name) || name.matches("^\\d.*")) {
+ name = escapeReservedWord(name);
+ }
+
+ return name;
+ }
+
+ @Override
+ public String toParamName(String name) {
+ // should be the same as variable name
+ return toVarName(name);
+ }
+
+ @Override
+ public String toModelName(String name) {
+ name = sanitizeName(name); // FIXME parameter should not be assigned. Also declare it as "final"
+
+ if (!StringUtils.isEmpty(modelNamePrefix)) {
+ name = modelNamePrefix + "_" + name;
+ }
+
+ if (!StringUtils.isEmpty(modelNameSuffix)) {
+ name = name + "_" + modelNameSuffix;
+ }
+
+ // camelize the model name
+ // phone_number => PhoneNumber
+ name = camelize(name);
+
+ // model name cannot use reserved keyword, e.g. return
+ if (isReservedWord(name)) {
+ String modelName = "Model" + name;
+ LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + modelName);
+ return modelName;
+ }
+
+ // model name starts with number
+ if (name.matches("^\\d.*")) {
+ String modelName = "Model" + name; // e.g. 200Response => Model200Response (after camelize)
+ LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + modelName);
+ return modelName;
+ }
+
+ return name;
+ }
+
+ @Override
+ public String toModelFilename(String name) {
+ // should be the same as the model name
+ return toModelName(name);
+ }
+
+ @Override
+ public String toModelImport(String name) {
+ return name;
+ }
+
+ @Override
+ public String toApiImport(String name) {
+ return toApiName(name);
+ }
+
+ @Override
+ public String getTypeDeclaration(Schema p) {
+ if (ModelUtils.isArraySchema(p)) {
+ ArraySchema ap = (ArraySchema) p;
+ Schema inner = ap.getItems();
+ return "[" + getTypeDeclaration(inner) + "]";
+ } else if (ModelUtils.isMapSchema(p)) {
+ Schema inner = ModelUtils.getAdditionalProperties(p);
+ return "{String: " + getTypeDeclaration(inner) + "}";
+ }
+ return super.getTypeDeclaration(p);
+ }
+
+ @Override
+ public String toDefaultValue(Schema p) {
+ if (ModelUtils.isBooleanSchema(p)) {
+ if (p.getDefault() != null) {
+ return p.getDefault().toString();
+ }
+ } else if (ModelUtils.isDateSchema(p)) {
+ // TODO
+ } else if (ModelUtils.isDateTimeSchema(p)) {
+ // TODO
+ } else if (ModelUtils.isNumberSchema(p)) {
+ if (p.getDefault() != null) {
+ return p.getDefault().toString();
+ }
+ } else if (ModelUtils.isIntegerSchema(p)) {
+ if (p.getDefault() != null) {
+ return p.getDefault().toString();
+ }
+ } else if (ModelUtils.isStringSchema(p)) {
+ if (p.getDefault() != null) {
+ return "'" + p.getDefault() + "'";
+ }
+ }
+
+ return null;
+ }
+
+ public void setModelPropertyNaming(String naming) {
+ if ("original".equals(naming) || "camelCase".equals(naming) ||
+ "PascalCase".equals(naming) || "snake_case".equals(naming)) {
+ this.modelPropertyNaming = naming;
+ } else {
+ throw new IllegalArgumentException("Invalid model property naming '" +
+ naming + "'. Must be 'original', 'camelCase', " +
+ "'PascalCase' or 'snake_case'");
+ }
+ }
+
+ @Override
+ public String toDefaultValueWithParam(String name, Schema p) {
+ String type = normalizeType(getTypeDeclaration(p));
+ if (!StringUtils.isEmpty(p.get$ref())) {
+ return " = " + type + ".constructFromObject(data['" + name + "']);";
+ } else {
+ return " = ApiClient.convertToType(data['" + name + "'], " + type + ");";
+ }
+ }
+
+ @Override
+ public void setParameterExampleValue(CodegenParameter p) {
+ String example;
+
+ if (p.defaultValue == null) {
+ example = p.example;
+ } else {
+ example = p.defaultValue;
+ }
+
+ String type = p.baseType;
+ if (type == null) {
+ type = p.dataType;
+ }
+
+ if (Boolean.TRUE.equals(p.isInteger)) {
+ if (example == null) {
+ example = "56";
+ }
+ } else if (Boolean.TRUE.equals(p.isLong)) {
+ if (example == null) {
+ example = "789";
+ }
+ } else if (Boolean.TRUE.equals(p.isDouble)
+ || Boolean.TRUE.equals(p.isFloat)
+ || Boolean.TRUE.equals(p.isNumber)) {
+ if (example == null) {
+ example = "3.4";
+ }
+ } else if (Boolean.TRUE.equals(p.isBoolean)) {
+ if (example == null) {
+ example = "true";
+ }
+ } else if (Boolean.TRUE.equals(p.isFile) || Boolean.TRUE.equals(p.isBinary)) {
+ if (example == null) {
+ example = "/path/to/file";
+ }
+ example = "\"" + escapeText(example) + "\"";
+ } else if (Boolean.TRUE.equals(p.isDate)) {
+ if (example == null) {
+ example = "2013-10-20";
+ }
+ example = "new Date(\"" + escapeText(example) + "\")";
+ } else if (Boolean.TRUE.equals(p.isDateTime)) {
+ if (example == null) {
+ example = "2013-10-20T19:20:30+01:00";
+ }
+ example = "new Date(\"" + escapeText(example) + "\")";
+ } else if (Boolean.TRUE.equals(p.isString)) {
+ if (example == null) {
+ example = p.paramName + "_example";
+ }
+ example = "\"" + escapeText(example) + "\"";
+
+ } else if (!languageSpecificPrimitives.contains(type)) {
+ // type is a model class, e.g. User
+ example = "new " + moduleName + "." + type + "()";
+ }
+
+ // container
+ if (Boolean.TRUE.equals(p.isListContainer)) {
+ example = setPropertyExampleValue(p.items);
+ example = "[" + example + "]";
+ } else if (Boolean.TRUE.equals(p.isMapContainer)) {
+ example = setPropertyExampleValue(p.items);
+ example = "{key: " + example + "}";
+ } else if (example == null) {
+ example = "null";
+ }
+
+ p.example = example;
+ }
+
+ protected String setPropertyExampleValue(CodegenProperty p) {
+ String example;
+
+ if (p == null) {
+ return "null";
+ }
+
+ if (p.defaultValue == null) {
+ example = p.example;
+ } else {
+ example = p.defaultValue;
+ }
+
+ String type = p.baseType;
+ if (type == null) {
+ type = p.dataType;
+ }
+
+ if (Boolean.TRUE.equals(p.isInteger)) {
+ if (example == null) {
+ example = "56";
+ }
+ } else if (Boolean.TRUE.equals(p.isLong)) {
+ if (example == null) {
+ example = "789";
+ }
+ } else if (Boolean.TRUE.equals(p.isDouble)
+ || Boolean.TRUE.equals(p.isFloat)
+ || Boolean.TRUE.equals(p.isNumber)) {
+ if (example == null) {
+ example = "3.4";
+ }
+ } else if (Boolean.TRUE.equals(p.isBoolean)) {
+ if (example == null) {
+ example = "true";
+ }
+ } else if (Boolean.TRUE.equals(p.isFile) || Boolean.TRUE.equals(p.isBinary)) {
+ if (example == null) {
+ example = "/path/to/file";
+ }
+ example = "\"" + escapeText(example) + "\"";
+ } else if (Boolean.TRUE.equals(p.isDate)) {
+ if (example == null) {
+ example = "2013-10-20";
+ }
+ example = "new Date(\"" + escapeText(example) + "\")";
+ } else if (Boolean.TRUE.equals(p.isDateTime)) {
+ if (example == null) {
+ example = "2013-10-20T19:20:30+01:00";
+ }
+ example = "new Date(\"" + escapeText(example) + "\")";
+ } else if (Boolean.TRUE.equals(p.isString)) {
+ if (example == null) {
+ example = p.name + "_example";
+ }
+ example = "\"" + escapeText(example) + "\"";
+
+ } else if (!languageSpecificPrimitives.contains(type)) {
+ // type is a model class, e.g. User
+ example = "new " + moduleName + "." + type + "()";
+ }
+
+ return example;
+ }
+
+ /**
+ * Normalize type by wrapping primitive types with single quotes.
+ *
+ * @param type Primitive type
+ * @return Normalized type
+ */
+ private String normalizeType(String type) {
+ return type.replaceAll("\\b(Boolean|Integer|Number|String|Date|Blob)\\b", "'$1'");
+ }
+
+ @Override
+ public String getSchemaType(Schema p) {
+ String openAPIType = super.getSchemaType(p);
+ String type = null;
+ if (typeMapping.containsKey(openAPIType)) {
+ type = typeMapping.get(openAPIType);
+ if (!needToImport(type)) {
+ return type;
+ }
+ } else {
+ type = openAPIType;
+ }
+ if (null == type) {
+ LOGGER.error("No Type defined for Schema " + p);
+ }
+ return toModelName(type);
+ }
+
+ @Override
+ public String toOperationId(String operationId) {
+ // throw exception if method name is empty
+ if (StringUtils.isEmpty(operationId)) {
+ throw new RuntimeException("Empty method/operation name (operationId) not allowed");
+ }
+
+ operationId = camelize(sanitizeName(operationId), true);
+
+ // method name cannot use reserved keyword, e.g. return
+ if (isReservedWord(operationId)) {
+ String newOperationId = camelize("call_" + operationId, true);
+ LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + newOperationId);
+ return newOperationId;
+ }
+
+ // operationId starts with a number
+ if (operationId.matches("^\\d.*")) {
+ String newOperationId = camelize("call_" + operationId, true);
+ LOGGER.warn(operationId + " (starting with a number) cannot be used as method name. Renamed to " + newOperationId);
+ return newOperationId;
+ }
+
+ return operationId;
+ }
+
+ @Override
+ public CodegenModel fromModel(String name, Schema model) {
+
+ // TODO: 5.0: Remove the camelCased vendorExtension below and ensure templates use the newer property naming.
+ once(LOGGER).warn("4.3.0 has deprecated the use of vendor extensions which don't follow lower-kebab casing standards with x- prefix.");
+
+ Map allDefinitions = ModelUtils.getSchemas(this.openAPI);
+ CodegenModel codegenModel = super.fromModel(name, model);
+
+ if (allDefinitions != null && codegenModel != null && codegenModel.parent != null && codegenModel.hasEnums) {
+ final Schema parentModel = allDefinitions.get(codegenModel.parentSchema);
+ final CodegenModel parentCodegenModel = super.fromModel(codegenModel.parent, parentModel);
+ codegenModel = JavascriptApolloClientCodegen.reconcileInlineEnums(codegenModel, parentCodegenModel);
+ }
+ if (ModelUtils.isArraySchema(model)) {
+ ArraySchema am = (ArraySchema) model;
+ if (codegenModel != null && am.getItems() != null) {
+ String itemType = getSchemaType(am.getItems());
+ codegenModel.vendorExtensions.put("x-isArray", true); // TODO: 5.0 Remove
+ codegenModel.vendorExtensions.put("x-is-array", true);
+ codegenModel.vendorExtensions.put("x-itemType", itemType); // TODO: 5.0 Remove
+ codegenModel.vendorExtensions.put("x-item-type", itemType);
+ }
+ } else if (ModelUtils.isMapSchema(model)) {
+ if (codegenModel != null && ModelUtils.getAdditionalProperties(model) != null) {
+ String itemType = getSchemaType(ModelUtils.getAdditionalProperties(model));
+ codegenModel.vendorExtensions.put("x-isMap", true); // TODO: 5.0 Remove
+ codegenModel.vendorExtensions.put("x-is-map", true);
+ codegenModel.vendorExtensions.put("x-itemType", itemType); // TODO: 5.0 Remove
+ codegenModel.vendorExtensions.put("x-item-type", itemType);
+ } else {
+ String type = model.getType();
+ if (codegenModel != null && isPrimitiveType(type)) {
+ codegenModel.vendorExtensions.put("x-isPrimitive", true); // TODO: 5.0 Remove
+ codegenModel.vendorExtensions.put("x-is-primitive", true);
+ }
+ }
+ }
+
+ return codegenModel;
+ }
+
+ /*
+ private String sanitizePath(String p) {
+ //prefer replace a ', instead of a fuLL URL encode for readability
+ return p.replaceAll("'", "%27");
+ }*/
+
+ private String trimBrackets(String s) {
+ if (s != null) {
+ int beginIdx = s.charAt(0) == '[' ? 1 : 0;
+ int endIdx = s.length();
+ if (s.charAt(endIdx - 1) == ']')
+ endIdx--;
+ return s.substring(beginIdx, endIdx);
+ }
+ return null;
+ }
+
+ private String getJSDocType(CodegenModel cm, CodegenProperty cp) {
+ if (Boolean.TRUE.equals(cp.isContainer)) {
+ if (cp.containerType.equals("array"))
+ return "Array.<" + cp.items + ">";
+ else if (cp.containerType.equals("map"))
+ return "Object.";
+ }
+ String dataType = trimBrackets(cp.datatypeWithEnum);
+ if (cp.isEnum) {
+ dataType = cm.classname + '.' + dataType;
+ }
+ return dataType;
+ }
+
+ private String getJSDocType(CodegenParameter cp) {
+ String dataType = trimBrackets(cp.dataType);
+ if (Boolean.TRUE.equals(cp.isListContainer)) {
+ return "Array.<" + dataType + ">";
+ } else if (Boolean.TRUE.equals(cp.isMapContainer)) {
+ return "Object.";
+ }
+ return dataType;
+ }
+
+ private String getJSDocType(CodegenOperation co) {
+ String returnType = trimBrackets(co.returnType);
+ if (returnType != null) {
+ if (Boolean.TRUE.equals(co.isListContainer)) {
+ return "Array.<" + returnType + ">";
+ } else if (Boolean.TRUE.equals(co.isMapContainer)) {
+ return "Object.";
+ }
+ }
+ return returnType;
+ }
+
+ private boolean isPrimitiveType(String type) {
+ final String[] primitives = {"number", "integer", "string", "boolean", "null"};
+ return Arrays.asList(primitives).contains(type);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map postProcessOperationsWithModels(Map objs, List
+*
+* A non-AMD browser application (discouraged) might do something like this:
+*
+* var xxxSvc = new {{moduleName}}.XxxApi(); // Allocate the API class we're going to use.
+* var yyy = new {{moduleName}}.Yyy(); // Construct a model instance.
+* yyyModel.someProperty = 'someValue';
+* ...
+* var zzz = xxxSvc.doSomething(yyyModel); // Invoke the service.
+* ...
+*
+*
+* @module {{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}index
+* @version {{projectVersion}}
+*/{{/emitJSDoc}}
+export {
+ {{=< >=}}
+ <#emitJSDoc>/**
+ * The ApiClient constructor.
+ * @property {module:<#invokerPackage>/ApiClient}
+ */
+ ApiClient<#models>,
+
+ <#emitJSDoc>/**
+ * The model constructor.
+ * @property {module:<#invokerPackage>/<#modelPackage>/}
+ */
+ <#apiInfo><#apis>,
+
+ <#emitJSDoc>/**
+ * The service constructor.
+ * @property {module:<#invokerPackage>/<#apiPackage>/}
+ */
+
+};<={{ }}=>
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/licenseInfo.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/licenseInfo.mustache
new file mode 100644
index 00000000000..40cac6d4a3c
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/licenseInfo.mustache
@@ -0,0 +1,12 @@
+/**
+ * {{{appName}}}
+ * {{{appDescription}}}
+ *
+ * {{#version}}The version of the OpenAPI document: {{{version}}}{{/version}}
+ * {{#infoEmail}}Contact: {{{infoEmail}}}{{/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/Javascript-Apollo/mocha.opts b/modules/openapi-generator/src/main/resources/Javascript-Apollo/mocha.opts
new file mode 100644
index 00000000000..907011807d6
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/mocha.opts
@@ -0,0 +1 @@
+--timeout 10000
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/model.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model.mustache
new file mode 100644
index 00000000000..3ddf83730f5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model.mustache
@@ -0,0 +1,4 @@
+{{>licenseInfo}}
+import ApiClient from '../ApiClient';
+{{#imports}}import {{import}} from './{{import}}';
+{{/imports}}{{#models}}{{#model}}{{#isEnum}}{{>partial_model_enum_class}}{{/isEnum}}{{^isEnum}}{{>partial_model_generic}}{{/isEnum}}{{/model}}{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_doc.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_doc.mustache
new file mode 100644
index 00000000000..04ab6f949ac
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_doc.mustache
@@ -0,0 +1,26 @@
+{{#models}}{{#model}}{{#isEnum}}# {{moduleName}}.{{classname}}
+
+## Enum
+
+{{#allowableValues}}{{#enumVars}}
+* `{{name}}` (value: `{{{value}}}`)
+{{/enumVars}}{{/allowableValues}}
+{{/isEnum}}{{^isEnum}}# {{moduleName}}.{{classname}}
+
+## Properties
+
+Name | Type | Description | Notes
+------------ | ------------- | ------------- | -------------
+{{#vars}}**{{name}}** | {{#isPrimitiveType}}**{{dataType}}**{{/isPrimitiveType}}{{^isPrimitiveType}}[**{{dataType}}**]({{complexType}}.md){{/isPrimitiveType}} | {{description}} | {{^required}}[optional] {{/required}}{{#isReadOnly}}[readonly] {{/isReadOnly}}{{#defaultValue}}[default to {{defaultValue}}]{{/defaultValue}}
+{{/vars}}
+{{#vars}}{{#isEnum}}
+
+
+## Enum: {{datatypeWithEnum}}
+
+{{#allowableValues}}{{#enumVars}}
+* `{{name}}` (value: `{{{value}}}`)
+{{/enumVars}}{{/allowableValues}}
+
+{{/isEnum}}{{/vars}}
+{{/isEnum}}{{/model}}{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_test.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_test.mustache
new file mode 100644
index 00000000000..9c80d3291b5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/model_test.mustache
@@ -0,0 +1,65 @@
+{{>licenseInfo}}
+(function(root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD.
+ define(['expect.js', process.cwd()+'/src/{{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}index'], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ // CommonJS-like environments that support module.exports, like Node.
+ factory(require('expect.js'), require(process.cwd()+'/src/{{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}index'));
+ } else {
+ // Browser globals (root is window)
+ factory(root.expect, root.{{moduleName}});
+ }
+}(this, function(expect, {{moduleName}}) {
+ 'use strict';
+
+ var instance;
+
+ beforeEach(function() {
+{{#models}}
+{{#model}}
+{{^isEnum}}
+ instance = new {{moduleName}}.{{classname}}();
+{{/isEnum}}
+{{/model}}
+{{/models}}
+ });
+
+ var getProperty = function(object, getter, property) {
+ // Use getter method if present; otherwise, get the property directly.
+ if (typeof object[getter] === 'function')
+ return object[getter]();
+ else
+ return object[property];
+ }
+
+ var setProperty = function(object, setter, property, value) {
+ // Use setter method if present; otherwise, set the property directly.
+ if (typeof object[setter] === 'function')
+ object[setter](value);
+ else
+ object[property] = value;
+ }
+
+ describe('{{classname}}', function() {
+ it('should create an instance of {{classname}}', function() {
+ // uncomment below and update the code to test {{classname}}
+ //var instane = new {{moduleName}}.{{classname}}();
+ //expect(instance).to.be.a({{moduleName}}.{{classname}});
+ });
+
+{{#models}}
+{{#model}}
+{{#vars}}
+ it('should have the property {{name}} (base name: "{{baseName}}")', function() {
+ // uncomment below and update the code to test the property {{name}}
+ //var instane = new {{moduleName}}.{{classname}}();
+ //expect(instance).to.be();
+ });
+
+{{/vars}}
+{{/model}}
+{{/models}}
+ });
+
+}));
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/package.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/package.mustache
new file mode 100644
index 00000000000..e6d3c752295
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/package.mustache
@@ -0,0 +1,29 @@
+{
+ "name": "{{{projectName}}}",
+ "version": "{{{projectVersion}}}",
+ "description": "{{{projectDescription}}}",
+ "license": "{{licenseName}}",
+ "main": "src/index.js",
+ "scripts": {
+ "test": "mocha --require @babel/register --recursive"
+ },
+ "browser": {
+ "fs": false
+ },
+{{#npmRepository}}
+ "publishConfig":{
+ "registry":"{{npmRepository}}"
+ },
+{{/npmRepository}}
+ "dependencies": {
+ "apollo-datasource-rest": "^0.7.0"
+ },
+ "devDependencies": {
+ "expect.js": "^0.3.1",
+ "mocha": "^5.2.0",
+ "sinon": "^7.2.0"
+ },
+ "files": [
+ "src"
+ ]
+}
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_enum_class.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_enum_class.mustache
new file mode 100644
index 00000000000..e88ddd9cfb5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_enum_class.mustache
@@ -0,0 +1,24 @@
+{{#emitJSDoc}}/**
+* Enum class {{classname}}.
+* @enum {{=<% %>=}}{<%datatype%>}<%={{ }}=%>
+* @readonly
+*/{{/emitJSDoc}}
+export default class {{classname}} {
+ {{#allowableValues}}{{#enumVars}}
+ {{#emitJSDoc}}/**
+ * value: {{{value}}}
+ * @const
+ */{{/emitJSDoc}}
+ "{{name}}" = {{{value}}};
+
+ {{/enumVars}}{{/allowableValues}}
+
+ {{#emitJSDoc}}/**
+ * Returns a {{classname}} enum value from a Javascript object name.
+ * @param {Object} data The plain JavaScript object containing the name of the enum value.
+ * @return {{=< >=}}{module:<#invokerPackage>/<#modelPackage>/}<={{ }}=> The enum {{classname}} value.
+ */{{/emitJSDoc}}
+ static constructFromObject(object) {
+ return object;
+ }
+}
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_generic.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_generic.mustache
new file mode 100644
index 00000000000..c16b43ec4d4
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_generic.mustache
@@ -0,0 +1,82 @@
+
+{{#models}}{{#model}}{{#emitJSDoc}}/**
+ * The {{classname}} model module.
+ * @module {{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{classname}}
+ * @version {{projectVersion}}
+ */{{/emitJSDoc}}
+class {{classname}} {{#parent}}{{^parentModel}}{{#vendorExtensions.x-is-array}}extends Array {{/vendorExtensions.x-is-array}}{{/parentModel}}{{/parent}}{
+ {{#vars}}
+ {{#emitJSDoc}}/**
+ * @member {{=< >=}}{<&vendorExtensions.x-jsdoc-type>}<={{ }}=> {{baseName}}
+ * @type {{=< >=}}{<&vendorExtensions.x-jsdoc-type>}<={{ }}=>{{#defaultValue}}
+ * @default {{{defaultValue}}}{{/defaultValue}}
+ */{{/emitJSDoc}}
+ {{baseName}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}};
+ {{/vars}}
+
+ {{#useInheritance}}{{#interfaceModels}}{{#allVars}}{{#emitJSDoc}}/**
+ * @member {{=< >=}}{<&vendorExtensions.x-jsdoc-type>}<={{ }}=> {{baseName}}
+ * @type {{=< >=}}{<&vendorExtensions.x-jsdoc-type>}<={{ }}=>
+ */{{/emitJSDoc}}
+ #{{baseName}};
+ {{/allVars}}{{/interfaceModels}}{{/useInheritance}}
+
+ {{#emitJSDoc}}/**
+ * Constructs a new {{classname}}.{{#description}}
+ * {{description}}{{/description}}
+ * @alias module:{{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{classname}}{{#useInheritance}}{{#parent}}
+ * @extends {{#parentModel}}module:{{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{classname}}{{/parentModel}}{{^parentModel}}{{#vendorExtensions.x-is-array}}Array{{/vendorExtensions.x-is-array}}{{#vendorExtensions.x-is-map}}Object{{/vendorExtensions.x-is-map}}{{/parentModel}}{{/parent}}{{#interfaces}}
+ * @implements module:{{#invokerPackage}}{{invokerPackage}}/{{/invokerPackage}}{{#modelPackage}}{{modelPackage}}/{{/modelPackage}}{{.}}{{/interfaces}}{{/useInheritance}}{{#vendorExtensions.x-all-required}}
+ * @param {{name}} {{=< >=}}{<&vendorExtensions.x-jsdoc-type>}<={{ }}=> {{#description}}{{{description}}}{{/description}}{{/vendorExtensions.x-all-required}}
+ */{{/emitJSDoc}}
+ constructor({{#vendorExtensions.x-all-required}}{{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-all-required}}) { {{#parent}}{{^parentModel}}{{#vendorExtensions.x-is-array}}
+ super();
+ {{/vendorExtensions.x-is-array}}{{/parentModel}}{{/parent}}{{#useInheritance}}
+ {{#interfaceModels}}{{classname}}.initialize(this{{#vendorExtensions.x-all-required}}, {{name}}{{/vendorExtensions.x-all-required}});{{/interfaceModels}}{{/useInheritance}}
+ {{classname}}.initialize(this{{#vendorExtensions.x-all-required}}, {{name}}{{/vendorExtensions.x-all-required}});
+ }
+
+ {{#emitJSDoc}}/**
+ * Initializes the fields of this object.
+ * This method is used by the constructors of any subclasses, in order to implement multiple inheritance (mix-ins).
+ * Only for internal use.
+ */{{/emitJSDoc}}
+ static initialize(obj{{#vendorExtensions.x-all-required}}, {{name}}{{/vendorExtensions.x-all-required}}) { {{#vars}}{{#required}}
+ obj['{{baseName}}'] = {{name}};{{/required}}{{/vars}}
+ }
+
+ {{#emitJSDoc}}/**
+ * Constructs a {{classname}} from a plain JavaScript object, optionally creating a new instance.
+ * Copies all relevant properties from data to obj if supplied or a new instance if not.
+ * @param {Object} data The plain JavaScript object bearing properties of interest.
+ * @param {{=< >=}}{module:<#invokerPackage>/<#modelPackage>/}<={{ }}=> obj Optional instance to populate.
+ * @return {{=< >=}}{module:<#invokerPackage>/<#modelPackage>/}<={{ }}=> The populated {{classname}} instance.
+ */{{/emitJSDoc}}
+ static constructFromObject(data, obj) {
+ if (data){{! TODO: support polymorphism: discriminator property on data determines class to instantiate.}} {
+ obj = obj || new {{classname}}();{{#parent}}{{^parentModel}}
+
+ ApiClient.constructFromObject(data, obj, '{{vendorExtensions.x-item-type}}');
+ {{/parentModel}}{{/parent}}{{#useInheritance}}{{#parentModel}}
+ {{classname}}.constructFromObject(data, obj);{{/parentModel}}{{#interfaces}}
+ {{.}}.constructFromObject(data, obj);{{/interfaces}}{{/useInheritance}}
+
+ {{#vars}}
+ if (data.hasOwnProperty('{{baseName}}')) {
+ obj['{{baseName}}']{{{defaultValueWithParam}}}
+ }
+ {{/vars}}
+ }
+ return obj;
+ }
+ {{/model}}
+}
+
+{{#vars}}{{#isEnum}}{{^isContainer}}
+{{>partial_model_inner_enum}}
+{{/isContainer}}{{/isEnum}}{{#items.isEnum}}{{#items}}{{^isContainer}}
+{{>partial_model_inner_enum}}
+{{/isContainer}}{{/items}}{{/items.isEnum}}{{/vars}}
+
+export default {{classname}};
+{{/models}}
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_inner_enum.mustache b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_inner_enum.mustache
new file mode 100644
index 00000000000..17cea4407d5
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/partial_model_inner_enum.mustache
@@ -0,0 +1,15 @@
+{{#emitJSDoc}}/**
+ * Allowed values for the {{baseName}} property.
+ * @enum {{=<% %>=}}{<%datatype%>}<%={{ }}=%>
+ * @readonly
+ */{{/emitJSDoc}}
+{{classname}}['{{datatypeWithEnum}}'] = {
+{{#allowableValues}}{{#enumVars}}
+ {{#emitJSDoc}}/**
+ * value: {{{value}}}
+ * @const
+ */{{/emitJSDoc}}
+ "{{name}}": {{{value}}}{{^-last}},
+ {{/-last}}
+{{/enumVars}}{{/allowableValues}}
+};
diff --git a/modules/openapi-generator/src/main/resources/Javascript-Apollo/travis.yml b/modules/openapi-generator/src/main/resources/Javascript-Apollo/travis.yml
new file mode 100644
index 00000000000..0968f7a4333
--- /dev/null
+++ b/modules/openapi-generator/src/main/resources/Javascript-Apollo/travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+cache: npm
+node_js:
+ - "6"
+ - "6.1"
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 58a7428cf92..cac33205e17 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
@@ -60,6 +60,7 @@ org.openapitools.codegen.languages.JavaResteasyServerCodegen
org.openapitools.codegen.languages.JavaResteasyEapServerCodegen
org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen
org.openapitools.codegen.languages.JavascriptClientCodegen
+org.openapitools.codegen.languages.JavascriptApolloClientCodegen
org.openapitools.codegen.languages.JavascriptFlowtypedClientCodegen
org.openapitools.codegen.languages.JavascriptClosureAngularClientCodegen
org.openapitools.codegen.languages.JMeterClientCodegen
diff --git a/pom.xml b/pom.xml
index 500d195b14e..4c8f3fcb339 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1239,6 +1239,7 @@
samples/server/petstore/php-slimsamples/server/petstore/php-slim4samples/client/petstore/javascript
+ samples/client/petstore/javascript-apollosamples/client/petstore/javascript-es6samples/openapi3/client/petstore/javascript-es6samples/client/petstore/javascript-promise
diff --git a/website/i18n/en.json b/website/i18n/en.json
index 890f1a9d449..89b5ad427b6 100644
--- a/website/i18n/en.json
+++ b/website/i18n/en.json
@@ -271,6 +271,10 @@
"title": "Config Options for javascript",
"sidebar_label": "javascript"
},
+ "generators/javascript-apollo": {
+ "title": "Config Options for javascript-apollo",
+ "sidebar_label": "javascript-apollo"
+ },
"generators/jaxrs-cxf-cdi": {
"title": "Config Options for jaxrs-cxf-cdi",
"sidebar_label": "jaxrs-cxf-cdi"