Skip to content

Commit 36b7205

Browse files
Add tag and method filters during generation (OpenAPITools#20801)
* Add Filter by tag and method under OpenAPINormalizer * Update message for invalid filters * Update customization documentation with new filters * Add comment for new code block
1 parent 123119c commit 36b7205

File tree

3 files changed

+139
-9
lines changed

3 files changed

+139
-9
lines changed

docs/customization.md

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -601,11 +601,29 @@ Example:
601601
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer REMOVE_X_INTERNAL=true
602602
```
603603
604-
- `FILTER`: When set to `operationId:addPet|getPetById` for example, it will add `x-internal:true` to operations with operationId not equal to addPet/getPetById (which will have x-internal set to false) so that these operations marked as internal won't be generated.
604+
- `FILTER`
605605
606-
Example:
607-
```
608-
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/petstore.yaml -o /tmp/java-okhttp/ --openapi-normalizer FILTER="operationId:addPet|getPetById"
606+
The `FILTER` parameter allows selective inclusion of API operations based on specific criteria. It applies the `x-internal: true` property to operations that do **not** match the specified values, preventing them from being generated.
607+
608+
### Available Filters
609+
610+
- **`operationId`**
611+
When set to `operationId:addPet|getPetById`, operations **not** matching `addPet` or `getPetById` will be marked as internal (`x-internal: true`), and excluded from generation. Matching operations will have `x-internal: false`.
612+
613+
- **`method`**
614+
When set to `method:get|post`, operations **not** using `GET` or `POST` methods will be marked as internal (`x-internal: true`), preventing their generation.
615+
616+
- **`tag`**
617+
When set to `tag:person|basic`, operations **not** tagged with `person` or `basic` will be marked as internal (`x-internal: true`), and will not be generated.
618+
619+
### Example Usage
620+
621+
```sh
622+
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate \
623+
-g java \
624+
-i modules/openapi-generator/src/test/resources/3_0/petstore.yaml \
625+
-o /tmp/java-okhttp/ \
626+
--openapi-normalizer FILTER="operationId:addPet|getPetById"
609627
```
610628

611629
- `SET_CONTAINER_TO_NULLABLE`: When set to `array|set|map` (or just `array`) for example, it will set `nullable` in array, set and map to true.

modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.slf4j.LoggerFactory;
3333

3434
import java.util.*;
35+
import java.util.function.Function;
3536
import java.util.stream.Collectors;
3637

3738
import static org.openapitools.codegen.utils.StringUtils.getUniqueString;
@@ -119,6 +120,9 @@ public class OpenAPINormalizer {
119120
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
120121
final String FILTER = "FILTER";
121122
HashSet<String> operationIdFilters = new HashSet<>();
123+
HashSet<String> methodFilters = new HashSet<>();
124+
125+
HashSet<String> tagFilters = new HashSet<>();
122126

123127
// when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
124128
final String SET_CONTAINER_TO_NULLABLE = "SET_CONTAINER_TO_NULLABLE";
@@ -238,15 +242,25 @@ public void processRules(Map<String, String> inputRules) {
238242

239243
String[] filterStrs = inputRules.get(FILTER).split(":");
240244
if (filterStrs.length != 2) { // only support operationId with : at the moment
241-
LOGGER.error("FILTER rule must be in the form of `operationId:name1|name2|name3`: {}", inputRules.get(FILTER));
245+
LOGGER.error("FILTER rule must be in the form of `operationId:name1|name2|name3` or `method:get|post|put` or `tag:tag1|tag2|tag3`: {}", inputRules.get(FILTER));
242246
} else {
243247
if ("operationId".equals(filterStrs[0])) {
244248
operationIdFilters = Arrays.stream(filterStrs[1].split("[|]"))
245249
.filter(Objects::nonNull)
246250
.map(String::trim)
247251
.collect(Collectors.toCollection(HashSet::new));
252+
} else if ("method".equals(filterStrs[0])) {
253+
methodFilters = Arrays.stream(filterStrs[1].split("[|]"))
254+
.filter(Objects::nonNull)
255+
.map(String::trim)
256+
.collect(Collectors.toCollection(HashSet::new));
257+
} else if ("tag".equals(filterStrs[0])) {
258+
tagFilters = Arrays.stream(filterStrs[1].split("[|]"))
259+
.filter(Objects::nonNull)
260+
.map(String::trim)
261+
.collect(Collectors.toCollection(HashSet::new));
248262
} else {
249-
LOGGER.error("FILTER rule must be in the form of `operationId:name1|name2|name3`: {}", inputRules.get(FILTER));
263+
LOGGER.error("FILTER rule must be in the form of `operationId:name1|name2|name3` or `method:get|post|put` or `tag:tag1|tag2|tag3`: {}", inputRules.get(FILTER));
250264
}
251265
}
252266
}
@@ -338,6 +352,27 @@ private void normalizePaths() {
338352
PathItem path = pathsEntry.getValue();
339353
List<Operation> operations = new ArrayList<>(path.readOperations());
340354

355+
Map<String, Function<PathItem, Operation>> methodMap = Map.of(
356+
"get", PathItem::getGet,
357+
"put", PathItem::getPut,
358+
"head", PathItem::getHead,
359+
"post", PathItem::getPost,
360+
"delete", PathItem::getDelete,
361+
"patch", PathItem::getPatch,
362+
"options", PathItem::getOptions,
363+
"trace", PathItem::getTrace
364+
);
365+
366+
// Iterates over each HTTP method in methodMap, retrieves the corresponding Operation from the PathItem,
367+
// and marks it as internal (`x-internal`) if the method is not in methodFilters.
368+
methodMap.forEach((method, getter) -> {
369+
Operation operation = getter.apply(path);
370+
if (operation != null && !methodFilters.isEmpty()) {
371+
LOGGER.info("operation `{}` marked internal only (x-internal: `{}`) by the method FILTER", operation.getOperationId(), !methodFilters.contains(method));
372+
operation.addExtension("x-internal", !methodFilters.contains(method));
373+
}
374+
});
375+
341376
// Include callback operation as well
342377
for (Operation operation : path.readOperations()) {
343378
Map<String, Callback> callbacks = operation.getCallbacks();
@@ -357,7 +392,14 @@ private void normalizePaths() {
357392
if (operationIdFilters.contains(operation.getOperationId())) {
358393
operation.addExtension("x-internal", false);
359394
} else {
360-
LOGGER.info("operation `{}` marked as internal only (x-internal: true) by the FILTER", operation.getOperationId());
395+
LOGGER.info("operation `{}` marked as internal only (x-internal: true) by the operationId FILTER", operation.getOperationId());
396+
operation.addExtension("x-internal", true);
397+
}
398+
} else if (!tagFilters.isEmpty()) {
399+
if (operation.getTags().stream().anyMatch(tagFilters::contains)) {
400+
operation.addExtension("x-internal", false);
401+
} else {
402+
LOGGER.info("operation `{}` marked as internal only (x-internal: true) by the tag FILTER", operation.getOperationId());
361403
operation.addExtension("x-internal", true);
362404
}
363405
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ public void testRemoveXInternal() {
485485
}
486486

487487
@Test
488-
public void testFilter() {
488+
public void testOperationIdFilter() {
489489
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
490490

491491
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
@@ -503,7 +503,7 @@ public void testFilter() {
503503
}
504504

505505
@Test
506-
public void testFilterWithTrim() {
506+
public void testOperationIdFilterWithTrim() {
507507
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
508508

509509
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
@@ -520,6 +520,76 @@ public void testFilterWithTrim() {
520520
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions().get("x-internal"), true);
521521
}
522522

523+
@Test
524+
public void testFilterWithMethod() {
525+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
526+
527+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
528+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
529+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions(), null);
530+
531+
Map<String, String> options = new HashMap<>();
532+
options.put("FILTER", "method:get");
533+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
534+
openAPINormalizer.normalize();
535+
536+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions().get("x-internal"), false);
537+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
538+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions().get("x-internal"), true);
539+
}
540+
@Test
541+
public void testFilterWithMethodWithTrim() {
542+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
543+
544+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
545+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
546+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions(), null);
547+
548+
Map<String, String> options = new HashMap<>();
549+
options.put("FILTER", "method:\n\t\t\t\tget");
550+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
551+
openAPINormalizer.normalize();
552+
553+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions().get("x-internal"), false);
554+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
555+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions().get("x-internal"), true);
556+
}
557+
558+
@Test
559+
public void testFilterWithTag() {
560+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
561+
562+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
563+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
564+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions(), null);
565+
566+
Map<String, String> options = new HashMap<>();
567+
options.put("FILTER", "tag:basic");
568+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
569+
openAPINormalizer.normalize();
570+
571+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions().get("x-internal"), false);
572+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
573+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions().get("x-internal"), true);
574+
}
575+
@Test
576+
public void testFilterWithTagWithTrim() {
577+
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/enableKeepOnlyFirstTagInOperation_test.yaml");
578+
579+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions(), null);
580+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
581+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions(), null);
582+
583+
Map<String, String> options = new HashMap<>();
584+
options.put("FILTER", "tag:basic");
585+
OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, options);
586+
openAPINormalizer.normalize();
587+
588+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getGet().getExtensions().get("x-internal"), false);
589+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getDelete().getExtensions().get("x-internal"), true);
590+
assertEquals(openAPI.getPaths().get("/person/display/{personId}").getPut().getExtensions().get("x-internal"), true);
591+
}
592+
523593
@Test
524594
public void testComposedSchemaDoesNotThrow() {
525595
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/composed-schema.yaml");

0 commit comments

Comments
 (0)