Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@ Note, this feature only supports simple component references with a format like

Specific components that are not referenced but should be retained in the final output should be identified using the <<user-content-x-smallrye-directives-retain,retain>> directive with the <<user-content-x-smallrye-directives,x-smallrye-directives>> extension.

[#remove-unused-tags]
==== Remove Unused Tags

[source%nowrap]
----
mp.openapi.extensions.smallrye.remove-unused-tags.enable
----
Set to `true` to enable automatic removal of unused tags from the top-level `tags` array in the OpenAPI model. A tag is considered unused if it is not referenced by any operation in the `paths` section. Unused tags will be removed following annotation scanning and after running all other `OASFilter` instances that may be configured. Default value is `false`.

Tags that are not referenced but should be retained in the final output should be identified using the <<user-content-x-smallrye-directives-retain,retain>> directive with the <<user-content-x-smallrye-directives,x-smallrye-directives>> extension.



[#remove-unused-schemas]
==== Removal of Unused Schemas (DEPRECATED)

Expand Down Expand Up @@ -183,7 +196,7 @@ The `x-smallrye-directives` extension is a string list/array that contains one o
[#x-smallrye-directives-retain]
===== retain

The `retain` directive is used together with the `mp.openapi.extensions.smallrye.remove-unused-components` property to indicate that a component should not be removed from the OpenAPI model even when it has no references from within the same model.
The `retain` directive is used together with the `mp.openapi.extensions.smallrye.remove-unused-components` or `mp.openapi.extensions.smallrye.remove-unused-tags.enable` properties to indicate that a component or tag should not be removed from the OpenAPI model even when it has no references from within the same model.

[source%nowrap, java]
----
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/io/smallrye/openapi/api/OpenApiConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ default Set<ReferenceType> removeUnusedComponents() {
.collect(Collectors.toSet());
}

default boolean removeUnusedTags() {
return getConfigValue(SmallRyeOASConfig.SMALLRYE_REMOVE_UNUSED_TAGS, Boolean.class, () -> Boolean.FALSE);
}

default boolean mergeSchemaExamples() {
return getConfigValue(SmallRyeOASConfig.SMALLRYE_MERGE_SCHEMA_EXAMPLES, Boolean.class, () -> Boolean.TRUE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import io.smallrye.openapi.api.util.MergeUtil;
import io.smallrye.openapi.api.util.UnusedComponentFilter;
import io.smallrye.openapi.api.util.UnusedSchemaFilter;
import io.smallrye.openapi.api.util.UnusedTagFilter;
import io.smallrye.openapi.model.Extensions;
import io.smallrye.openapi.model.ReferenceType;
import io.smallrye.openapi.runtime.util.ProfileFilter;
Expand Down Expand Up @@ -209,6 +210,10 @@ private OpenAPI filterModel(OpenAPI model) {
model = FilterUtil.applyFilter(new UnusedComponentFilter(removeUnusedComponents), model);
}

if (config.removeUnusedTags()) {
model = FilterUtil.applyFilter(new UnusedTagFilter(), model);
}

if (!intermediateModel) {
// Remove any `x-smallrye-*` extensions that should not be in the result
model = FilterUtil.applyFilter(Extensions.newRemovalFilter(), model);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ private SmallRyeOASConfig() {
private static final String SUFFIX_SORTED_PROPERTIES_ENABLE = "sorted-properties.enable";
private static final String SUFFIX_REMOVE_UNUSED_COMPONENTS = "remove-unused-components";
private static final String SUFFIX_REMOVE_UNUSED_SCHEMAS_ENABLE = "remove-unused-schemas.enable";
private static final String SUFFIX_REMOVE_UNUSED_TAGS_ENABLE = "remove-unused-tags.enable";
private static final String SUFFIX_MERGE_SCHEMA_EXAMPLES = "merge-schema-examples";
private static final String SUFFIX_SORTED_PARAMETERS_ENABLE = "sorted-parameters.enable";
private static final String SUFFIX_GENERIC_RESPONSE_USE_DEFAULT = "generic-response-use-default";
Expand Down Expand Up @@ -56,6 +57,8 @@ private SmallRyeOASConfig() {

public static final String SMALLRYE_REMOVE_UNUSED_COMPONENTS = SMALLRYE_PREFIX + SUFFIX_REMOVE_UNUSED_COMPONENTS;

public static final String SMALLRYE_REMOVE_UNUSED_TAGS = SMALLRYE_PREFIX + SUFFIX_REMOVE_UNUSED_TAGS_ENABLE;

public static final String SMALLRYE_MERGE_SCHEMA_EXAMPLES = SMALLRYE_PREFIX + SUFFIX_MERGE_SCHEMA_EXAMPLES;

public static final String SMALLRYE_SORTED_PARAMETERS_ENABLE = SMALLRYE_PREFIX + SUFFIX_SORTED_PARAMETERS_ENABLE;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.smallrye.openapi.api.util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Extensible;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.eclipse.microprofile.openapi.models.Operation;
import org.eclipse.microprofile.openapi.models.tags.Tag;

import io.smallrye.openapi.model.Extensions;

/**
* An implementation of OASFilter that scans the OpenAPI model and removes
* any tags from the top-level `tags` array that are not referenced by any
* operation in the paths.
*/
public class UnusedTagFilter implements OASFilter {

/**
* Set of tag names that are referenced by at least one operation.
*/
private final Set<String> referencedTagNames = new HashSet<>();

@Override
public Operation filterOperation(Operation operation) {
List<String> operationTags = operation.getTags();

if (operationTags != null) {
referencedTagNames.addAll(operationTags);
}

return operation;
}

@Override
public void filterOpenAPI(OpenAPI openAPI) {
List<Tag> tags = openAPI.getTags();

if (tags == null || tags.isEmpty()) {
return;
}

List<Tag> tagsToKeep = new ArrayList<>();

for (Tag tag : tags) {

Check warning on line 49 in core/src/main/java/io/smallrye/openapi/api/util/UnusedTagFilter.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Reduce the total number of break and continue statements in this loop to use at most one.

See more on https://sonarcloud.io/project/issues?id=smallrye_smallrye-open-api&issues=AZ69Fz80whkEO4EgfzFX&open=AZ69Fz80whkEO4EgfzFX&pullRequest=2590
String tagName = tag.getName();

// Keep tags that are referenced by operations
if (tagName != null && referencedTagNames.contains(tagName)) {
tagsToKeep.add(tag);
continue;
}

// Keep tags marked with x-smallrye-directives=retain
if (Extensions.getDirectives((Extensible<?>) tag).contains("retain")) {

Check warning on line 59 in core/src/main/java/io/smallrye/openapi/api/util/UnusedTagFilter.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unnecessary cast to "Extensible".

See more on https://sonarcloud.io/project/issues?id=smallrye_smallrye-open-api&issues=AZ69Fz80whkEO4EgfzFY&open=AZ69Fz80whkEO4EgfzFY&pullRequest=2590
tagsToKeep.add(tag);
continue;
}

// Tag is unused and not marked to retain, log removal
UtilLogging.logger.unusedTagRemoved(tagName != null ? tagName : "<unnamed>");
}

// Update the tags list if any were removed
if (tagsToKeep.size() != tags.size()) {
openAPI.setTags(tagsToKeep);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ interface UtilLogging extends BasicLogger {
@Message(id = 1002, value = "%s with zero references removed from %s: %s")
void unusedComponentRemoved(String type, String prefix, String name);

@LogMessage(level = Logger.Level.DEBUG)
@Message(id = 1003, value = "Tag with zero references removed: %s")
void unusedTagRemoved(String name);

}
Loading
Loading