@@ -133,7 +133,6 @@ public class OpenAPINormalizer {
133133
134134 // when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
135135 final String FILTER = "FILTER" ;
136- Filter filter = new Filter ();
137136
138137 // when set (e.g. operationId:getPetById|addPet), filter out (or remove) everything else
139138 final String SET_CONTAINER_TO_NULLABLE = "SET_CONTAINER_TO_NULLABLE" ;
@@ -272,16 +271,7 @@ public void processRules(Map<String, String> inputRules) {
272271
273272 if (inputRules .get (FILTER ) != null ) {
274273 rules .put (FILTER , true );
275- String filters = inputRules .get (FILTER );
276- try {
277- filter = new Filter (filters );
278- } catch (RuntimeException e ) {
279- 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" ,
280- filters , Filter .OPERATION_ID , Filter .METHOD , Filter .TAG , Filter .PATH , e .getMessage ());
281- // throw an exception. This is a breaking change compared to pre 7.16.0
282- // Workaround: fix the syntax!
283- throw new IllegalArgumentException (message );
284- }
274+ // actual parsing is delayed to allow customization of the Filter processing
285275 }
286276
287277 if (inputRules .get (SET_CONTAINER_TO_NULLABLE ) != null ) {
@@ -327,6 +317,19 @@ public void processRules(Map<String, String> inputRules) {
327317 }
328318 }
329319
320+ /**
321+ * Create the filter to process the FILTER normalizer.
322+ * Override this to create a custom filter normalizer.
323+ *
324+ * @param openApi Contract used in the filtering (could be used for customization).
325+ * @param filters full FILTER value
326+ *
327+ * @return a Filter containing the parsed filters.
328+ */
329+ protected Filter createFilter (OpenAPI openApi , String filters ) {
330+ return new Filter (filters );
331+ }
332+
330333 /**
331334 * Normalizes the OpenAPI input, which may not perfectly conform to
332335 * the specification.
@@ -388,7 +391,10 @@ protected void normalizePaths() {
388391 "trace" , PathItem ::getTrace
389392 );
390393
391- if (filter .hasFilter ()) {
394+ if (Boolean .TRUE .equals (getRule (FILTER ))) {
395+ String filters = inputRules .get (FILTER );
396+ Filter filter = createFilter (this .openAPI , filters );
397+ filter .parse ();
392398 // Iterates over each HTTP method in methodMap, retrieves the corresponding Operations from the PathItem,
393399 // and marks it as internal (`x-internal=true`) if the method/operationId/tag/path is not in the filters.
394400 filter .apply (pathsEntry .getKey (), path , methodMap );
@@ -1796,33 +1802,52 @@ protected Schema processNormalize31Spec(Schema schema, Set<Schema> visitedSchema
17961802
17971803 // ===================== end of rules =====================
17981804
1799- static class Filter {
1805+ protected static class Filter {
18001806 public static final String OPERATION_ID = "operationId" ;
18011807 public static final String METHOD = "method" ;
18021808 public static final String TAG = "tag" ;
18031809 public static final String PATH = "path" ;
1810+ private final String filters ;
18041811 protected Set <String > operationIdFilters = Collections .emptySet ();
18051812 protected Set <String > methodFilters = Collections .emptySet ();
18061813 protected Set <String > tagFilters = Collections .emptySet ();
18071814 protected Set <String > pathStartingWithFilters = Collections .emptySet ();
18081815
1809- Filter () {
1816+ protected Filter (String filters ) {
1817+ this .filters = filters .trim ();
1818+ }
18101819
1820+ /**
1821+ * Perform the parsing of the filter string.
1822+ *
1823+ * @return true if filters need to be processed
1824+ */
1825+ public boolean parse () {
1826+ if (StringUtils .isEmpty (filters )) {
1827+ return false ;
1828+ }
1829+ try {
1830+ doParse ();
1831+ return hasFilter ();
1832+ } catch (RuntimeException e ) {
1833+ 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" ,
1834+ filters , Filter .OPERATION_ID , Filter .METHOD , Filter .TAG , Filter .PATH , e .getMessage ());
1835+ // throw an exception. This is a breaking change compared to pre 7.16.0
1836+ // Workaround: fix the syntax!
1837+ throw new IllegalArgumentException (message );
1838+ }
18111839 }
18121840
1813- public Filter ( String filters ) {
1841+ private void doParse ( ) {
18141842 for (String filter : filters .split (";" )) {
18151843 filter = filter .trim ();
18161844 String [] filterStrs = filter .split (":" );
18171845 if (filterStrs .length != 2 ) { // only support filter with : at the moment
1818- throw new IllegalArgumentException ("filter not supported :[" + filters + "]" );
1846+ throw new IllegalArgumentException ("filter with no value not supported :[" + filter + "]" );
18191847 } else {
18201848 String filterKey = filterStrs [0 ].trim ();
18211849 String filterValue = filterStrs [1 ];
1822- Set <String > parsedFilters = Arrays .stream (filterValue .split ("[|]" ))
1823- .filter (Objects ::nonNull )
1824- .map (String ::trim )
1825- .collect (Collectors .toCollection (HashSet ::new ));
1850+ Set <String > parsedFilters = splitByPipe (filterValue );
18261851 if (OPERATION_ID .equals (filterKey )) {
18271852 operationIdFilters = parsedFilters ;
18281853 } else if (METHOD .equals (filterKey )) {
@@ -1833,12 +1858,56 @@ public Filter(String filters) {
18331858 } else if (PATH .equals (filterKey )) {
18341859 pathStartingWithFilters = parsedFilters ;
18351860 } else {
1836- throw new IllegalArgumentException ( "filter not supported :[" + filters + "]" );
1861+ parse ( filterKey , filterValue );
18371862 }
18381863 }
18391864 }
18401865 }
18411866
1867+ /**
1868+ * Split the filterValue by pipe.
1869+ *
1870+ * @return the split values.
1871+ */
1872+ protected Set <String > splitByPipe (String filterValue ) {
1873+ return Arrays .stream (filterValue .split ("[|]" ))
1874+ .filter (Objects ::nonNull )
1875+ .map (String ::trim )
1876+ .collect (Collectors .toCollection (HashSet ::new ));
1877+ }
1878+
1879+ /**
1880+ * Parse non default filters.
1881+ *
1882+ * Override this method to add custom parsing logic.
1883+ *
1884+ * By default throws IllegalArgumentException.
1885+ *
1886+ * @param filterName name of the filter
1887+ * @param filterValue value of the filter
1888+ */
1889+ protected void parse (String filterName , String filterValue ) {
1890+ parseFails (filterName , filterValue );
1891+ }
1892+
1893+ protected void parseFails (String filterName , String filterValue ) {
1894+ throw new IllegalArgumentException ("filter not supported :[" + filterName + ":" + filterValue + "]" );
1895+ }
1896+
1897+ /**
1898+ * Test if the OpenAPI contract match an extra filter.
1899+ *
1900+ * Override this method to add custom logic.
1901+ *
1902+ * @param operation Openapi Operation
1903+ * @param path Path of the operation
1904+ *
1905+ * @return true if the operation of path match the filter
1906+ */
1907+ protected boolean hasCustomFilterMatch (String path , Operation operation ) {
1908+ return false ;
1909+ }
1910+
18421911 public boolean hasFilter () {
18431912 return !operationIdFilters .isEmpty () || !methodFilters .isEmpty () || !tagFilters .isEmpty () || !pathStartingWithFilters .isEmpty ();
18441913 }
@@ -1848,22 +1917,32 @@ public void apply(String path, PathItem pathItem, Map<String, Function<PathItem,
18481917 Operation operation = getter .apply (pathItem );
18491918 if (operation != null ) {
18501919 boolean found = false ;
1851- found |= hasMatch (PATH , operation , hasPathStarting (path ));
1852- found |= hasMatch (TAG , operation , hasTag (operation ));
1853- found |= hasMatch (OPERATION_ID , operation , hasOperationId (operation ));
1854- found |= hasMatch (METHOD , operation , hasMethod (method ));
1920+ found |= logIfMatch (PATH , operation , hasPathStarting (path ));
1921+ found |= logIfMatch (TAG , operation , hasTag (operation ));
1922+ found |= logIfMatch (OPERATION_ID , operation , hasOperationId (operation ));
1923+ found |= logIfMatch (METHOD , operation , hasMethod (method ));
1924+ found |= hasCustomFilterMatch (path , operation );
1925+
18551926 operation .addExtension (X_INTERNAL , !found );
18561927 }
18571928 });
18581929 }
18591930
1860- private boolean hasMatch (String filterName , Operation operation , boolean filterMatched ) {
1931+ protected boolean logIfMatch (String filterName , Operation operation , boolean filterMatched ) {
18611932 if (filterMatched ) {
1862- OpenAPINormalizer . LOGGER . info ( "operation `{}` marked as internal only (x-internal: true) by the {} FILTER" , operation . getOperationId (), filterName );
1933+ logMatch ( filterName , operation );
18631934 }
18641935 return filterMatched ;
18651936 }
18661937
1938+ protected void logMatch (String filterName , Operation operation ) {
1939+ getLogger ().info ("operation `{}` marked as internal only (x-internal: true) by the {} FILTER" , operation .getOperationId (), filterName );
1940+ }
1941+
1942+ protected Logger getLogger () {
1943+ return OpenAPINormalizer .LOGGER ;
1944+ }
1945+
18671946 private boolean hasPathStarting (String path ) {
18681947 return pathStartingWithFilters .stream ().anyMatch (filter -> path .startsWith (filter ));
18691948 }
@@ -1879,5 +1958,6 @@ private boolean hasOperationId(Operation operation) {
18791958 private boolean hasMethod (String method ) {
18801959 return methodFilters .contains (method );
18811960 }
1961+
18821962 }
18831963}
0 commit comments