From a34eeaed77a6afc6634372aec683de0b4a002456 Mon Sep 17 00:00:00 2001 From: Beppe Catanese <1771700+gcatanese@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:10:09 +0100 Subject: [PATCH] [GO Gin Server] Webhooks support: add missing webhook handlers (#17411) * Implement postProcessWebhooksWithModels * Implement postProcessWebhooksWithModels * Add missing webhook handlers * Test webhook handler * Generate samples --- .../codegen/languages/AbstractGoCodegen.java | 104 +++++++++++++++++- .../go-gin-server/controller-api.mustache | 16 ++- .../resources/go-gin-server/routers.mustache | 3 +- .../goginserver/GoGinServerCodegenTest.java | 4 +- .../petstore/go-gin-api-server/go/api_pet.go | 83 +++++++++----- .../go-gin-api-server/go/api_store.go | 41 +++++-- .../petstore/go-gin-api-server/go/api_user.go | 81 ++++++++++---- 7 files changed, 260 insertions(+), 72 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index 5ddd5a713f2..b88d98086f8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -22,10 +22,7 @@ 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.model.ModelMap; -import org.openapitools.codegen.model.ModelsMap; -import org.openapitools.codegen.model.OperationMap; -import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.model.*; import org.openapitools.codegen.utils.ModelUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -586,6 +583,105 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege return objs; } + @Override + public WebhooksMap postProcessWebhooksWithModels(WebhooksMap objs, List allModels) { + OperationMap objectMap = objs.getWebhooks(); + List operations = objectMap.getOperation(); + + for (CodegenOperation operation : operations) { + // http method verb conversion (e.g. PUT => Put) + operation.httpMethod = camelize(operation.httpMethod.toLowerCase(Locale.ROOT)); + } + + // remove model imports to avoid error + List> imports = objs.getImports(); + if (imports == null) + return objs; + + Iterator> iterator = imports.iterator(); + while (iterator.hasNext()) { + String _import = iterator.next().get("import"); + if (_import.startsWith(apiPackage())) + iterator.remove(); + } + + // this will only import "fmt" and "strings" if there are items in pathParams + for (CodegenOperation operation : operations) { + if (operation.pathParams != null && operation.pathParams.size() > 0) { + imports.add(createMapping("import", "strings")); + break; //just need to import once + } + } + + boolean addedTimeImport = false; + boolean addedOSImport = false; + boolean addedReflectImport = false; + for (CodegenOperation operation : operations) { + // import "os" if the operation uses files + if (!addedOSImport && "*os.File".equals(operation.returnType)) { + imports.add(createMapping("import", "os")); + addedOSImport = true; + } + for (CodegenParameter param : operation.allParams) { + // import "os" if the operation uses files + if (!addedOSImport && "*os.File".equals(param.dataType)) { + imports.add(createMapping("import", "os")); + addedOSImport = true; + } + + // import "time" if the operation has a time parameter. + if (!addedTimeImport && "time.Time".equals(param.dataType)) { + imports.add(createMapping("import", "time")); + addedTimeImport = true; + } + + // import "reflect" package if the parameter is collectionFormat=multi + if (!addedReflectImport && param.isCollectionFormatMulti) { + imports.add(createMapping("import", "reflect")); + addedReflectImport = true; + } + + // set x-exportParamName + char nameFirstChar = param.paramName.charAt(0); + if (Character.isUpperCase(nameFirstChar)) { + // First char is already uppercase, just use paramName. + param.vendorExtensions.put("x-export-param-name", param.paramName); + } else { + // It's a lowercase first char, let's convert it to uppercase + StringBuilder sb = new StringBuilder(param.paramName); + sb.setCharAt(0, Character.toUpperCase(nameFirstChar)); + param.vendorExtensions.put("x-export-param-name", sb.toString()); + } + } + + setExportParameterName(operation.queryParams); + setExportParameterName(operation.formParams); + setExportParameterName(operation.headerParams); + setExportParameterName(operation.bodyParams); + setExportParameterName(operation.cookieParams); + setExportParameterName(operation.optionalParams); + setExportParameterName(operation.requiredParams); + + } + + // recursively add import for mapping one type to multiple imports + List> recursiveImports = objs.getImports(); + if (recursiveImports == null) + return objs; + + ListIterator> listIterator = imports.listIterator(); + while (listIterator.hasNext()) { + String _import = listIterator.next().get("import"); + // if the import package happens to be found in the importMapping (key) + // add the corresponding import package to the list + if (importMapping.containsKey(_import)) { + listIterator.add(createMapping("import", importMapping.get(_import))); + } + } + + return objs; + } + private void setExportParameterName(List codegenParameters) { for (CodegenParameter param : codegenParameters) { char nameFirstChar = param.paramName.charAt(0); diff --git a/modules/openapi-generator/src/main/resources/go-gin-server/controller-api.mustache b/modules/openapi-generator/src/main/resources/go-gin-server/controller-api.mustache index 197da1c3312..e480e2394a8 100644 --- a/modules/openapi-generator/src/main/resources/go-gin-server/controller-api.mustache +++ b/modules/openapi-generator/src/main/resources/go-gin-server/controller-api.mustache @@ -7,12 +7,18 @@ import ( ) type {{classname}} struct { +} + {{#operation}} - // {{httpMethod}} {{{basePathWithoutHost}}}{{{path}}} - // {{{summary}}} +// {{httpMethod}} {{{basePathWithoutHost}}}{{{path}}}{{#summary}} +// {{{.}}} {{/summary}} {{#isDeprecated}} - // Deprecated +// Deprecated {{/isDeprecated}} - {{nickname}} gin.HandlerFunc +func (api *{{classname}}) {{nickname}}(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + {{/operation}} -}{{/operations}} +{{/operations}} diff --git a/modules/openapi-generator/src/main/resources/go-gin-server/routers.mustache b/modules/openapi-generator/src/main/resources/go-gin-server/routers.mustache index fbee487a76a..a2b758c69cc 100644 --- a/modules/openapi-generator/src/main/resources/go-gin-server/routers.mustache +++ b/modules/openapi-generator/src/main/resources/go-gin-server/routers.mustache @@ -51,7 +51,8 @@ func DefaultHandleFunc(c *gin.Context) { type ApiHandleFunctions struct { {{#apiInfo}}{{#apis}}{{#operations}} // Routes for the {{classname}} part of the API - {{classname}} {{classname}}{{/operations}}{{/apis}}{{/apiInfo}} + {{classname}} {{classname}}{{/operations}}{{/apis}}{{/apiInfo}}{{#webhooks}}{{#operations}} + {{classname}} {{classname}}{{/operations}}{{/webhooks}} } func getRoutes(handleFunctions ApiHandleFunctions) []Route { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/goginserver/GoGinServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/goginserver/GoGinServerCodegenTest.java index 084878a9155..4d70aa6591d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/goginserver/GoGinServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/goginserver/GoGinServerCodegenTest.java @@ -74,10 +74,12 @@ public class GoGinServerCodegenTest { DefaultGenerator generator = new DefaultGenerator(); List files = generator.opts(configurator.toClientOptInput()).generate(); - files.forEach(File::deleteOnExit); + //files.forEach(File::deleteOnExit); TestUtils.assertFileContains(Paths.get(output + "/go/routers.go"), "NewPetPost"); + TestUtils.assertFileContains(Paths.get(output + "/go/api_default.go"), + " c.JSON(200, gin.H{\"status\": \"OK\"})"); } } diff --git a/samples/server/petstore/go-gin-api-server/go/api_pet.go b/samples/server/petstore/go-gin-api-server/go/api_pet.go index f5f4ab5716b..323fc27cf72 100644 --- a/samples/server/petstore/go-gin-api-server/go/api_pet.go +++ b/samples/server/petstore/go-gin-api-server/go/api_pet.go @@ -14,29 +14,62 @@ import ( ) type PetAPI struct { - // Post /v2/pet - // Add a new pet to the store - AddPet gin.HandlerFunc - // Delete /v2/pet/:petId - // Deletes a pet - DeletePet gin.HandlerFunc - // Get /v2/pet/findByStatus - // Finds Pets by status - FindPetsByStatus gin.HandlerFunc - // Get /v2/pet/findByTags - // Finds Pets by tags - // Deprecated - FindPetsByTags gin.HandlerFunc - // Get /v2/pet/:petId - // Find pet by ID - GetPetById gin.HandlerFunc - // Put /v2/pet - // Update an existing pet - UpdatePet gin.HandlerFunc - // Post /v2/pet/:petId - // Updates a pet in the store with form data - UpdatePetWithForm gin.HandlerFunc - // Post /v2/pet/:petId/uploadImage - // uploads an image - UploadFile gin.HandlerFunc } + +// Post /v2/pet +// Add a new pet to the store +func (api *PetAPI) AddPet(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Delete /v2/pet/:petId +// Deletes a pet +func (api *PetAPI) DeletePet(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/pet/findByStatus +// Finds Pets by status +func (api *PetAPI) FindPetsByStatus(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/pet/findByTags +// Finds Pets by tags +// Deprecated +func (api *PetAPI) FindPetsByTags(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/pet/:petId +// Find pet by ID +func (api *PetAPI) GetPetById(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Put /v2/pet +// Update an existing pet +func (api *PetAPI) UpdatePet(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Post /v2/pet/:petId +// Updates a pet in the store with form data +func (api *PetAPI) UpdatePetWithForm(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Post /v2/pet/:petId/uploadImage +// uploads an image +func (api *PetAPI) UploadFile(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + diff --git a/samples/server/petstore/go-gin-api-server/go/api_store.go b/samples/server/petstore/go-gin-api-server/go/api_store.go index 4987a93862d..41db6e84633 100644 --- a/samples/server/petstore/go-gin-api-server/go/api_store.go +++ b/samples/server/petstore/go-gin-api-server/go/api_store.go @@ -14,16 +14,33 @@ import ( ) type StoreAPI struct { - // Delete /v2/store/order/:orderId - // Delete purchase order by ID - DeleteOrder gin.HandlerFunc - // Get /v2/store/inventory - // Returns pet inventories by status - GetInventory gin.HandlerFunc - // Get /v2/store/order/:orderId - // Find purchase order by ID - GetOrderById gin.HandlerFunc - // Post /v2/store/order - // Place an order for a pet - PlaceOrder gin.HandlerFunc } + +// Delete /v2/store/order/:orderId +// Delete purchase order by ID +func (api *StoreAPI) DeleteOrder(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/store/inventory +// Returns pet inventories by status +func (api *StoreAPI) GetInventory(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/store/order/:orderId +// Find purchase order by ID +func (api *StoreAPI) GetOrderById(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Post /v2/store/order +// Place an order for a pet +func (api *StoreAPI) PlaceOrder(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + diff --git a/samples/server/petstore/go-gin-api-server/go/api_user.go b/samples/server/petstore/go-gin-api-server/go/api_user.go index bfd6f9fba4c..218b79131eb 100644 --- a/samples/server/petstore/go-gin-api-server/go/api_user.go +++ b/samples/server/petstore/go-gin-api-server/go/api_user.go @@ -14,28 +14,61 @@ import ( ) type UserAPI struct { - // Post /v2/user - // Create user - CreateUser gin.HandlerFunc - // Post /v2/user/createWithArray - // Creates list of users with given input array - CreateUsersWithArrayInput gin.HandlerFunc - // Post /v2/user/createWithList - // Creates list of users with given input array - CreateUsersWithListInput gin.HandlerFunc - // Delete /v2/user/:username - // Delete user - DeleteUser gin.HandlerFunc - // Get /v2/user/:username - // Get user by user name - GetUserByName gin.HandlerFunc - // Get /v2/user/login - // Logs user into the system - LoginUser gin.HandlerFunc - // Get /v2/user/logout - // Logs out current logged in user session - LogoutUser gin.HandlerFunc - // Put /v2/user/:username - // Updated user - UpdateUser gin.HandlerFunc } + +// Post /v2/user +// Create user +func (api *UserAPI) CreateUser(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Post /v2/user/createWithArray +// Creates list of users with given input array +func (api *UserAPI) CreateUsersWithArrayInput(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Post /v2/user/createWithList +// Creates list of users with given input array +func (api *UserAPI) CreateUsersWithListInput(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Delete /v2/user/:username +// Delete user +func (api *UserAPI) DeleteUser(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/user/:username +// Get user by user name +func (api *UserAPI) GetUserByName(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/user/login +// Logs user into the system +func (api *UserAPI) LoginUser(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Get /v2/user/logout +// Logs out current logged in user session +func (api *UserAPI) LogoutUser(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} + +// Put /v2/user/:username +// Updated user +func (api *UserAPI) UpdateUser(c *gin.Context) { + // Your handler implementation + c.JSON(200, gin.H{"status": "OK"}) +} +