Skip to content

Commit

Permalink
[Go]: Interface definitions for api functions (#5914)
Browse files Browse the repository at this point in the history
Introduces a new "generateInterfaces" option, allowing for better testability of generated clients
  • Loading branch information
arvindth authored Sep 1, 2020
1 parent 9fd66fb commit ab5b0fa
Show file tree
Hide file tree
Showing 50 changed files with 3,968 additions and 1,691 deletions.
1 change: 1 addition & 0 deletions bin/configs/go-experimental-go-petstore-oas2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ inputSpec: modules/openapi-generator/src/test/resources/2_0/petstore-with-fake-e
templateDir: modules/openapi-generator/src/main/resources/go-experimental
additionalProperties:
packageName: petstore
generateInterfaces: true
1 change: 1 addition & 0 deletions bin/configs/go-experimental-go-petstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ additionalProperties:
enumClassPrefix: "true"
packageName: petstore
disallowAdditionalPropertiesIfNotPresent: false
generateInterfaces: true
1 change: 1 addition & 0 deletions bin/configs/go-petstore-withXml.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ additionalProperties:
packageName: petstore
withXml: "true"
withGoCodegenComment: "true"
generateInterfaces: true
1 change: 1 addition & 0 deletions docs/generators/go-experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ sidebar_label: go-experimental
| ------ | ----------- | ------ | ------- |
|disallowAdditionalPropertiesIfNotPresent|Specify the behavior when the 'additionalProperties' keyword is not present in the OAS document. If false: the 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications. If true: when the 'additionalProperties' keyword is not present in a schema, the value of 'additionalProperties' is set to false, i.e. no additional properties are allowed. Note: this mode is not compliant with the JSON schema specification. This is the original openapi-generator behavior.This setting is currently ignored for OAS 2.0 documents: 1) When the 'additionalProperties' keyword is not present in a 2.0 schema, additional properties are NOT allowed. 2) Boolean values of the 'additionalProperties' keyword are ignored. It's as if additional properties are NOT allowed.Note: the root cause are issues #1369 and #1371, which must be resolved in the swagger-parser project.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>when the 'additionalProperties' keyword is not present in a schema, the value of 'additionalProperties' is automatically set to false, i.e. no additional properties are allowed. Note: this mode is not compliant with the JSON schema specification. This is the original openapi-generator behavior.</dd></dl>|true|
|enumClassPrefix|Prefix enum with class name| |false|
|generateInterfaces|Generate interfaces for api classes| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|isGoSubmodule|whether the generated Go module is a submodule| |false|
|packageName|Go package name (convention: lowercase).| |openapi|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/go.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ sidebar_label: go
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|enumClassPrefix|Prefix enum with class name| |false|
|generateInterfaces|Generate interfaces for api classes| |false|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
|isGoSubmodule|whether the generated Go module is a submodule| |false|
|packageName|Go package name (convention: lowercase).| |openapi|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
protected boolean withXml = false;
protected boolean enumClassPrefix = false;
protected boolean structPrefix = false;
protected boolean generateInterfaces = false;

protected String packageName = "openapi";
protected Set<String> numberTypes;
Expand Down Expand Up @@ -780,6 +781,10 @@ public void setStructPrefix(boolean structPrefix) {
this.structPrefix = structPrefix;
}

public void setGenerateInterfaces(boolean generateInterfaces) {
this.generateInterfaces = generateInterfaces;
}

@Override
public String toDefaultValue(Schema schema) {
if (schema.getDefault() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class GoClientCodegen extends AbstractGoCodegen {
public static final String WITH_XML = "withXml";
public static final String STRUCT_PREFIX = "structPrefix";
public static final String WITH_AWSV4_SIGNATURE = "withAWSV4Signature";
public static final String GENERATE_INTERFACES = "generateInterfaces";

public GoClientCodegen() {
super();
Expand Down Expand Up @@ -91,6 +92,7 @@ public GoClientCodegen() {
cliOptions.add(CliOption.newBoolean(CodegenConstants.ENUM_CLASS_PREFIX, CodegenConstants.ENUM_CLASS_PREFIX_DESC));
cliOptions.add(CliOption.newBoolean(STRUCT_PREFIX, "whether to prefix struct with the class name. e.g. DeletePetOpts => PetApiDeletePetOpts"));
cliOptions.add(CliOption.newBoolean(WITH_AWSV4_SIGNATURE, "whether to include AWS v4 signature support"));
cliOptions.add(CliOption.newBoolean(GENERATE_INTERFACES, "Generate interfaces for api classes"));

// option to change the order of form/body parameter
cliOptions.add(CliOption.newBoolean(
Expand Down Expand Up @@ -164,6 +166,11 @@ public void processOpts() {
setStructPrefix(Boolean.parseBoolean(additionalProperties.get(STRUCT_PREFIX).toString()));
additionalProperties.put(STRUCT_PREFIX, structPrefix);
}

if (additionalProperties.containsKey(GENERATE_INTERFACES)) {
setGenerateInterfaces(Boolean.parseBoolean(additionalProperties.get(GENERATE_INTERFACES).toString()));
additionalProperties.put(GENERATE_INTERFACES, generateInterfaces);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,77 @@ import (
var (
_ _context.Context
)
{{#generateInterfaces}}

type {{classname}} interface {
{{#operation}}

/*
* {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
{{#notes}}
* {{{unescapedNotes}}}
{{/notes}}
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().{{#pathParams}}
* @param {{paramName}}{{#description}} {{{.}}}{{/description}}{{/pathParams}}
* @return Api{{operationId}}Request
*/
{{{nickname}}}(ctx _context.Context{{#pathParams}}, {{paramName}} {{{dataType}}}{{/pathParams}}) Api{{operationId}}Request

/*
* {{nickname}}Execute executes the request{{#returnType}}
* @return {{{.}}}{{/returnType}}
*/
{{nickname}}Execute(r Api{{operationId}}Request) ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error)
{{/operation}}
}
{{/generateInterfaces}}

// {{classname}}Service {{classname}} service
type {{classname}}Service service

{{#operation}}
type api{{operationId}}Request struct {
ctx _context.Context
apiService *{{classname}}Service{{#allParams}}
{{paramName}} {{^isPathParam}}*{{/isPathParam}}{{{dataType}}}{{/allParams}}
}

type Api{{operationId}}Request struct {
ctx _context.Context{{#generateInterfaces}}
ApiService {{classname}}
{{/generateInterfaces}}{{^generateInterfaces}}
ApiService *{{classname}}Service
{{/generateInterfaces}}
{{#allParams}}
{{^isPathParam}}
func (r api{{operationId}}Request) {{vendorExtensions.x-export-param-name}}({{paramName}} {{{dataType}}}) api{{operationId}}Request {
{{paramName}} {{^isPathParam}}*{{/isPathParam}}{{{dataType}}}
{{/allParams}}
}
{{#allParams}}{{^isPathParam}}
func (r Api{{operationId}}Request) {{vendorExtensions.x-export-param-name}}({{paramName}} {{{dataType}}}) Api{{operationId}}Request {
r.{{paramName}} = &{{paramName}}
return r
}{{/isPathParam}}{{/allParams}}

func (r Api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) {
return r.ApiService.{{nickname}}Execute(r)
}
{{/isPathParam}}
{{/allParams}}

/*
{{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
* {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
{{#notes}}
{{{unescapedNotes}}}
* {{{unescapedNotes}}}
{{/notes}}
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().{{#pathParams}}
* @param {{paramName}}{{#description}} {{{.}}}{{/description}}{{/pathParams}}
@return api{{operationId}}Request
*/
func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#pathParams}}, {{paramName}} {{{dataType}}}{{/pathParams}}) api{{operationId}}Request {
return api{{operationId}}Request{
apiService: a,
* @return Api{{operationId}}Request
*/
func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#pathParams}}, {{paramName}} {{{dataType}}}{{/pathParams}}) Api{{operationId}}Request {
return Api{{operationId}}Request{
ApiService: a,
ctx: ctx,{{#pathParams}}
{{paramName}}: {{paramName}},{{/pathParams}}
}
}

/*
Execute executes the request
{{#returnType}}
@return {{{.}}}
{{/returnType}}
*/
func (r api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) {
* Execute executes the request{{#returnType}}
* @return {{{.}}}{{/returnType}}
*/
func (a *{{{classname}}}Service) {{nickname}}Execute(r Api{{operationId}}Request) ({{#returnType}}{{{.}}}, {{/returnType}}*_nethttp.Response, error) {
var (
localVarHTTPMethod = _nethttp.Method{{httpMethod}}
localVarPostBody interface{}
Expand All @@ -69,7 +97,7 @@ func (r api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnT
{{/returnType}}
)

localBasePath, err := r.apiService.client.cfg.ServerURLWithContext(r.ctx, "{{{classname}}}Service.{{{nickname}}}")
localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "{{{classname}}}Service.{{{nickname}}}")
if err != nil {
return {{#returnType}}localVarReturnValue, {{/returnType}}nil, GenericOpenAPIError{error: err.Error()}
}
Expand Down Expand Up @@ -282,12 +310,12 @@ func (r api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnT
{{/isKeyInCookie}}
{{/isApiKey}}
{{/authMethods}}
req, err := r.apiService.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFormFileName, localVarFileName, localVarFileBytes)
if err != nil {
return {{#returnType}}localVarReturnValue, {{/returnType}}nil, err
}

localVarHTTPResponse, err := r.apiService.client.callAPI(req)
localVarHTTPResponse, err := a.client.callAPI(req)
if err != nil || localVarHTTPResponse == nil {
return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, err
}
Expand All @@ -311,7 +339,7 @@ func (r api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnT
if localVarHTTPResponse.StatusCode == {{{code}}} {
{{/wildcard}}
var v {{{dataType}}}
err = r.apiService.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr.error = err.Error()
return {{#returnType}}localVarReturnValue, {{/returnType}}localVarHTTPResponse, newErr
Expand All @@ -331,7 +359,7 @@ func (r api{{operationId}}Request) Execute() ({{#returnType}}{{{.}}}, {{/returnT
}

{{#returnType}}
err = r.apiService.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type"))
if err != nil {
newErr := GenericOpenAPIError{
body: localVarBody,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ type APIClient struct {
{{#apis}}
{{#operations}}

{{#generateInterfaces}}
{{classname}} {{classname}}
{{/generateInterfaces}}
{{^generateInterfaces}}
{{classname}} *{{classname}}Service
{{/generateInterfaces}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
Expand Down
44 changes: 40 additions & 4 deletions modules/openapi-generator/src/main/resources/go/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,40 @@ var (
_ _context.Context
)

{{#generateInterfaces}}
type {{classname}} interface {
{{#operation}}

/*
* {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
*
{{#notes}}
* {{notes}}
*
{{/notes}}
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
{{#allParams}}
{{#required}}
* @param {{paramName}}{{#description}} {{{.}}}{{/description}}
{{/required}}
{{/allParams}}
{{#hasOptionalParams}}
* @param optional nil or *{{#structPrefix}}{{&classname}}{{/structPrefix}}{{{nickname}}}Opts - Optional Parameters:
{{#allParams}}
{{^required}}
* @param "{{vendorExtensions.x-export-param-name}}" ({{#isPrimitiveType}}{{^isBinary}}optional.{{vendorExtensions.x-optional-data-type}}{{/isBinary}}{{#isBinary}}optional.Interface of {{dataType}}{{/isBinary}}{{/isPrimitiveType}}{{^isPrimitiveType}}optional.Interface of {{dataType}}{{/isPrimitiveType}}) - {{#description}} {{{.}}}{{/description}}
{{/required}}
{{/allParams}}
{{/hasOptionalParams}}
{{#returnType}}
* @return {{{returnType}}}
{{/returnType}}
*/
{{{nickname}}}(ctx _context.Context{{#hasParams}}, {{/hasParams}}{{#allParams}}{{#required}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}localVarOptionals *{{#structPrefix}}{{&classname}}{{/structPrefix}}{{{nickname}}}Opts{{/hasOptionalParams}}) ({{#returnType}}{{{returnType}}}, {{/returnType}}*_nethttp.Response, error)
{{/operation}}
}

{{/generateInterfaces}}
// {{classname}}Service {{classname}} service
type {{classname}}Service service
{{#operation}}
Expand All @@ -43,9 +77,11 @@ type {{#structPrefix}}{{&classname}}{{/structPrefix}}{{{nickname}}}Opts struct {

{{/hasOptionalParams}}
/*
{{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
* {{operationId}}{{#summary}} {{{.}}}{{/summary}}{{^summary}} Method for {{operationId}}{{/summary}}
*
{{#notes}}
{{notes}}
* {{notes}}
*
{{/notes}}
* @param ctx _context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
{{#allParams}}
Expand All @@ -62,9 +98,9 @@ type {{#structPrefix}}{{&classname}}{{/structPrefix}}{{{nickname}}}Opts struct {
{{/allParams}}
{{/hasOptionalParams}}
{{#returnType}}
@return {{{returnType}}}
* @return {{{returnType}}}
{{/returnType}}
*/
*/
func (a *{{{classname}}}Service) {{{nickname}}}(ctx _context.Context{{#hasParams}}, {{/hasParams}}{{#allParams}}{{#required}}{{paramName}} {{{dataType}}}{{#hasMore}}, {{/hasMore}}{{/required}}{{/allParams}}{{#hasOptionalParams}}localVarOptionals *{{#structPrefix}}{{&classname}}{{/structPrefix}}{{{nickname}}}Opts{{/hasOptionalParams}}) ({{#returnType}}{{{returnType}}}, {{/returnType}}*_nethttp.Response, error) {
var (
localVarHTTPMethod = _nethttp.Method{{httpMethod}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ type APIClient struct {
{{#apis}}
{{#operations}}

{{#generateInterfaces}}
{{classname}} {{classname}}
{{/generateInterfaces}}
{{^generateInterfaces}}
{{classname}} *{{classname}}Service
{{/generateInterfaces}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ protected void verifyOptions() {
verify(clientCodegen).setPackageName(GoClientOptionsProvider.PACKAGE_NAME_VALUE);
verify(clientCodegen).setWithGoCodegenComment(GoClientOptionsProvider.WITH_GO_CODEGEN_COMMENT_VALUE);
verify(clientCodegen).setWithXml(GoClientOptionsProvider.WITH_XML_VALUE);
verify(clientCodegen).setWithXml(GoClientOptionsProvider.ENUM_CLASS_PREFIX_VALUE);
verify(clientCodegen).setEnumClassPrefix(GoClientOptionsProvider.ENUM_CLASS_PREFIX_VALUE);
verify(clientCodegen).setPrependFormOrBodyParameters(GoClientOptionsProvider.PREPEND_FORM_OR_BODY_PARAMETERS_VALUE);
verify(clientCodegen).setIsGoSubmodule(GoClientOptionsProvider.IS_GO_SUBMODULE_VALUE);
verify(clientCodegen).setStructPrefix(GoClientOptionsProvider.STRUCT_PREFIX_VALUE);
verify(clientCodegen).setWithAWSV4Signature(GoClientOptionsProvider.WITH_AWSV4_SIGNATURE);
verify(clientCodegen).setGenerateInterfaces(GoClientOptionsProvider.GENERATE_INTERFACES_VALUE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class GoClientOptionsProvider implements OptionsProvider {
public static final boolean IS_GO_SUBMODULE_VALUE = true;
public static final boolean STRUCT_PREFIX_VALUE = true;
public static final boolean WITH_AWSV4_SIGNATURE = true;
public static final boolean GENERATE_INTERFACES_VALUE = true;

@Override
public String getLanguage() {
Expand All @@ -52,6 +53,7 @@ public Map<String, String> createOptions() {
.put(CodegenConstants.PREPEND_FORM_OR_BODY_PARAMETERS, "true")
.put(CodegenConstants.IS_GO_SUBMODULE, "true")
.put(CodegenConstants.WITH_AWSV4_SIGNATURE_COMMENT, "true")
.put("generateInterfaces", "true")
.put("structPrefix", "true")
.build();
}
Expand Down
Loading

0 comments on commit ab5b0fa

Please sign in to comment.