Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
## Overview
This is the swagger codegen project, which allows generation of API client libraries (SDK generation), server stubs and documentation automatically given an [OpenAPI Spec](https://github.com/OAI/OpenAPI-Specification). Currently, the following languages/frameworks are supported:

- **API clients**: **ActionScript**, **Ada**, **Apex**, **Bash**, **C#** (.net 2.0, 4.0 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Eiffel**, **Go**, **Groovy**, **Haskell** (http-client, Servant), **Java** (Jersey1.x, Jersey2.x, OkHttp, Retrofit1.x, Retrofit2.x, Feign, RestTemplate, RESTEasy, Vertx), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust**, **Scala** (akka, http4s, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node)
- **API clients**: **ActionScript**, **Ada**, **Apex**, **Bash**, **C#** (.net 2.0, 4.0 or later), **C++** (cpprest, Qt5, Tizen), **Clojure**, **Dart**, **Elixir**, **Eiffel**, **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), **Kotlin**, **Lua**, **Node.js** (ES5, ES6, AngularJS with Google Closure Compiler annotations) **Objective-C**, **Perl**, **PHP**, **PowerShell**, **Python**, **R**, **Ruby**, **Rust**, **Scala** (akka, http4s, swagger-async-httpclient), **Swift** (2.x, 3.x, 4.x), **Typescript** (Angular1.x, Angular2.x, Fetch, jQuery, Node)
- **Server stubs**: **C#** (ASP.NET Core, NancyFx), **C++** (Pistache, Restbed), **Erlang**, **Go**, **Haskell** (Servant), **Java** (MSF4J, Spring, Undertow, JAX-RS: CDI, CXF, Inflector, RestEasy, Play Framework), **PHP** (Lumen, Slim, Silex, [Symfony](https://symfony.com/), [Zend Expressive](https://github.com/zendframework/zend-expressive)), **Python** (Flask), **NodeJS**, **Ruby** (Sinatra, Rails5), **Rust**, **Scala** ([Finch](https://github.com/finagle/finch), Scalatra)
- **API documentation generators**: **HTML**, **Confluence Wiki**
- **Configuration files**: [**Apache2**](https://httpd.apache.org/)
Expand Down Expand Up @@ -516,6 +516,7 @@ CONFIG OPTIONS
okhttp-gson (default) - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1
retrofit - HTTP client: OkHttp 2.4.0. JSON processing: Gson 2.3.1 (Retrofit 1.9.0)
retrofit2 - HTTP client: OkHttp 2.5.0. JSON processing: Gson 2.4 (Retrofit 2.0.0-beta2)
google-api-client - HTTP client: google-api-client 1.23.0. JSON processing: Jackson 2.8.9
```

Your config file for Java can look like
Expand Down Expand Up @@ -982,6 +983,7 @@ Here is a list of template creators:
* Java (RestTemplate): @nbruno
* Java (RESTEasy): @gayathrigs
* Java (Vertx): @lopesmcc
* Java (Google APIs Client Library): @charlescapps
* Javascript/NodeJS: @jfiala
* Javascript (Closure-annotated Angular) @achew22
* JMeter: @davidkiss
Expand Down
1 change: 1 addition & 0 deletions bin/java-petstore-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
./bin/java-petstore-resttemplate.sh
./bin/java-petstore-resttemplate-withxml.sh
./bin/java-petstore-resteasy.sh
./bin/java-petstore-google-api-client.sh
4 changes: 4 additions & 0 deletions bin/java-petstore-google-api-client.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"library": "google-api-client",
"artifactId": "swagger-petstore-google-api-client"
}
34 changes: 34 additions & 0 deletions bin/java-petstore-google-api-client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/sh

SCRIPT="$0"

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/swagger-codegen-cli/target/swagger-codegen-cli.jar"

if [ ! -f "$executable" ]
then
mvn clean package
fi

# if you've executed sbt assembly previously it will use that instead.
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
ags="$@ generate -t modules/swagger-codegen/src/main/resources/Java/libraries/google-api-client -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l java -c bin/java-petstore-google-api-client.json -o samples/client/petstore/java/google-api-client -DhideGenerationTimestamp=true"

echo "Removing files and folders under samples/client/petstore/java/google-api-client/src/main"
rm -rf samples/client/petstore/java/google-api-client/src/main
find samples/client/petstore/java/google-api-client -maxdepth 1 -type f ! -name "README.md" -exec rm {} +
java $JAVA_OPTS -jar $executable $ags
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

public class CodegenOperation {
public final List<CodegenProperty> responseHeaders = new ArrayList<CodegenProperty>();
public boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams,
public boolean hasAuthMethods, hasConsumes, hasProduces, hasParams, hasOptionalParams, hasRequiredParams,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised this is needed. Most generator use the params array and loop with "isRequired" to build almost everything. Is there any specific reason to add this to the "generic code"?

Copy link
Copy Markdown
Contributor Author

@charlescapps charlescapps Oct 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you can check {{isRequired}} but that's not sufficient, because we use {{hasNext}} to see if there's a subsequent element, and that isn't aware of isRequired, so it was generating code with 2 commas in a row.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok makes sense!

returnTypeIsPrimitive, returnSimpleType, subresourceOperation, isMapContainer,
isListContainer, isMultipart, hasMore = true,
isResponseBinary = false, isResponseFile = false, hasReference = false,
Expand All @@ -28,6 +28,7 @@ public class CodegenOperation {
public List<CodegenParameter> queryParams = new ArrayList<CodegenParameter>();
public List<CodegenParameter> headerParams = new ArrayList<CodegenParameter>();
public List<CodegenParameter> formParams = new ArrayList<CodegenParameter>();
public List<CodegenParameter> requiredParams = new ArrayList<CodegenParameter>();
public List<CodegenSecurity> authMethods;
public List<Tag> tags;
public List<CodegenResponse> responses = new ArrayList<CodegenResponse>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2229,6 +2229,7 @@ public CodegenOperation fromOperation(String path,
List<CodegenParameter> headerParams = new ArrayList<CodegenParameter>();
List<CodegenParameter> cookieParams = new ArrayList<CodegenParameter>();
List<CodegenParameter> formParams = new ArrayList<CodegenParameter>();
List<CodegenParameter> requiredParams = new ArrayList<CodegenParameter>();

if (parameters != null) {
for (Parameter param : parameters) {
Expand Down Expand Up @@ -2259,6 +2260,9 @@ public CodegenOperation fromOperation(String path,


allParams.add(p);
if (p.required || p.isBodyParam) {
requiredParams.add(p.copy());
}
// Issue #2561 (neilotoole) : Moved setting of is<Type>Param flags
// from here to fromParameter().
if (param instanceof QueryParameter) {
Expand Down Expand Up @@ -2312,13 +2316,15 @@ public int compare(CodegenParameter one, CodegenParameter another) {
op.headerParams = addHasMore(headerParams);
// op.cookieParams = cookieParams;
op.formParams = addHasMore(formParams);
op.requiredParams = addHasMore(requiredParams);
op.externalDocs = operation.getExternalDocs();
// legacy support
op.nickname = op.operationId;

if (op.allParams.size() > 0) {
op.hasParams = true;
}
op.hasRequiredParams = op.requiredParams.size() > 0;

// set Restful Flag
op.isRestfulShow = op.isRestfulShow();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public JavaClientCodegen() {
supportedLibraries.put("resttemplate", "HTTP client: Spring RestTemplate 4.3.9-RELEASE. JSON processing: Jackson 2.8.9");
supportedLibraries.put("resteasy", "HTTP client: Resteasy client 3.1.3.Final. JSON processing: Jackson 2.8.9");
supportedLibraries.put("vertx", "HTTP client: VertX client 3.2.4. JSON processing: Jackson 2.8.9");
supportedLibraries.put("google-api-client", "HTTP client: Google API client 1.23.0. JSON processing: Jackson 2.8.9");

CliOption libraryOption = new CliOption(CodegenConstants.LIBRARY, "library template (sub-template) to use");
libraryOption.setEnum(supportedLibraries);
Expand Down Expand Up @@ -172,10 +173,13 @@ public void processOpts() {
supportingFiles.add(new SupportingFile("StringUtil.mustache", invokerFolder, "StringUtil.java"));
}

supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.mustache", authFolder, "HttpBasicAuth.java"));
supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java"));
supportingFiles.add(new SupportingFile("auth/OAuth.mustache", authFolder, "OAuth.java"));
supportingFiles.add(new SupportingFile("auth/OAuthFlow.mustache", authFolder, "OAuthFlow.java"));
// google-api-client doesn't use the Swagger auth, because it uses Google Credential directly (HttpRequestInitializer)
if (!"google-api-client".equals(getLibrary())) {
Copy link
Copy Markdown
Contributor

@JFCote JFCote Oct 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go in the GoogleApiClientCodegen.java since it's only useful for your generator.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll do that right now, sounds like a plan.

Copy link
Copy Markdown
Contributor Author

@charlescapps charlescapps Oct 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is following the existing pattern. There actually aren't any subclasses of JavaClientCodegen; it uses if-statements on the library throughout to determine the configuration of each library. I'd have to figure out how to support a new subclass, it looks like it uses reflection to instantiate the JavaClientCodegen with the classes listed in modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig

supportingFiles.add(new SupportingFile("auth/HttpBasicAuth.mustache", authFolder, "HttpBasicAuth.java"));
supportingFiles.add(new SupportingFile("auth/ApiKeyAuth.mustache", authFolder, "ApiKeyAuth.java"));
supportingFiles.add(new SupportingFile("auth/OAuth.mustache", authFolder, "OAuth.java"));
supportingFiles.add(new SupportingFile("auth/OAuthFlow.mustache", authFolder, "OAuthFlow.java"));
}
supportingFiles.add(new SupportingFile( "gradlew.mustache", "", "gradlew") );
supportingFiles.add(new SupportingFile( "gradlew.bat.mustache", "", "gradlew.bat") );
supportingFiles.add(new SupportingFile( "gradle-wrapper.properties.mustache",
Expand All @@ -196,7 +200,7 @@ public void processOpts() {
apiDocTemplateFiles.remove("api_doc.mustache");
}

if (!("feign".equals(getLibrary()) || "resttemplate".equals(getLibrary()) || usesAnyRetrofitLibrary())) {
if (!("feign".equals(getLibrary()) || "resttemplate".equals(getLibrary()) || usesAnyRetrofitLibrary() || "google-api-client".equals(getLibrary()))) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see other library has done it too... @wing328, is this normal? Since we are using objects, we should'nt have any "if" in the JavaClientCodegen... It should all be in specific file of each generator...
Or there is a good reason for this ??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it appears there are no subclasses of JavaClientCodegen, see above comment, @wing328 probably has a more complete explanation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. It's weird because for example, for the server generator "java play framework" which I maintain, there is:
JavaPlayFrameworkCodegen -> AbstractJavaCodegen -> DefaultCodegen.
This prevent having "if" everywhere.

Same thing for Typescript jQuery which I maintain too:
TypeScriptJqueryClientCodegen -> AbstractTypeScriptClientCodegen -> DefaultCodegen

I wonder why the JavaClientCodegen are all mixed in one file.
For the moment, don't change anything. We will see with @wing328 answers :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Yeah, a refactor into subclasses probably belongs in a separate pull request, but I'm game to do whatever you suggest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ok for now and belong in another pull request like you said. :)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we added support for other HTTP libraries, we didn't foresee we can support so many libraries (9 HTTP libraries support so far thanks to the awesome community). I agree a separate PR to refactor into subclasses would be ideal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Is this waiting on any additional reviews? Let me know if any more changes are needed here. Thanks! This will really help out our company in our migration to use Swagger clients for all of our service-to-service communication. (And possibly we could use this for our Android app, too).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charlescapps I'll take another look later today and merge accordingly.

supportingFiles.add(new SupportingFile("apiException.mustache", invokerFolder, "ApiException.java"));
supportingFiles.add(new SupportingFile("Configuration.mustache", invokerFolder, "Configuration.java"));
supportingFiles.add(new SupportingFile("Pair.mustache", invokerFolder, "Pair.java"));
Expand Down Expand Up @@ -240,6 +244,8 @@ public void processOpts() {
apiTemplateFiles.put("apiImpl.mustache", "Impl.java");
apiTemplateFiles.put("rxApiImpl.mustache", ".java");
supportingFiles.remove(new SupportingFile("manifest.mustache", projectFolder, "AndroidManifest.xml"));
} else if ("google-api-client".equals(getLibrary())) {
additionalProperties.put("jackson", "true");
} else {
LOGGER.error("Unknown library option (-l/--library): " + getLibrary());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package {{invokerPackage}};

import {{apiPackage}}.*;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
{{#joda}}
import com.fasterxml.jackson.datatype.joda.JodaModule;
{{/joda}}
{{#java8}}
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
{{/java8}}
{{#threetenbp}}
import com.fasterxml.jackson.datatype.threetenbp.ThreeTenModule;
{{/threetenbp}}
{{#threetenbp}}
import org.threeten.bp.*;
{{/threetenbp}}
import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.http.AbstractHttpContent;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.Json;

import java.io.IOException;
import java.io.OutputStream;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

{{>generatedAnnotation}}
public class ApiClient {
private final String basePath;
private final HttpRequestFactory httpRequestFactory;
private final ObjectMapper objectMapper;

private static final String defaultBasePath = "{{basePath}}";

// A reasonable default object mapper. Client can pass in a chosen ObjectMapper anyway, this is just for reasonable defaults.
private static ObjectMapper createDefaultObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new RFC3339DateFormat());
{{#joda}}
objectMapper.registerModule(new JodaModule());
{{/joda}}
{{#java8}}
objectMapper.registerModule(new JavaTimeModule());
{{/java8}}
{{#threetenbp}}
ThreeTenModule module = new ThreeTenModule();
module.addDeserializer(Instant.class, CustomInstantDeserializer.INSTANT);
module.addDeserializer(OffsetDateTime.class, CustomInstantDeserializer.OFFSET_DATE_TIME);
module.addDeserializer(ZonedDateTime.class, CustomInstantDeserializer.ZONED_DATE_TIME);
objectMapper.registerModule(module);
{{/threetenbp}}
return objectMapper;
}

public ApiClient() {
this(null, null, null, null);
}

public ApiClient(
String basePath,
HttpTransport httpTransport,
HttpRequestInitializer initializer,
ObjectMapper objectMapper
) {
this.basePath = basePath == null ? defaultBasePath : (
basePath.endsWith("/") ? basePath.substring(0, basePath.length() - 1) : basePath
);
this.httpRequestFactory = (httpTransport == null ? Utils.getDefaultTransport() : httpTransport).createRequestFactory(initializer);
this.objectMapper = (objectMapper == null ? createDefaultObjectMapper() : objectMapper);
}

public HttpRequestFactory getHttpRequestFactory() {
return httpRequestFactory;
}

public String getBasePath() {
return basePath;
}

public ObjectMapper getObjectMapper() {
return objectMapper;
}

public class JacksonJsonHttpContent extends AbstractHttpContent {
/* A POJO that can be serialized with a com.fasterxml Jackson ObjectMapper */
private final Object data;

public JacksonJsonHttpContent(Object data) {
super(Json.MEDIA_TYPE);
this.data = data;
}

@Override
public void writeTo(OutputStream out) throws IOException {
objectMapper.writeValue(out, data);
}
}

// Builder pattern to get API instances for this client.
{{#apiInfo}}{{#apis}}
public {{classname}} {{classVarName}}Api() {
return new {{classname}}(this);
}
{{/apis}}{{/apiInfo}}
}
Loading