From 9eb5882f944dd89a0ccf212151d07e1cf663349a Mon Sep 17 00:00:00 2001 From: Beppe Catanese <1771700+gcatanese@users.noreply.github.com> Date: Mon, 11 Dec 2023 04:21:23 +0100 Subject: [PATCH] OpenAPI 3.1.0 Add webhooks support (#17174) * Add support for webhooks * Test webhook generation with Go Gin server * Generate samples * Removing \t * Remove tabs --- .../openapitools/codegen/CodegenConfig.java | 3 + .../codegen/CodegenConstants.java | 2 + .../openapitools/codegen/DefaultCodegen.java | 8 + .../codegen/DefaultGenerator.java | 255 +++++++++++++++++- .../codegen/model/WebhooksMap.java | 24 ++ .../resources/go-gin-server/routers.mustache | 12 +- .../codegen/DefaultCodegenTest.java | 15 ++ .../goginserver/GoGinServerCodegenTest.java | 21 ++ .../src/test/resources/3_1/webhooks.yaml | 34 +++ .../petstore/go-gin-api-server/go/routers.go | 3 +- 10 files changed, 363 insertions(+), 14 deletions(-) create mode 100644 modules/openapi-generator/src/main/java/org/openapitools/codegen/model/WebhooksMap.java create mode 100644 modules/openapi-generator/src/test/resources/3_1/webhooks.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index acb4e9afa995..9d43b19ccce7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -30,6 +30,7 @@ import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.model.WebhooksMap; import java.io.File; import java.util.List; @@ -217,6 +218,8 @@ public interface CodegenConfig { OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels); + WebhooksMap postProcessWebhooksWithModels(WebhooksMap objs, List allModels); + Map postProcessSupportingFileData(Map objs); void postProcessModelProperty(CodegenModel model, CodegenProperty property); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java index 670529a733aa..d9a09c037921 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java @@ -28,6 +28,7 @@ public class CodegenConstants { public static final String SUPPORTING_FILES = "supportingFiles"; public static final String MODEL_TESTS = "modelTests"; public static final String MODEL_DOCS = "modelDocs"; + public static final String WEBHOOKS = "webhooks"; public static final String API_TESTS = "apiTests"; public static final String API_DOCS = "apiDocs"; @@ -296,6 +297,7 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case, public static final String GENERATE_APIS = "generateApis"; public static final String GENERATE_API_DOCS = "generateApiDocs"; + public static final String GENERATE_WEBHOOKS = "generateWebhooks"; public static final String GENERATE_API_TESTS = "generateApiTests"; public static final String GENERATE_API_TESTS_DESC = "Specifies that api tests are to be generated."; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 33691acdd68b..f49296e44773 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -60,6 +60,7 @@ import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; import org.openapitools.codegen.model.OperationsMap; +import org.openapitools.codegen.model.WebhooksMap; import org.openapitools.codegen.serializer.SerializerUtils; import org.openapitools.codegen.templating.MustacheEngineAdapter; import org.openapitools.codegen.templating.mustache.*; @@ -977,6 +978,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { + return objs; + } + // override with any special post-processing @Override @SuppressWarnings("static-method") diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 2b0a510a1211..1e4162ea5e60 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -42,11 +42,7 @@ import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; import org.openapitools.codegen.meta.GeneratorMetadata; import org.openapitools.codegen.meta.Stability; -import org.openapitools.codegen.model.ApiInfoMap; -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.serializer.SerializerUtils; import org.openapitools.codegen.templating.CommonTemplateContentLocator; import org.openapitools.codegen.templating.GeneratorTemplateContentLocator; @@ -86,6 +82,7 @@ public class DefaultGenerator implements Generator { private Boolean generateApis = null; private Boolean generateModels = null; private Boolean generateSupportingFiles = null; + private Boolean generateWebhooks = null; private Boolean generateApiTests = null; private Boolean generateApiDocumentation = null; private Boolean generateModelTests = null; @@ -210,10 +207,11 @@ void configureGeneratorProperties() { generateApis = GlobalSettings.getProperty(CodegenConstants.APIS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.APIS, null); generateModels = GlobalSettings.getProperty(CodegenConstants.MODELS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODELS, null); generateSupportingFiles = GlobalSettings.getProperty(CodegenConstants.SUPPORTING_FILES) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.SUPPORTING_FILES, null); + generateWebhooks = GlobalSettings.getProperty(CodegenConstants.WEBHOOKS) != null ? Boolean.TRUE : getGeneratorPropertyDefaultSwitch(CodegenConstants.WEBHOOKS, null); - if (generateApis == null && generateModels == null && generateSupportingFiles == null) { + if (generateApis == null && generateModels == null && generateSupportingFiles == null && generateWebhooks == null) { // no specifics are set, generate everything - generateApis = generateModels = generateSupportingFiles = true; + generateApis = generateModels = generateSupportingFiles = generateWebhooks = true; } else { if (generateApis == null) { generateApis = false; @@ -224,6 +222,9 @@ void configureGeneratorProperties() { if (generateSupportingFiles == null) { generateSupportingFiles = false; } + if (generateWebhooks == null) { + generateWebhooks = false; + } } // model/api tests and documentation options rely on parent generate options (api or model) and no other options. // They default to true in all scenarios and can only be marked false explicitly @@ -241,6 +242,7 @@ void configureGeneratorProperties() { config.additionalProperties().put(CodegenConstants.GENERATE_APIS, generateApis); config.additionalProperties().put(CodegenConstants.GENERATE_MODELS, generateModels); + config.additionalProperties().put(CodegenConstants.GENERATE_WEBHOOKS, generateWebhooks); if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true); @@ -762,6 +764,169 @@ void generateApis(List files, List allOperations, List files, List allWebhooks, List allModels) { + if (!generateWebhooks) { + // TODO: Process these anyway and present info via dryRun? + LOGGER.info("Skipping generation of Webhooks."); + return; + } + Map> webhooks = processWebhooks(this.openAPI.getWebhooks()); + Set webhooksToGenerate = null; + String webhookNames = GlobalSettings.getProperty(CodegenConstants.WEBHOOKS); + if (webhookNames != null && !webhookNames.isEmpty()) { + webhooksToGenerate = new HashSet<>(Arrays.asList(webhookNames.split(","))); + } + if (webhooksToGenerate != null && !webhooksToGenerate.isEmpty()) { + Map> Webhooks = new TreeMap<>(); + for (String m : webhooks.keySet()) { + if (webhooksToGenerate.contains(m)) { + Webhooks.put(m, webhooks.get(m)); + } + } + webhooks = Webhooks; + } + for (String tag : webhooks.keySet()) { + try { + List wks = webhooks.get(tag); + wks.sort((one, another) -> ObjectUtils.compare(one.operationId, another.operationId)); + WebhooksMap operation = processWebhooks(config, tag, wks, allModels); + URL url = URLPathUtils.getServerURL(openAPI, config.serverVariableOverrides()); + operation.put("basePath", basePath); + operation.put("basePathWithoutHost", removeTrailingSlash(config.encodePath(url.getPath()))); + operation.put("contextPath", contextPath); + operation.put("baseName", tag); + Optional.ofNullable(openAPI.getTags()).orElseGet(Collections::emptyList).stream() + .map(Tag::getName) + .filter(Objects::nonNull) + .filter(tag::equalsIgnoreCase) + .findFirst() + .ifPresent(tagName -> operation.put("operationTagName", config.escapeText(tagName))); + operation.put("operationTagDescription", ""); + Optional.ofNullable(openAPI.getTags()).orElseGet(Collections::emptyList).stream() + .filter(t -> tag.equalsIgnoreCase(t.getName())) + .map(Tag::getDescription) + .filter(Objects::nonNull) + .findFirst() + .ifPresent(description -> operation.put("operationTagDescription", config.escapeText(description))); + Optional.ofNullable(config.additionalProperties().get("appVersion")).ifPresent(version -> operation.put("version", version)); + operation.put("apiPackage", config.apiPackage()); + operation.put("modelPackage", config.modelPackage()); + operation.putAll(config.additionalProperties()); + operation.put("classname", config.toApiName(tag)); + operation.put("classVarName", config.toApiVarName(tag)); + operation.put("importPath", config.toApiImport(tag)); + operation.put("classFilename", config.toApiFilename(tag)); + operation.put("strictSpecBehavior", config.isStrictSpecBehavior()); + Optional.ofNullable(openAPI.getInfo()).map(Info::getLicense).ifPresent(license -> operation.put("license", license)); + Optional.ofNullable(openAPI.getInfo()).map(Info::getContact).ifPresent(contact -> operation.put("contact", contact)); + + if (allModels == null || allModels.isEmpty()) { + operation.put("hasModel", false); + } else { + operation.put("hasModel", true); + } + + if (!config.vendorExtensions().isEmpty()) { + operation.put("vendorExtensions", config.vendorExtensions()); + } + + // process top-level x-group-parameters + if (config.vendorExtensions().containsKey("x-group-parameters")) { + boolean isGroupParameters = Boolean.parseBoolean(config.vendorExtensions().get("x-group-parameters").toString()); + + OperationMap objectMap = operation.getWebhooks(); + List operations = objectMap.getOperation(); + for (CodegenOperation op : operations) { + if (isGroupParameters && !op.vendorExtensions.containsKey("x-group-parameters")) { + op.vendorExtensions.put("x-group-parameters", Boolean.TRUE); + } + } + } + + // Pass sortParamsByRequiredFlag through to the Mustache template... + boolean sortParamsByRequiredFlag = true; + if (this.config.additionalProperties().containsKey(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG)) { + sortParamsByRequiredFlag = Boolean.parseBoolean(this.config.additionalProperties().get(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG).toString()); + } + operation.put("sortParamsByRequiredFlag", sortParamsByRequiredFlag); + + /* consumes, produces are no longer defined in OAS3.0 + processMimeTypes(swagger.getConsumes(), operation, "consumes"); + processMimeTypes(swagger.getProduces(), operation, "produces"); + */ + + allWebhooks.add(operation); + + addAuthenticationSwitches(operation); + + for (String templateName : config.apiTemplateFiles().keySet()) { + File written = null; + if (config.templateOutputDirs().containsKey(templateName)) { + String outputDir = config.getOutputDir() + File.separator + config.templateOutputDirs().get(templateName); + String filename = config.apiFilename(templateName, tag, outputDir); + // do not overwrite apiController file for spring server + if (apiFilePreCheck(filename, generatorCheck, templateName, templateCheck)){ + written = processTemplateToFile(operation, templateName, filename, generateWebhooks, CodegenConstants.WEBHOOKS, outputDir); + } else { + LOGGER.info("Implementation file {} is not overwritten",filename); + } + } else { + String filename = config.apiFilename(templateName, tag); + if(apiFilePreCheck(filename, generatorCheck, templateName, templateCheck)){ + written = processTemplateToFile(operation, templateName, filename, generateWebhooks, CodegenConstants.WEBHOOKS); + } else { + LOGGER.info("Implementation file {} is not overwritten",filename); + } + } + if (written != null) { + files.add(written); + if (config.isEnablePostProcessFile() && !dryRun) { + config.postProcessFile(written, "api"); + } + } + } + + // to generate api test files + for (String templateName : config.apiTestTemplateFiles().keySet()) { + String filename = config.apiTestFilename(templateName, tag); + File apiTestFile = new File(filename); + // do not overwrite test file that already exists + if (apiTestFile.exists()) { + this.templateProcessor.skip(apiTestFile.toPath(), "Test files never overwrite an existing file of the same name."); + } else { + File written = processTemplateToFile(operation, templateName, filename, generateApiTests, CodegenConstants.API_TESTS, config.apiTestFileFolder()); + if (written != null) { + files.add(written); + if (config.isEnablePostProcessFile() && !dryRun) { + config.postProcessFile(written, "api-test"); + } + } + } + } + + // to generate api documentation files + for (String templateName : config.apiDocTemplateFiles().keySet()) { + String filename = config.apiDocFilename(templateName, tag); + File written = processTemplateToFile(operation, templateName, filename, generateApiDocumentation, CodegenConstants.API_DOCS); + if (written != null) { + files.add(written); + if (config.isEnablePostProcessFile() && !dryRun) { + config.postProcessFile(written, "api-doc"); + } + } + } + + } catch (Exception e) { + throw new RuntimeException("Could not generate api file for '" + tag + "'", e); + } + } + if (GlobalSettings.getProperty("debugOperations") != null) { + LOGGER.info("############ Operation info ############"); + Json.prettyPrint(allWebhooks); + } + + } + // checking if apiController file is already existed for spring generator private boolean apiFilePreCheck(String filename, String generator, String templateName, String apiControllerTemplate) { File apiFile = new File(filename); @@ -917,6 +1082,10 @@ private void generateSupportingFiles(List files, Map bundl } Map buildSupportFileBundle(List allOperations, List allModels) { + return this.buildSupportFileBundle(allOperations, allModels, null); + } + + Map buildSupportFileBundle(List allOperations, List allModels, List allWebhooks) { Map bundle = new HashMap<>(config.additionalProperties()); bundle.put("apiPackage", config.apiPackage()); @@ -936,6 +1105,7 @@ Map buildSupportFileBundle(List allOperations, Li } bundle.put("contextPath", contextPath); bundle.put("apiInfo", apis); + bundle.put("webhooks", allWebhooks); bundle.put("models", allModels); bundle.put("apiFolder", config.apiPackage().replace('.', File.separatorChar)); bundle.put("modelPackage", config.modelPackage()); @@ -1066,9 +1236,11 @@ public List generate() { // apis List allOperations = new ArrayList<>(); generateApis(files, allOperations, allModels); - + // webhooks + List allWebhooks = new ArrayList<>(); + generateWebhooks(files, allWebhooks, allModels); // supporting files - Map bundle = buildSupportFileBundle(allOperations, allModels); + Map bundle = buildSupportFileBundle(allOperations, allModels, allWebhooks); generateSupportingFiles(files, bundle); if (dryRun) { @@ -1243,6 +1415,27 @@ public Map> processPaths(Paths paths) { return ops; } + public Map> processWebhooks(Map webhooks) { + Map> ops = new TreeMap<>(); + // when input file is not valid and doesn't contain any paths + if (webhooks == null) { + return ops; + } + for (Map.Entry webhooksEntry : webhooks.entrySet()) { + String resourceKey = webhooksEntry.getKey(); + PathItem path = webhooksEntry.getValue(); + processOperation(resourceKey, "get", path.getGet(), ops, path); + processOperation(resourceKey, "head", path.getHead(), ops, path); + processOperation(resourceKey, "put", path.getPut(), ops, path); + processOperation(resourceKey, "post", path.getPost(), ops, path); + processOperation(resourceKey, "delete", path.getDelete(), ops, path); + processOperation(resourceKey, "patch", path.getPatch(), ops, path); + processOperation(resourceKey, "options", path.getOptions(), ops, path); + processOperation(resourceKey, "trace", path.getTrace(), ops, path); + } + return ops; + } + private void processOperation(String resourcePath, String httpMethod, Operation operation, Map> operations, PathItem path) { if (operation == null) { return; @@ -1394,6 +1587,50 @@ private OperationsMap processOperations(CodegenConfig config, String tag, List wks, List allModels) { + WebhooksMap operations = new WebhooksMap(); + OperationMap objs = new OperationMap(); + objs.setClassname(config.toApiName(tag)); + objs.setPathPrefix(config.toApiVarName(tag)); + + // check for nickname uniqueness + if (config.getAddSuffixToDuplicateOperationNicknames()) { + Set opIds = new HashSet<>(); + int counter = 0; + for (CodegenOperation op : wks) { + String opId = op.nickname; + if (opIds.contains(opId)) { + counter++; + op.nickname += "_" + counter; + } + opIds.add(opId); + } + } + objs.setOperation(wks); + + operations.setWebhooks(objs); + operations.put("package", config.apiPackage()); + + Set allImports = new ConcurrentSkipListSet<>(); + for (CodegenOperation op : wks) { + allImports.addAll(op.imports); + } + + Map mappings = getAllImportsMappings(allImports); + Set> imports = toImportsObjects(mappings); + + //Some codegen implementations rely on a list interface for the imports + operations.setImports(new ArrayList<>(imports)); + + // add a flag to indicate whether there's any {{import}} + if (!imports.isEmpty()) { + operations.put("hasImport", true); + } + + config.postProcessWebhooksWithModels(operations, allModels); + return operations; + } + /** * Transforms a set of imports to a map with key config.toModelImport(import) and value the import string. * diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/model/WebhooksMap.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/model/WebhooksMap.java new file mode 100644 index 000000000000..4fd5af073bc3 --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/model/WebhooksMap.java @@ -0,0 +1,24 @@ +package org.openapitools.codegen.model; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class WebhooksMap extends HashMap { + public OperationMap getWebhooks() { + return (OperationMap) get("operations"); + } + + public void setWebhooks(OperationMap objs) { + put("operations", objs); + } + + @SuppressWarnings("unchecked") + public List> getImports() { + return (List>) get("imports"); + } + + public void setImports(List> imports) { + put("imports", imports); + } +} \ No newline at end of file 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 d99c4429f622..fbee487a76a6 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 @@ -55,13 +55,19 @@ type ApiHandleFunctions struct { } func getRoutes(handleFunctions ApiHandleFunctions) []Route { - return []Route{ - {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + return []Route{ {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} { "{{operationId}}", http.Method{{httpMethod}}, "{{{basePathWithoutHost}}}{{{path}}}", handleFunctions.{{classname}}.{{operationId}}, - },{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} + },{{/operation}}{{/operations}}{{/apis}}{{/apiInfo}}{{#webhooks}}{{#operations}}{{#operation}} + // webhook handler (adjust path accordingly) + { + "{{operationId}}", + http.Method{{httpMethod}}, + "{{{basePathWithoutHost}}}{{{path}}}", + handleFunctions.{{classname}}.{{operationId}}, + },{{/operation}}{{/operations}}{{/webhooks}} } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index 22460ef163ac..0ca9eca9211d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -51,6 +51,7 @@ import org.testng.annotations.Test; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; @@ -4808,4 +4809,18 @@ void testIsXML() { Assert.assertTrue(codegen.isXmlMimeType("application/xml")); Assert.assertTrue(codegen.isXmlMimeType("application/rss+xml")); } + + @Test + public void testWebhooks() throws IOException { + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_1/webhooks.yaml"); + final DefaultCodegen codegen = new DefaultCodegen(); + codegen.setOpenAPI(openAPI); + + Operation operation = openAPI.getWebhooks().get("newPet").getPost(); + CodegenOperation co = codegen.fromOperation("newPet", "get", operation, null); + + Assert.assertEquals(co.path, "/newPet"); + Assert.assertEquals(co.operationId, "newPetGet"); + } + } 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 0602c2af8f24..084878a91558 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 @@ -59,4 +59,25 @@ public void verifyGoMod() throws IOException { "require github.com/gin-gonic/gin v1.9.1"); } + @Test + public void webhooks() throws IOException { + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("go-gin-server") + .setGitUserId("my-user") + .setGitRepoId("my-repo") + .setPackageName("my-package") + .setInputSpec("src/test/resources/3_1/webhooks.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + TestUtils.assertFileContains(Paths.get(output + "/go/routers.go"), + "NewPetPost"); + } + } diff --git a/modules/openapi-generator/src/test/resources/3_1/webhooks.yaml b/modules/openapi-generator/src/test/resources/3_1/webhooks.yaml new file mode 100644 index 000000000000..44fc73aaa917 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/webhooks.yaml @@ -0,0 +1,34 @@ +openapi: 3.1.0 +info: + title: Webhook Example + version: 1.0.0 +# Since OAS 3.1.0 the paths element isn't necessary. Now a valid OpenAPI Document can describe only paths, webhooks, or even only reusable components +webhooks: + # Each webhook needs a name + newPet: + # This is a Path Item Object, the only difference is that the request is initiated by the API provider + post: + requestBody: + description: Information about a new pet in the system + content: + application/json: + schema: + $ref: "#/components/schemas/Pet" + responses: + "200": + description: Return a 200 status to indicate that the data was received successfully + +components: + schemas: + Pet: + required: + - id + - name + properties: + id: + type: integer + format: int64 + name: + type: string + tag: + type: string diff --git a/samples/server/petstore/go-gin-api-server/go/routers.go b/samples/server/petstore/go-gin-api-server/go/routers.go index f63c9490433b..b528b1521c21 100644 --- a/samples/server/petstore/go-gin-api-server/go/routers.go +++ b/samples/server/petstore/go-gin-api-server/go/routers.go @@ -67,8 +67,7 @@ type ApiHandleFunctions struct { } func getRoutes(handleFunctions ApiHandleFunctions) []Route { - return []Route{ - + return []Route{ { "AddPet", http.MethodPost,