Skip to content

Commit beef8a1

Browse files
joseegarciaclaude
andcommitted
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>
1 parent c6a4ec1 commit beef8a1

3 files changed

Lines changed: 26 additions & 5 deletions

File tree

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,35 @@ public static void mergeWebhooksIntoPaths(final JsonNode openApi) {
110110
? (ObjectNode) root.get(PATHS)
111111
: root.putObject(PATHS);
112112
webhooks.fields().forEachRemaining(webhook -> {
113-
final String pathKey = webhook.getKey().startsWith("/") ? webhook.getKey() : "/" + webhook.getKey();
114-
if (!paths.has(pathKey)) {
113+
final String webhookName = webhook.getKey();
114+
final String pathKey = webhookName.startsWith("/") ? webhookName : "/" + webhookName;
115+
// A leading-slash-only key would break the by-url grouping (pathUrl.split("/")[1]).
116+
if (StringUtils.isNotBlank(StringUtils.strip(webhookName, "/")) && !paths.has(pathKey)) {
117+
defaultOperationTags(webhook.getValue(), StringUtils.strip(webhookName, "/"));
115118
paths.set(pathKey, webhook.getValue());
116119
}
117120
});
118121
}
119122
}
120123

124+
/**
125+
* Ensures every operation of a webhook-derived Path Item carries a {@code tags} entry. Webhook
126+
* operations normally omit {@code tags}, but the path pipeline requires one; a missing/empty
127+
* {@code tags} is defaulted to the webhook name so generation works in both grouping modes.
128+
*/
129+
private static void defaultOperationTags(final JsonNode pathItem, final String defaultTag) {
130+
if (pathItem instanceof ObjectNode) {
131+
pathItem.fields().forEachRemaining(field -> {
132+
if (REST_VERB_SET.contains(field.getKey()) && field.getValue() instanceof ObjectNode) {
133+
final ObjectNode operation = (ObjectNode) field.getValue();
134+
if (!ApiTool.hasNode(operation, "tags") || !operation.get("tags").isArray() || operation.get("tags").isEmpty()) {
135+
operation.putArray("tags").add(defaultTag);
136+
}
137+
}
138+
});
139+
}
140+
}
141+
121142
public static Map<String, JsonNode> processPaths(final JsonNode openApi, final Map<String, JsonNode> schemaMap, SpecFile specFile) {
122143
final var basicJsonNodeMap = new HashMap<>(schemaMap);
123144

multiapi-engine/src/test/resources/openapigenerator/testWebhooks/api-test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ webhooks:
1818
post:
1919
summary: New pet notification
2020
operationId: newPetWebhook
21-
tags:
22-
- webhook
21+
# No `tags`: webhook operations normally omit them. The merge defaults the tag
22+
# to the webhook name so generation works in the default (by-url) grouping mode.
2323
requestBody:
2424
content:
2525
application/json:

multiapi-engine/src/test/resources/openapigenerator/testWebhooks/assets/NewPetApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public interface NewPetApi {
2929
@Operation(
3030
operationId = "newPetWebhook",
3131
summary = "New pet notification",
32-
tags = {"webhook"},
32+
tags = {"newPet"},
3333
responses = {
3434
@ApiResponse(responseCode = "200", description = "Notification acknowledged")
3535
}

0 commit comments

Comments
 (0)