Skip to content

Commit 12fd597

Browse files
joseegman-idoneeajoseegarciaclaude
authored
Support OpenAPI 3.1 top-level webhooks (#375) (#377)
* Fix #375: support OpenAPI 3.1 array-valued type (unions & nullable) OpenAPI 3.1 / JSON Schema 2020-12 allows a schema's `type` to be an array, e.g. type: ["string", "null"] (the nullable idiom that replaces 3.0's `nullable: true`, and general unions). ApiTool.getType() read the type via JsonNode.textValue(), which returns null for an array node and collapsed to "", so every isObject/isArray/isString/isNumber/isDateTime predicate and MapperUtil.getSimpleType failed -> the generator silently produced wrong or empty types for those fields. - ApiTool.getType: when `type` is an array, resolve to the first non-"null" entry ("null" only marks the type as nullable). All the isX predicates and getSimpleType then work unchanged for union/nullable types. - MapperUtil.processNumber: read the type via ApiTool.getType instead of schema.get("type").asText(), so array-valued numeric types resolve. - Add TypeConstants.NULL. - Add a 3.1.0 regression fixture (testOpenApi31Types) covering ["string", "null"], ["integer","null"], ["number","null"]+double, int64 and a plain array, with golden assets. Bumps version 6.3.2 -> 6.4.0 across the engine, maven and gradle modules. Non-array `type` handling is unchanged, so existing 3.0 specs are unaffected. Follow-up 3.1 items (examples array, $ref siblings, webhooks) tracked in #375. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Support OpenAPI 3.1 top-level webhooks (#375) OpenAPI 3.1 adds a top-level `webhooks` object: a map of named Path Item Objects describing out-of-band requests. The generator only iterated `paths`, so webhooks were ignored - no handler interface, no payload models. Add OpenApiUtil.mergeWebhooksIntoPaths, invoked right after parsing in OpenApiGenerator.processFile. Each webhook (keyed by name) is merged into `paths` under a "/"-prefixed key so the existing pipeline generates a handler interface for its operations and the request/response payload models. `paths` is created if the contract has none, and existing `paths` entries are never overwritten. Adds a 3.1.0 webhooks-only regression fixture (testWebhooks) with golden assets (NewPetApi + PetDTO). Stacked on the 6.4.0 array-type work (#376); both are part of the 3.1 support milestone, so the version stays 6.4.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Default tags on tag-less webhook operations to avoid NPE Webhook operations normally omit `tags`, but createOperation requires one (Objects.requireNonNull), so a realistic tag-less webhook crashed with a NullPointerException in the default by-url grouping mode - and was silently skipped in tag-grouping mode. The prior test masked this by giving the webhook explicit tags. mergeWebhooksIntoPaths now defaults each webhook operation's tags to the webhook name when absent/empty, and skips blank keys (guards the pathUrl.split()[1] grouping). The testWebhooks fixture is updated to the realistic tag-less shape to cover this. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Harden 3.1 type/webhook handling and add edge-case tests getType: extract array-type resolution into a helper. A genuine multi-type union (e.g. ["string","integer"]) now logs a warning and uses the first concrete type; a "null"-only/empty type array falls back to `object` instead of an empty type string. Adds edge-case regression fixtures: - testOpenApi31Union: union -> String (+warning), ["null"] -> Object - testWebhookPathCollision: webhook name colliding with an existing path is dropped (existing paths take precedence). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Annotate Gradle tasks with @DisableCachingByDefault Gradle 9.x plugin validation fails the build when a task type is neither @CacheableTask nor @DisableCachingByDefault. OpenApiTask and AsyncApiTask are code generators whose spec-file inputs are not declared as cacheable inputs, so caching is disabled explicitly with a documented reason. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: update thollander/actions-comment-pull-request to v2 (Node 20 deprecated) --------- Co-authored-by: joseegarcia <jose.garcia@disashop.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a549c5f commit 12fd597

16 files changed

Lines changed: 736 additions & 26 deletions

File tree

.github/workflows/gradle-pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
continue-on-error: true
3030
- name: Warn about version specification
3131
if: steps.plugin-version-check.outcome != 'success'
32-
uses: thollander/actions-comment-pull-request@v1
32+
uses: thollander/actions-comment-pull-request@v2
3333
with:
3434
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3535
message: Project version has not been updated in build.gradle. Please, update your version using https://semver.org specifications

.github/workflows/maven-pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
continue-on-error: true
2929
- name: Warn about version specification
3030
if: ${{ steps.engine_version_check.outcome != 'success' || steps.maven_plugin_version_check.outcome != 'success' }}
31-
uses: thollander/actions-comment-pull-request@v1
31+
uses: thollander/actions-comment-pull-request@v2
3232
with:
3333
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3434
message: Project version has not been updated in pom.xml. Please, update your version using https://semver.org specifications

multiapi-engine/src/main/java/com/sngular/api/generator/plugin/common/tools/ApiTool.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@
2121
import com.sngular.api.generator.plugin.asyncapi.util.FactoryTypeEnum;
2222
import com.sngular.api.generator.plugin.common.files.FileLocation;
2323
import com.sngular.api.generator.plugin.common.model.TypeConstants;
24+
import lombok.extern.slf4j.Slf4j;
2425
import org.apache.commons.collections4.CollectionUtils;
2526
import org.apache.commons.collections4.IteratorUtils;
2627
import org.apache.commons.collections4.Transformer;
2728
import org.apache.commons.lang3.StringUtils;
2829

30+
@Slf4j
2931
public final class ApiTool {
3032

3133
public static final String FORMAT = "format";
@@ -224,19 +226,27 @@ public static String getType(final JsonNode schema) {
224226
return "";
225227
}
226228
final JsonNode typeNode = getNode(schema, "type");
227-
if (typeNode.isArray()) {
228-
// OpenAPI 3.1 / JSON Schema 2020-12: "type" may be an array (e.g. ["string", "null"]).
229-
// Resolve to the first non-"null" entry; "null" only marks the type as nullable.
230-
String resolvedType = "";
231-
for (final JsonNode element : typeNode) {
232-
if (!TypeConstants.NULL.equalsIgnoreCase(element.asText())) {
233-
resolvedType = element.asText();
234-
break;
235-
}
229+
return typeNode.isArray() ? getArrayType(typeNode) : StringUtils.defaultIfEmpty(typeNode.textValue(), "");
230+
}
231+
232+
private static String getArrayType(final JsonNode typeNode) {
233+
// OpenAPI 3.1 / JSON Schema 2020-12: "type" may be an array (e.g. ["string", "null"]).
234+
// "null" only marks the type as nullable; keep the concrete (non-"null") types.
235+
final List<String> concreteTypes = new ArrayList<>();
236+
for (final JsonNode element : typeNode) {
237+
if (!TypeConstants.NULL.equalsIgnoreCase(element.asText())) {
238+
concreteTypes.add(element.asText());
236239
}
237-
return resolvedType;
238240
}
239-
return StringUtils.defaultIfEmpty(typeNode.textValue(), "");
241+
if (concreteTypes.isEmpty()) {
242+
// Only "null" (or an empty array): no concrete type to generate, fall back to object.
243+
return TypeConstants.OBJECT;
244+
}
245+
if (concreteTypes.size() > 1) {
246+
log.warn("Schema declares a multi-type union {}; only '{}' is generated, the remaining types are ignored.",
247+
concreteTypes, concreteTypes.get(0));
248+
}
249+
return concreteTypes.get(0);
240250
}
241251

242252
public static boolean hasItems(final JsonNode schema) {

multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/OpenApiGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ private void processPackage(final String apiPackage) {
9898
private void processFile(final SpecFile specFile) {
9999

100100
final JsonNode openAPI = OpenApiUtil.getPojoFromSpecFile(baseDir, specFile);
101+
OpenApiUtil.mergeWebhooksIntoPaths(openAPI);
101102
OpenApiUtil.solvePathRefs(openAPI, baseDir.resolve(specFile.getFilePath()).getParent().toUri());
102103
final String clientPackage = specFile.getClientPackage();
103104

multiapi-engine/src/main/java/com/sngular/api/generator/plugin/openapi/utils/OpenApiUtil.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public class OpenApiUtil {
3535

3636
public static final String PATHS = "paths";
3737

38+
public static final String WEBHOOKS = "webhooks";
39+
3840
static final Set<String> REST_VERB_SET = Set.of("get", "post", "delete", "patch", "put");
3941

4042
private OpenApiUtil() {
@@ -92,6 +94,52 @@ public static JsonNode getPojoFromSpecFile(final Path baseDir, final SpecFile sp
9294
return SchemaUtil.getPojoFromRef(baseDir.toUri(), specFile.getFilePath());
9395
}
9496

97+
/**
98+
* Merges the OpenAPI 3.1 top-level {@code webhooks} object into {@code paths} so the existing
99+
* path pipeline generates a handler interface and the request/response payload models for each
100+
* webhook. Each webhook is a Path Item Object keyed by name; it is added under a {@code "/"}-
101+
* prefixed key (webhooks have no URL) so the by-url grouping treats the webhook name as the
102+
* endpoint. Existing {@code paths} entries take precedence and are never overwritten.
103+
*
104+
* @param openApi the parsed root contract; its {@code paths} node is created/extended in place.
105+
*/
106+
public static void mergeWebhooksIntoPaths(final JsonNode openApi) {
107+
final JsonNode webhooks = openApi.get(WEBHOOKS);
108+
if (webhooks instanceof ObjectNode && openApi instanceof ObjectNode) {
109+
final ObjectNode root = (ObjectNode) openApi;
110+
final ObjectNode paths = root.has(PATHS) && root.get(PATHS).isObject()
111+
? (ObjectNode) root.get(PATHS)
112+
: root.putObject(PATHS);
113+
webhooks.fields().forEachRemaining(webhook -> {
114+
final String webhookName = webhook.getKey();
115+
final String pathKey = webhookName.startsWith("/") ? webhookName : "/" + webhookName;
116+
// A leading-slash-only key would break the by-url grouping (pathUrl.split("/")[1]).
117+
if (StringUtils.isNotBlank(StringUtils.strip(webhookName, "/")) && !paths.has(pathKey)) {
118+
defaultOperationTags(webhook.getValue(), StringUtils.strip(webhookName, "/"));
119+
paths.set(pathKey, webhook.getValue());
120+
}
121+
});
122+
}
123+
}
124+
125+
/**
126+
* Ensures every operation of a webhook-derived Path Item carries a {@code tags} entry. Webhook
127+
* operations normally omit {@code tags}, but the path pipeline requires one; a missing/empty
128+
* {@code tags} is defaulted to the webhook name so generation works in both grouping modes.
129+
*/
130+
private static void defaultOperationTags(final JsonNode pathItem, final String defaultTag) {
131+
if (pathItem instanceof ObjectNode) {
132+
pathItem.fields().forEachRemaining(field -> {
133+
if (REST_VERB_SET.contains(field.getKey()) && field.getValue() instanceof ObjectNode) {
134+
final ObjectNode operation = (ObjectNode) field.getValue();
135+
if (!ApiTool.hasNode(operation, "tags") || !operation.get("tags").isArray() || operation.get("tags").isEmpty()) {
136+
operation.putArray("tags").add(defaultTag);
137+
}
138+
}
139+
});
140+
}
141+
}
142+
95143
/**
96144
* Dereferences Path Item Objects that are declared as a {@code $ref} to another file (modular
97145
* contracts). These references are otherwise never resolved, so the affected paths silently

multiapi-engine/src/test/java/com/sngular/api/generator/plugin/openapi/OpenApiGeneratorFixtures.java

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,33 @@ public final class OpenApiGeneratorFixtures {
147147
.clientPackage("com.sngular.multifileplugin.externalref.client").modelNamePrefix("Api")
148148
.modelNameSuffix("DTO").build());
149149

150+
static final List<SpecFile> TEST_OPEN_API_31_UNION = List
151+
.of(SpecFile.builder().filePath("openapigenerator/testOpenApi31Union/api-test.yml")
152+
.apiPackage("com.sngular.multifileplugin.openapi31union")
153+
.modelPackage("com.sngular.multifileplugin.openapi31union.model")
154+
.clientPackage("com.sngular.multifileplugin.openapi31union.client")
155+
.modelNameSuffix("DTO").build());
156+
157+
static final List<SpecFile> TEST_WEBHOOK_PATH_COLLISION = List
158+
.of(SpecFile.builder().filePath("openapigenerator/testWebhookPathCollision/api-test.yml")
159+
.apiPackage("com.sngular.multifileplugin.webhookpathcollision")
160+
.modelPackage("com.sngular.multifileplugin.webhookpathcollision.model")
161+
.clientPackage("com.sngular.multifileplugin.webhookpathcollision.client")
162+
.modelNameSuffix("DTO").build());
163+
164+
static final List<SpecFile> TEST_WEBHOOKS = List
165+
.of(SpecFile.builder().filePath("openapigenerator/testWebhooks/api-test.yml")
166+
.apiPackage("com.sngular.multifileplugin.webhooks")
167+
.modelPackage("com.sngular.multifileplugin.webhooks.model")
168+
.clientPackage("com.sngular.multifileplugin.webhooks.client")
169+
.modelNameSuffix("DTO").build());
170+
150171
static final List<SpecFile> TEST_OPEN_API_31_TYPES = List
151172
.of(SpecFile.builder().filePath("openapigenerator/testOpenApi31Types/api-test.yml")
152173
.apiPackage("com.sngular.multifileplugin.openapi31types")
153174
.modelPackage("com.sngular.multifileplugin.openapi31types.model")
154175
.clientPackage("com.sngular.multifileplugin.openapi31types.client")
155-
.modelNameSuffix("DTO").build());
176+
.modelNameSuffix("DTO").build());
156177

157178
static final List<SpecFile> TEST_EXTERNAL_PATH_ITEM_REF_GENERATION = List
158179
.of(SpecFile.builder().filePath("openapigenerator/testExternalPathItemRefsGeneration/api-test.yml")
@@ -777,6 +798,60 @@ static Function<Path, Boolean> validateExternalRefGeneration() {
777798
DEFAULT_MODEL_API, expectedExceptionFiles, DEFAULT_EXCEPTION_API);
778799
}
779800

801+
static Function<Path, Boolean> validateOpenApi31Union() {
802+
803+
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/openapi31union";
804+
805+
final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/openapi31union/model";
806+
807+
final String COMMON_PATH = "openapigenerator/testOpenApi31Union/";
808+
809+
final String ASSETS_PATH = COMMON_PATH + "assets/";
810+
811+
final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "ItemApi.java");
812+
813+
final List<String> expectedTestApiModelFiles = List.of(ASSETS_PATH + "ItemDTO.java");
814+
815+
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
816+
DEFAULT_MODEL_API, Collections.emptyList(), null);
817+
}
818+
819+
static Function<Path, Boolean> validateWebhookPathCollision() {
820+
821+
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/webhookpathcollision";
822+
823+
final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/webhookpathcollision/model";
824+
825+
final String COMMON_PATH = "openapigenerator/testWebhookPathCollision/";
826+
827+
final String ASSETS_PATH = COMMON_PATH + "assets/";
828+
829+
final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "NewPetApi.java");
830+
831+
final List<String> expectedTestApiModelFiles = List.of(ASSETS_PATH + "PetDTO.java");
832+
833+
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
834+
DEFAULT_MODEL_API, Collections.emptyList(), null);
835+
}
836+
837+
static Function<Path, Boolean> validateWebhooks() {
838+
839+
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/webhooks";
840+
841+
final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/webhooks/model";
842+
843+
final String COMMON_PATH = "openapigenerator/testWebhooks/";
844+
845+
final String ASSETS_PATH = COMMON_PATH + "assets/";
846+
847+
final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "NewPetApi.java");
848+
849+
final List<String> expectedTestApiModelFiles = List.of(ASSETS_PATH + "PetDTO.java");
850+
851+
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
852+
DEFAULT_MODEL_API, Collections.emptyList(), null);
853+
}
854+
780855
static Function<Path, Boolean> validateOpenApi31Types() {
781856

782857
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/openapi31types";
@@ -793,25 +868,25 @@ static Function<Path, Boolean> validateOpenApi31Types() {
793868

794869
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
795870
DEFAULT_MODEL_API, Collections.emptyList(), null);
796-
}
797-
798-
static Function<Path, Boolean> validateExternalPathItemRefGeneration() {
871+
}
799872

800-
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/externalpathitemref";
873+
static Function<Path, Boolean> validateExternalPathItemRefGeneration() {
801874

802-
final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/externalpathitemref/model";
875+
final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/externalpathitemref";
803876

804-
final String COMMON_PATH = "openapigenerator/testExternalPathItemRefsGeneration/";
877+
final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/externalpathitemref/model";
805878

806-
final String ASSETS_PATH = COMMON_PATH + "assets/";
879+
final String COMMON_PATH = "openapigenerator/testExternalPathItemRefsGeneration/";
807880

808-
final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "DashboardApi.java");
881+
final String ASSETS_PATH = COMMON_PATH + "assets/";
809882

810-
final List<String> expectedTestApiModelFiles = List.of(ASSETS_PATH + "DashboardDTO.java");
883+
final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "DashboardApi.java");
811884

812-
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
813-
DEFAULT_MODEL_API, Collections.emptyList(), null);
814-
}
885+
final List<String> expectedTestApiModelFiles = List.of(ASSETS_PATH + "DashboardDTO.java");
886+
887+
return path -> commonTest(path, expectedTestApiFile, expectedTestApiModelFiles, DEFAULT_TARGET_API,
888+
DEFAULT_MODEL_API, Collections.emptyList(), null);
889+
}
815890

816891
static Function<Path, Boolean> validateAnyOfInResponse() {
817892

multiapi-engine/src/test/java/com/sngular/api/generator/plugin/openapi/OpenApiGeneratorTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ static Stream<Arguments> fileSpecToProcess() {
8585
OpenApiGeneratorFixtures.validateExternalRefGeneration()),
8686
Arguments.of("testOpenApi31Types", OpenApiGeneratorFixtures.TEST_OPEN_API_31_TYPES,
8787
OpenApiGeneratorFixtures.validateOpenApi31Types()),
88+
Arguments.of("testWebhooks", OpenApiGeneratorFixtures.TEST_WEBHOOKS,
89+
OpenApiGeneratorFixtures.validateWebhooks()),
90+
Arguments.of("testOpenApi31Union", OpenApiGeneratorFixtures.TEST_OPEN_API_31_UNION,
91+
OpenApiGeneratorFixtures.validateOpenApi31Union()),
92+
Arguments.of("testWebhookPathCollision", OpenApiGeneratorFixtures.TEST_WEBHOOK_PATH_COLLISION,
93+
OpenApiGeneratorFixtures.validateWebhookPathCollision()),
8894
Arguments.of("testExternalPathItemRefsGeneration", OpenApiGeneratorFixtures.TEST_EXTERNAL_PATH_ITEM_REF_GENERATION,
8995
OpenApiGeneratorFixtures.validateExternalPathItemRefGeneration()),
9096
Arguments.of("testAnyOfInResponse", OpenApiGeneratorFixtures.TEST_ANY_OF_IN_RESPONSE,
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
# Edge-case regression for OpenAPI 3.1 array-valued `type` (issue #375):
3+
# a genuine multi-type union (no "null") and a "null"-only type.
4+
# The generator picks the first concrete type for a union (logging a warning) and
5+
# falls back to `object` when the array has no concrete type.
6+
openapi: "3.1.0"
7+
info:
8+
version: 1.0.0
9+
title: Union types API (OpenAPI 3.1)
10+
license:
11+
name: MIT
12+
servers:
13+
- url: http://localhost:8080/v1
14+
tags:
15+
- name: item
16+
paths:
17+
/item:
18+
get:
19+
summary: Get the item
20+
operationId: getItem
21+
tags:
22+
- item
23+
responses:
24+
'200':
25+
description: The requested item
26+
content:
27+
application/json:
28+
schema:
29+
$ref: "#/components/schemas/Item"
30+
components:
31+
schemas:
32+
Item:
33+
type: object
34+
properties:
35+
id:
36+
type: string
37+
code:
38+
type: ["string", "integer"]
39+
note:
40+
type: ["null"]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.sngular.multifileplugin.openapi31union;
2+
3+
import java.util.Optional;
4+
import java.util.List;
5+
import java.util.Map;
6+
import javax.validation.Valid;
7+
8+
import io.swagger.v3.oas.annotations.Operation;
9+
import io.swagger.v3.oas.annotations.Parameter;
10+
import io.swagger.v3.oas.annotations.media.Content;
11+
import io.swagger.v3.oas.annotations.media.Schema;
12+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
13+
import org.springframework.http.MediaType;
14+
import org.springframework.http.HttpStatus;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.web.bind.annotation.*;
17+
import org.springframework.web.context.request.NativeWebRequest;
18+
19+
import com.sngular.multifileplugin.openapi31union.model.ItemDTO;
20+
21+
public interface ItemApi {
22+
23+
/**
24+
* GET /item: Get the item
25+
* @return The requested item; (status code 200)
26+
*/
27+
28+
@Operation(
29+
operationId = "getItem",
30+
summary = "Get the item",
31+
tags = {"item"},
32+
responses = {
33+
@ApiResponse(responseCode = "200", description = "The requested item", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ItemDTO.class)))
34+
}
35+
)
36+
@RequestMapping(
37+
method = RequestMethod.GET,
38+
value = "/item",
39+
produces = {"application/json"}
40+
)
41+
42+
default ResponseEntity<ItemDTO> getItem() {
43+
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
44+
}
45+
46+
}

0 commit comments

Comments
 (0)