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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type {{classname}} struct {
{{#isDeprecated}}
// Deprecated
{{/isDeprecated}}
func (api *{{classname}}) {{nickname}}(c *gin.Context) {
func (api *{{classname}}) {{operationId}}(c *gin.Context) {
// Your handler implementation
c.JSON(200, gin.H{"status": "OK"})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ type {{classname}} interface {


{{#operation}}
// {{nickname}} {{httpMethod}} {{{basePathWithoutHost}}}{{{path}}}{{#summary}}
// {{operationId}} {{httpMethod}} {{{basePathWithoutHost}}}{{{path}}}{{#summary}}
// {{{.}}} {{/summary}}
{{#isDeprecated}}
// Deprecated
{{/isDeprecated}}
{{nickname}}(c *gin.Context)
{{operationId}}(c *gin.Context)

{{/operation}}
{{/operations}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ func (c *{{classname}}Controller) OrderedRoutes() []Route {

{{#operations}}{{#operation}}

// {{nickname}} - {{{summary}}}
// {{operationId}} - {{{summary}}}
{{#isDeprecated}}
// Deprecated
{{/isDeprecated}}
func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Request) {
func (c *{{classname}}Controller) {{operationId}}(w http.ResponseWriter, r *http.Request) {
{{#hasFormParams}}
{{#isMultipart}}
if err := r.ParseMultipartForm(32 << 20); err != nil {
Expand Down Expand Up @@ -641,7 +641,7 @@ func (c *{{classname}}Controller) {{nickname}}(w http.ResponseWriter, r *http.Re
{{/isArray}}
{{/isBodyParam}}
{{/allParams}}
result, err := c.service.{{nickname}}(r.Context(){{#allParams}}, {{#isNullable}}{{#isBodyParam}}&{{/isBodyParam}}{{/isNullable}}{{paramName}}Param{{/allParams}})
result, err := c.service.{{operationId}}(r.Context(){{#allParams}}, {{#isNullable}}{{#isBodyParam}}&{{/isBodyParam}}{{/isNullable}}{{paramName}}Param{{/allParams}})
// If an error occurred, encode the error with the status code
if err != nil {
c.errorHandler(w, r, err, &result)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ func New{{classname}}Service() *{{classname}}Service {
return &{{classname}}Service{}
}{{#operations}}{{#operation}}

// {{nickname}} - {{summary}}
// {{operationId}} - {{summary}}
{{#isDeprecated}}
// Deprecated
{{/isDeprecated}}
func (s *{{classname}}Service) {{nickname}}(ctx context.Context{{#allParams}}, {{paramName}} {{#isNullable}}*{{/isNullable}}{{dataType}}{{/allParams}}) (ImplResponse, error) {
// TODO - update {{nickname}} with the required logic for this service method.
func (s *{{classname}}Service) {{operationId}}(ctx context.Context{{#allParams}}, {{paramName}} {{#isNullable}}*{{/isNullable}}{{dataType}}{{/allParams}}) (ImplResponse, error) {
// TODO - update {{operationId}} with the required logic for this service method.
// Add {{classFilename}}_service.go to the .openapi-generator-ignore to avoid overwriting this service implementation when updating open api generation.

{{#responses}}
Expand All @@ -39,5 +39,5 @@ func (s *{{classname}}Service) {{nickname}}(ctx context.Context{{#allParams}}, {

{{/dataType}}
{{/responses}}
return Response(http.StatusNotImplemented, nil), errors.New("{{nickname}} method not implemented")
return Response(http.StatusNotImplemented, nil), errors.New("{{operationId}} method not implemented")
}{{/operation}}{{/operations}}
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.TestUtils;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.testng.Assert;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
Expand Down Expand Up @@ -85,6 +87,42 @@ public void verifyInterfaceOnly() throws IOException {
"type PetAPI interface");
}

@Test
public void verifyDuplicateOperationIdsAreConsistent() throws IOException {
// Regression test for duplicate auto-generated operationIds (e.g. /foo and /foo/).
// go-gin-server's routers.mustache references handleFunctions.<Class>.{{operationId}}
// while interface-api.mustache/controller-api.mustache used to declare the method
// with {{nickname}}. nickname and operationId were deduplicated by two independent
// passes with inconsistent counter conventions (_1 vs _0), so routers referenced
// FooGet_0 while the handler/interface declared FooGet_1, producing uncompilable
// code. The fix makes those templates use {{operationId}} consistently.
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = createDefaultCodegenConfigurator(output)
.setInputSpec("src/test/resources/3_0/go-gin-server/duplicate-operation-ids.yaml");

DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

// routers.go uses {{operationId}} to reference the handler method on the generated struct.
TestUtils.assertFileContains(Paths.get(output + "/go/routers.go"),
"handleFunctions.DefaultAPI.FooGet_0");
// controller-api.mustache now uses {{operationId}} for the handler method, matching
// the {{operationId}} reference in routers.go.
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"func (api *DefaultAPI) FooGet_0(");

// Negative assertion: the divergent _1 name must not appear anywhere.
String routers = new String(Files.readAllBytes(Paths.get(output + "/go/routers.go")), StandardCharsets.UTF_8);
String apiDefault = new String(Files.readAllBytes(Paths.get(output + "/go/api_default.go")), StandardCharsets.UTF_8);
Assert.assertFalse(routers.contains("FooGet_1"),
"nickname diverged from operationId (FooGet_1 present in routers.go)");
Assert.assertFalse(apiDefault.contains("FooGet_1"),
"nickname diverged from operationId (FooGet_1 present in api_default.go)");
}

private static CodegenConfigurator createDefaultCodegenConfigurator(File output) {
return new CodegenConfigurator()
.setGeneratorName("go-gin-server")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
Expand Down Expand Up @@ -74,6 +75,58 @@ public void verifyOrder() throws IOException {

}

@Test
public void verifyDuplicateOperationIdsAreConsistent() throws IOException {
// Regression test for duplicate auto-generated operationIds (e.g. /foo and /foo/).
// Previously nickname and operationId were deduplicated by two independent passes with
// inconsistent counter conventions (_1 vs _0), so the route map / interfaces used
// {{operationId}} (e.g. FooGet_0) while the controller handler / service stub used
// {{nickname}} (e.g. FooGet_1), producing uncompilable code.
File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final CodegenConfigurator configurator = createDefaultCodegenConfigurator(output)
.setInputSpec("src/test/resources/3_0/go-server/duplicate-operation-ids.yaml");

DefaultGenerator generator = new DefaultGenerator();
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
files.forEach(File::deleteOnExit);

// The deduplicated operationId pair is FooGet and FooGet_0; routes, handlers and
// service stubs must all reference the same name (templates use {{operationId}}).
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"\"FooGet\": Route{");
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"\"FooGet_0\": Route{");

// Route handler reference (uses {{operationId}}) and handler method definition
// (uses {{nickname}}) must reference the same name.
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"c.FooGet_0,");
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"func (c *DefaultAPIController) FooGet_0(");
// Service call inside the handler must also use the deduplicated operationId.
TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"),
"c.service.FooGet_0(");

// The interface (api.go) and the service stub must declare FooGet_0 too.
TestUtils.assertFileContains(Paths.get(output + "/go/api.go"),
"FooGet_0(");
TestUtils.assertFileContains(Paths.get(output + "/go/api_default_service.go"),
"func (s *DefaultAPIService) FooGet_0(");

// Negative assertions: the divergent _1 name must not appear anywhere.
String apiDefault = new String(Files.readAllBytes(Paths.get(output + "/go/api_default.go")), StandardCharsets.UTF_8);
String api = new String(Files.readAllBytes(Paths.get(output + "/go/api.go")), StandardCharsets.UTF_8);
String apiService = new String(Files.readAllBytes(Paths.get(output + "/go/api_default_service.go")), StandardCharsets.UTF_8);
Assert.assertFalse(apiDefault.contains("FooGet_1"),
"nickname diverged from operationId (FooGet_1 present in api_default.go)");
Assert.assertFalse(api.contains("FooGet_1"),
"nickname diverged from operationId (FooGet_1 present in api.go)");
Assert.assertFalse(apiService.contains("FooGet_1"),
"nickname diverged from operationId (FooGet_1 present in api_default_service.go)");
}

private static CodegenConfigurator createDefaultCodegenConfigurator(File output) {
return new CodegenConfigurator()
.setGeneratorName("go-server")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
openapi: 3.0.1

info:
version: 1.0.0
title: Duplicate auto-generated operationIds

paths:
/foo:
get:
tags:
- default
responses:
'200':
description: ok
/foo/:
get:
tags:
- default
responses:
'200':
description: ok
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
openapi: 3.0.1

info:
version: 1.0.0
title: Duplicate auto-generated operationIds

paths:
/foo:
get:
tags:
- default
responses:
'200':
description: ok
/foo/:
get:
tags:
- default
responses:
'200':
description: ok
Loading