@@ -327,16 +327,29 @@ public void processRules(Map<String, String> inputRules) {
327327 }
328328
329329 /**
330- * Create the filter to process the FILTER normalizer.
330+ * Create the operations filter to process the FILTER normalizer.
331331 * Override this to create a custom filter normalizer.
332332 *
333333 * @param openApi Contract used in the filtering (could be used for customization).
334- * @param filters full FILTER value
334+ * @param input full input value
335335 *
336- * @return a Filter containing the parsed filters.
336+ * @return an OperationsFilter containing the parsed filters.
337337 */
338- protected Filter createFilter (OpenAPI openApi , String filters ) {
339- return new Filter (filters );
338+ protected OperationsFilter createOperationsFilter (OpenAPI openApi , String input ) {
339+ return new OperationsFilter (input );
340+ }
341+
342+ /**
343+ * Create the security schemes filter to process the FILTER normalizer.
344+ * Override this to create a custom filter normalizer.
345+ *
346+ * @param openApi Contract used in the filtering (could be used for customization).
347+ * @param input full input value
348+ *
349+ * @return an SecuritySchemesFilter containing the parsed filters.
350+ */
351+ protected SecuritySchemesFilter createSecuritySchemesFilter (OpenAPI openApi , String input ) {
352+ return new SecuritySchemesFilter (input );
340353 }
341354
342355 /**
@@ -403,7 +416,7 @@ protected void normalizePaths() {
403416
404417 if (Boolean .TRUE .equals (getRule (FILTER ))) {
405418 String filters = inputRules .get (FILTER );
406- Filter filter = createFilter (this .openAPI , filters );
419+ OperationsFilter filter = createOperationsFilter (this .openAPI , filters );
407420 if (filter .parse ()) {
408421 // Iterates over each HTTP method in methodMap, retrieves the corresponding Operations from the PathItem,
409422 // and marks it as internal (`x-internal=true`) if the method/operationId/tag/path is not in the filters.
@@ -586,9 +599,9 @@ protected void normalizeHeaders(Map<String, Header> headers) {
586599 * Normalizes securitySchemes in components
587600 */
588601 protected void normalizeComponentsSecuritySchemes () {
589- if (StringUtils .isEmpty (bearerAuthSecuritySchemeName )) {
590- return ;
591- }
602+ if (StringUtils .isEmpty (bearerAuthSecuritySchemeName )) {
603+ return ;
604+ }
592605
593606 Map <String , SecurityScheme > schemes = openAPI .getComponents ().getSecuritySchemes ();
594607 if (schemes == null ) {
@@ -1938,20 +1951,22 @@ private void normalizeExclusiveMinMax31(Schema<?> schema) {
19381951
19391952 // ===================== end of rules =====================
19401953
1941- protected static class Filter {
1942- public static final String OPERATION_ID = "operationId" ;
1943- public static final String METHOD = "method" ;
1944- public static final String TAG = "tag" ;
1945- public static final String PATH = "path" ;
1946- private final String filters ;
1947- protected Set <String > operationIdFilters = Collections .emptySet ();
1948- protected Set <String > methodFilters = Collections .emptySet ();
1949- protected Set <String > tagFilters = Collections .emptySet ();
1950- protected Set <String > pathStartingWithFilters = Collections .emptySet ();
1951- private boolean hasFilter ;
1954+ // Base class for filters. It provides basic parsing logic and utility functions for filters.
1955+ // All filters should have the same syntax:
1956+ // `filterName:value1|value2|value3` and multiple filters can be separated by `;`.
1957+ protected static abstract class BaseFilter {
1958+ protected boolean hasFilter ;
1959+ private final String input ;
1960+ // Key - filtering method, value - set of accepted values.
1961+ // For example, to filter operations by method the key would be "method" and the value is a set of {"get", "post"}.
1962+ protected Map <String , Set <String >> filteringMethodsMap ;
1963+
1964+ protected BaseFilter (String input ) {
1965+ this .input = input .trim ();
1966+ }
19521967
1953- protected Filter ( String filters ) {
1954- this . filters = filters . trim () ;
1968+ public boolean hasFilter ( ) {
1969+ return hasFilter ;
19551970 }
19561971
19571972 /**
@@ -1960,23 +1975,34 @@ protected Filter(String filters) {
19601975 * @return true if filters need to be processed
19611976 */
19621977 public boolean parse () {
1963- if (StringUtils .isEmpty (filters )) {
1978+ if (StringUtils .isEmpty (input )) {
19641979 return false ;
19651980 }
19661981 try {
19671982 doParse ();
19681983 return hasFilter ();
19691984 } catch (RuntimeException e ) {
1970- String message = String . format ( Locale . ROOT , "FILTER rule [%s] must be in the form of `%s:name1|name2|name3` or `%s:get|post|put` or `%s:tag1|tag2|tag3` or `%s:/v1|/v2`. Error: %s" ,
1971- filters , Filter . OPERATION_ID , Filter . METHOD , Filter . TAG , Filter . PATH , e .getMessage ());
1985+ String usage = usageMessage ();
1986+ String message = String . format ( Locale . ROOT , "%s Input: `%s` Error: %s" , usage , input , e .getMessage ());
19721987 // throw an exception. This is a breaking change compared to pre 7.16.0
19731988 // Workaround: fix the syntax!
19741989 throw new IllegalArgumentException (message );
19751990 }
19761991 }
19771992
1993+ // Defines the filtering methods supported by the filter.
1994+ // Can be overridden by child classes to customize filtering.
1995+ public abstract Set <String > filteringMethods ();
1996+
1997+ // Defines the subject being filtered, e.g. operation, security scheme, etc. This is used for logging purposes.
1998+ public abstract String filteringSubject ();
1999+
2000+ // Defines the usage message for the filter. This is used for logging purposes when the filter syntax is incorrect.
2001+ public abstract String usageMessage ();
2002+
19782003 private void doParse () {
1979- for (String filter : filters .split (";" )) {
2004+ Set <String > filteringMethods = filteringMethods ();
2005+ for (String filter : input .split (";" )) {
19802006 filter = filter .trim ();
19812007 String [] filterStrs = filter .split (":" );
19822008 if (filterStrs .length != 2 ) { // only support filter with : at the moment
@@ -1986,15 +2012,16 @@ private void doParse() {
19862012 String filterValue = filterStrs [1 ];
19872013 Set <String > parsedFilters = splitByPipe (filterValue );
19882014 hasFilter = true ;
1989- if (OPERATION_ID .equals (filterKey )) {
1990- operationIdFilters = parsedFilters ;
1991- } else if (METHOD .equals (filterKey )) {
1992- methodFilters = parsedFilters ;
1993- } else if (TAG .equals (filterKey )) {
1994- tagFilters = parsedFilters ;
1995- } else if (PATH .equals (filterKey )) {
1996- pathStartingWithFilters = parsedFilters ;
1997- } else {
2015+
2016+ boolean found = false ;
2017+ for (String method : filteringMethods ) {
2018+ if (method .equals (filterKey )) {
2019+ found = true ;
2020+ filteringMethodsMap .put (filterKey , parsedFilters );
2021+ break ;
2022+ }
2023+ }
2024+ if (!found ) {
19982025 parse (filterKey , filterValue );
19992026 }
20002027 }
@@ -2014,7 +2041,7 @@ protected Set<String> splitByPipe(String filterValue) {
20142041 }
20152042
20162043 /**
2017- * Parse non default filters .
2044+ * Parse non default filtering methods .
20182045 *
20192046 * Override this method to add custom parsing logic.
20202047 *
@@ -2031,6 +2058,50 @@ protected void parseFails(String filterName, String filterValue) {
20312058 throw new IllegalArgumentException ("filter not supported :[" + filterName + ":" + filterValue + "]" );
20322059 }
20332060
2061+ protected boolean logIfMatch (String filterName , String subjectId , boolean filterMatched ) {
2062+ if (filterMatched ) {
2063+ logMatch (filterName , subjectId );
2064+ }
2065+ return filterMatched ;
2066+ }
2067+
2068+ protected void logMatch (String filterName , String subjectId ) {
2069+ getLogger ().info ("{} `{}` marked as internal only (x-internal: true) by the {} filter" , filteringSubject (),
2070+ subjectId , filterName );
2071+ }
2072+
2073+ protected Logger getLogger () {
2074+ return OpenAPINormalizer .LOGGER ;
2075+ }
2076+ }
2077+
2078+ protected static class OperationsFilter extends BaseFilter {
2079+ public static final String OPERATION_ID = "operationId" ;
2080+ public static final String METHOD = "method" ;
2081+ public static final String TAG = "tag" ;
2082+ public static final String PATH = "path" ;
2083+
2084+ protected OperationsFilter (String filters ) {
2085+ super (filters );
2086+ }
2087+
2088+ @ Override
2089+ public Set <String > filteringMethods () {
2090+ return Set .of (OPERATION_ID , METHOD , TAG , PATH );
2091+ }
2092+
2093+ @ Override
2094+ public String filteringSubject () {
2095+ return "Operation" ;
2096+ }
2097+
2098+ @ Override
2099+ public String usageMessage () {
2100+ return String .format (Locale .ROOT ,
2101+ "FILTER rule must be in the form of `%s:name1|name2|name3` or `%s:get|post|put` or `%s:tag1|tag2|tag3` or `%s:/v1|/v2`." ,
2102+ OperationsFilter .OPERATION_ID , OperationsFilter .METHOD , OperationsFilter .TAG , OperationsFilter .PATH );
2103+ }
2104+
20342105 /**
20352106 * Test if the OpenAPI contract match an extra filter.
20362107 *
@@ -2045,58 +2116,103 @@ protected boolean hasCustomFilterMatch(String path, Operation operation) {
20452116 return false ;
20462117 }
20472118
2048- public boolean hasFilter () {
2049- return hasFilter ;
2050- }
2051-
20522119 public void apply (String path , PathItem pathItem , Map <String , Function <PathItem , Operation >> methodMap ) {
20532120 methodMap .forEach ((method , getter ) -> {
20542121 Operation operation = getter .apply (pathItem );
20552122 if (operation != null ) {
20562123 boolean found = false ;
2057- found |= logIfMatch (PATH , operation , hasPathStarting (path ));
2058- found |= logIfMatch (TAG , operation , hasTag (operation ));
2059- found |= logIfMatch (OPERATION_ID , operation , hasOperationId (operation ));
2060- found |= logIfMatch (METHOD , operation , hasMethod (method ));
2124+ String operationId = operation .getOperationId ();
2125+ found |= logIfMatch (PATH , operationId , hasPathStarting (path ));
2126+ found |= logIfMatch (TAG , operationId , hasTag (operation ));
2127+ found |= logIfMatch (OPERATION_ID , operationId , hasOperationId (operation ));
2128+ found |= logIfMatch (METHOD , operationId , hasMethod (method ));
20612129 found |= hasCustomFilterMatch (path , operation );
20622130
20632131 operation .addExtension (X_INTERNAL , !found );
20642132 }
20652133 });
20662134 }
20672135
2068- protected boolean logIfMatch (String filterName , Operation operation , boolean filterMatched ) {
2069- if (filterMatched ) {
2070- logMatch (filterName , operation );
2071- }
2072- return filterMatched ;
2073- }
2074-
2075- protected void logMatch (String filterName , Operation operation ) {
2076- getLogger ().info ("operation `{}` marked as internal only (x-internal: true) by the {} FILTER" , operation .getOperationId (), filterName );
2077- }
2078-
2079- protected Logger getLogger () {
2080- return OpenAPINormalizer .LOGGER ;
2081- }
2082-
20832136 private boolean hasPathStarting (String path ) {
2137+ Set <String > pathStartingWithFilters = filteringMethodsMap .getOrDefault (PATH , Collections .emptySet ());
20842138 return pathStartingWithFilters .stream ().anyMatch (filter -> path .startsWith (filter ));
20852139 }
20862140
2087- private boolean hasTag ( Operation operation ) {
2141+ private boolean hasTag (Operation operation ) {
2142+ Set <String > tagFilters = filteringMethodsMap .getOrDefault (TAG , Collections .emptySet ());
20882143 return operation .getTags () != null && operation .getTags ().stream ().anyMatch (tagFilters ::contains );
20892144 }
20902145
20912146 private boolean hasOperationId (Operation operation ) {
2147+ Set <String > operationIdFilters = filteringMethodsMap .getOrDefault (OPERATION_ID , Collections .emptySet ());
20922148 return operationIdFilters .contains (operation .getOperationId ());
20932149 }
20942150
20952151 private boolean hasMethod (String method ) {
2152+ Set <String > methodFilters = filteringMethodsMap .getOrDefault (METHOD , Collections .emptySet ());
20962153 return methodFilters .contains (method );
20972154 }
20982155 }
20992156
2157+ protected static class SecuritySchemesFilter extends BaseFilter {
2158+ public static final String KEY = "key" ;
2159+ public static final String TYPE = "type" ;
2160+
2161+ protected SecuritySchemesFilter (String filters ) {
2162+ super (filters );
2163+ }
2164+
2165+ @ Override
2166+ public Set <String > filteringMethods () {
2167+ return Set .of (KEY , TYPE );
2168+ }
2169+
2170+ @ Override
2171+ public String filteringSubject () {
2172+ return "Security scheme" ;
2173+ }
2174+
2175+ @ Override
2176+ public String usageMessage () {
2177+ return String .format (Locale .ROOT ,
2178+ "SECURITY_SCHEMES_FILTER rule must be in the form of `%s:key1|key2|key3` or `%s:apiKey|http|mutualTLS|oauth2|openIdConnect`." ,
2179+ KEY , TYPE );
2180+ }
2181+
2182+ /**
2183+ * Test if the OpenAPI contract match an extra filter.
2184+ *
2185+ * Override this method to add custom logic.
2186+ *
2187+ * @param schemeKey Security scheme key
2188+ * @param scheme Security scheme
2189+ *
2190+ * @return true if the security scheme matches the filter
2191+ */
2192+ protected boolean hasCustomFilterMatch (String schemeKey , SecurityScheme scheme ) {
2193+ return false ;
2194+ }
2195+
2196+ public void apply (String schemeKey , SecurityScheme scheme ) {
2197+ boolean found = false ;
2198+ found |= logIfMatch (KEY , schemeKey , hasKey (schemeKey ));
2199+ found |= logIfMatch (TYPE , schemeKey , hasType (scheme .getType ()));
2200+ found |= hasCustomFilterMatch (schemeKey , scheme );
2201+
2202+ scheme .addExtension (X_INTERNAL , !found );
2203+ }
2204+
2205+ private boolean hasKey (String key ) {
2206+ Set <String > keyFilters = filteringMethodsMap .getOrDefault (KEY , Collections .emptySet ());
2207+ return keyFilters .contains (key );
2208+ }
2209+
2210+ private boolean hasType (String type ) {
2211+ Set <String > typeFilters = filteringMethodsMap .getOrDefault (TYPE , Collections .emptySet ());
2212+ return typeFilters .contains (type );
2213+ }
2214+ }
2215+
21002216 /**
21012217 * When set to true, remove "properties" attribute on schema other than "object"
21022218 * since it should be ignored and may result in odd generated code
0 commit comments