diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java index 220af8d71d..b0ab171f5d 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/jackson/ModelResolver.java @@ -1353,7 +1353,8 @@ protected Schema _createSchemaForEnum(Class> enumClass) { for (Enum en : enumConstants) { Field enumField = ReflectionUtils.findField(en.name(), enumClass); - if (null != enumField && enumField.isAnnotationPresent(Hidden.class)) { + // Skip enum field if @Hidden is present and ignoreHidden is not enabled + if (null != enumField && enumField.isAnnotationPresent(Hidden.class) && !Boolean.TRUE.equals(configuration.isIgnoreHidden())) { continue; } @@ -1386,6 +1387,10 @@ protected boolean ignore(final Annotated member, final XmlAccessorType xmlAccess } protected boolean hasHiddenAnnotation(Annotated annotated) { + // If ignoreHidden is enabled, always return false (don't hide anything) + if (Boolean.TRUE.equals(configuration.isIgnoreHidden())) { + return false; + } return annotated.hasAnnotation(Hidden.class) || ( annotated.hasAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) && annotated.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class).hidden() diff --git a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Configuration.java b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Configuration.java index f9a3db41d7..d1bd9ae3a0 100644 --- a/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Configuration.java +++ b/modules/swagger-core/src/main/java/io/swagger/v3/core/util/Configuration.java @@ -40,6 +40,7 @@ public String toString() { private String openAPIVersion = "3.0.1"; private GroupsValidationStrategy groupsValidationStrategy = GroupsValidationStrategy.DEFAULT; private String validatorProcessorClass; + private Boolean ignoreHidden = Boolean.FALSE; public OpenAPI getOpenAPI() { return openAPI; @@ -161,4 +162,17 @@ public Configuration validatorProcessorClass(String validatorProcessorClass) { this.validatorProcessorClass = validatorProcessorClass; return this; } + + public Boolean isIgnoreHidden() { + return ignoreHidden; + } + + public void setIgnoreHidden(Boolean ignoreHidden) { + this.ignoreHidden = ignoreHidden; + } + + public Configuration ignoreHidden(Boolean ignoreHidden) { + this.ignoreHidden = ignoreHidden; + return this; + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/HiddenFieldTest.java b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/HiddenFieldTest.java index a0e9ccca00..3991a1a922 100644 --- a/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/HiddenFieldTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/v3/core/resolving/HiddenFieldTest.java @@ -3,20 +3,25 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.core.converter.ModelConverters; +import io.swagger.v3.core.util.Configuration; import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; +import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Map; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertNull; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; public class HiddenFieldTest { + @BeforeMethod + public void resetSingletons() { + // Singletons need to be reset due to configuration changes between tests + ModelConverters.reset(); + } + @Test(description = "it should ignore a hidden field") public void testHiddenField() { final Map models = ModelConverters.getInstance().read(ModelWithHiddenFields.class); @@ -87,4 +92,49 @@ public String getHidden() { return hidden; } } + + @Test(description = "it should include hidden fields when ignoreHidden is true") + public void testIgnoreHiddenConfiguration() { + Configuration configuration = new Configuration() + .ignoreHidden(Boolean.TRUE); + + final Map models = ModelConverters.getInstance(configuration).read(ModelWithHiddenFields.class); + + final Schema model = models.get("ModelWithHiddenFields"); + assertNotNull(model); + assertEquals(model.getProperties().size(), 3); + + final Schema idValue = (Schema) model.getProperties().get("id"); + assertTrue(idValue instanceof IntegerSchema); + + assertTrue(model.getRequired().contains("id")); + + final Schema nameValue = (Schema) model.getProperties().get("name"); + assertTrue(nameValue instanceof StringSchema); + + // password field should now be included when ignoreHidden is true + final Schema passwordValue = (Schema) model.getProperties().get("password"); + assertNotNull(passwordValue); + assertTrue(passwordValue instanceof StringSchema); + } + + @Test(description = "it should include hidden fields in @JsonCreator when ignoreHidden is true") + public void testIgnoreHiddenConfigurationWithJsonCreator() { + Configuration configuration = new Configuration() + .ignoreHidden(Boolean.TRUE); + + final Map models = ModelConverters.getInstance(configuration).read(ModelWithHiddenFieldsInJsonCreator.class); + + final Schema model = models.get("ModelWithHiddenFieldsInJsonCreator"); + assertNotNull(model); + assertEquals(model.getProperties().size(), 2); + + final Schema idValue = (Schema) model.getProperties().get("id"); + assertTrue(idValue instanceof IntegerSchema); + + // hidden field should now be included when ignoreHidden is true + final Schema hiddenValue = (Schema) model.getProperties().get("hidden"); + assertNotNull(hiddenValue); + assertTrue(hiddenValue instanceof StringSchema); + } } diff --git a/modules/swagger-gradle-plugin/README.md b/modules/swagger-gradle-plugin/README.md index 8371a6d0b8..fa4197392d 100644 --- a/modules/swagger-gradle-plugin/README.md +++ b/modules/swagger-gradle-plugin/README.md @@ -102,6 +102,7 @@ Parameter | Description | Required | Default `openapi31`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| false | `schemaResolution`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| DEFAULT | `openAPIVersion`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| `3.0.1/3.1.0` | +`ignoreHidden`|Include ignored Operations/Parameters in the OpenApi output|false|false **Note** parameter `openApiFile` corresponds to [config](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties) openAPI. It points to a location of a file in YAML or JSON format representing the input spec that will be merged with the resolved spec. Typically used to add Info section, or any other meta data. An example of such file: @@ -133,3 +134,4 @@ info: - Since version 2.2.28, `openAPIVersion` parameter is available, allowing to specify the version of the OpenAPI specification to be used for the resolved spec. - Since version 2.2.29, `groupsValidationStrategy` parameter is available, allowing to specify the strategy for resolving Validation annotations (`never`, `always`, `neverIfNoContext`). - Since version 2.2.29, `validatorProcessorClass` parameter is available, allowing to specify a custom validator processor class, implementation of `io.swagger.v3.core.util.ValidatorProcessor`. +- Since version 2.2.40, `ignoreHidden` parameter is available, if set to true all ignored Operations/Parameters will be included in the OpenApi document diff --git a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java index ba42d5f8ce..96bfde2bb9 100644 --- a/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java +++ b/modules/swagger-gradle-plugin/src/main/java/io/swagger/v3/plugins/gradle/tasks/ResolveTask.java @@ -145,6 +145,13 @@ public enum Format {JSON, YAML, JSONANDYAML} @Optional public final Property groupsValidationStrategy = getProject().getObjects().property(String.class); + /** + * @since 2.2.40 + */ + @Input + @Optional + public final Property ignoreHidden = getProject().getObjects().property(Boolean.class); + public Property getOutputFileName() { return outputFileName; } @@ -307,6 +314,27 @@ public void setGroupsValidationStrategy(String groupsValidationStrategy) { this.groupsValidationStrategy.set(groupsValidationStrategy); } + /** + * @since 2.2.40 + */ + public Property getIgnoreHidden() { + return ignoreHidden; + } + + /** + * @since 2.2.40 + */ + public void setIgnoreHidden(Boolean ignoreHidden) { + this.ignoreHidden.set(ignoreHidden); + } + + /** + * @since 2.2.40 + */ + public void setIgnoreHidden(@Nullable String ignoreHidden) { + setIgnoreHidden(ignoreHidden == null ? null : Boolean.valueOf(ignoreHidden)); + } + public Property getContextId() { return contextId; } @@ -595,6 +623,11 @@ public void resolve() throws GradleException { method.invoke(swaggerLoader, openAPIVersion.get()); } + if (ignoreHidden.isPresent()) { + method = swaggerLoaderClass.getDeclaredMethod("setIgnoreHidden", Boolean.class); + method.invoke(swaggerLoader, ignoreHidden.get()); + } + method = swaggerLoaderClass.getDeclaredMethod("resolve"); Map specs = (Map) method.invoke(swaggerLoader); diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java index c923387360..c088911632 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/SwaggerConfiguration.java @@ -50,6 +50,8 @@ public class SwaggerConfiguration implements OpenAPIConfiguration { private String validatorProcessorClass; + private Boolean ignoreHidden = Boolean.FALSE; + @Override public String getDefaultResponseCode() { @@ -471,6 +473,29 @@ public SwaggerConfiguration validatorProcessorClass(String validatorProcessorCla return this; } + /** + * @since 2.2.40 + */ + @Override + public Boolean isIgnoreHidden() { + return ignoreHidden; + } + + /** + * @since 2.2.40 + */ + public void setIgnoreHidden(Boolean ignoreHidden) { + this.ignoreHidden = ignoreHidden; + } + + /** + * @since 2.2.40 + */ + public SwaggerConfiguration ignoreHidden(Boolean ignoreHidden) { + this.ignoreHidden = ignoreHidden; + return this; + } + public Configuration toConfiguration() { Configuration configuration = new Configuration(); @@ -483,6 +508,7 @@ public Configuration toConfiguration() { configuration.setOpenAPIVersion(getOpenAPIVersion()); configuration.setGroupsValidationStrategy(getGroupsValidationStrategy()); configuration.setValidatorProcessorClass(getValidatorProcessorClass()); + configuration.setIgnoreHidden(isIgnoreHidden()); return configuration; } diff --git a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java index cd14be468d..41716f3fc9 100644 --- a/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java +++ b/modules/swagger-integration/src/main/java/io/swagger/v3/oas/integration/api/OpenAPIConfiguration.java @@ -95,4 +95,9 @@ public interface OpenAPIConfiguration { * @since 2.2.29 */ public Configuration toConfiguration(); + + /** + * @since 2.2.40 + */ + Boolean isIgnoreHidden(); } diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java index b15115aaf7..26456a1772 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/Reader.java @@ -299,7 +299,8 @@ public OpenAPI read(Class cls, openAPI.setOpenapi("3.1.0"); } - if (hidden != null) { // || (apiPath == null && !isSubresource)) { + // Skip class if @Hidden annotation is present and ignoreHidden is not enabled + if (hidden != null && !Boolean.TRUE.equals(config.isIgnoreHidden())) { // || (apiPath == null && !isSubresource)) { return openAPI; } @@ -1611,11 +1612,13 @@ private boolean isEmptyComponents(Components components) { protected boolean isOperationHidden(Method method) { io.swagger.v3.oas.annotations.Operation apiOperation = ReflectionUtils.getAnnotation(method, io.swagger.v3.oas.annotations.Operation.class); - if (apiOperation != null && apiOperation.hidden()) { + // Check @Operation(hidden=true) - respect ignoreHidden configuration + if (apiOperation != null && apiOperation.hidden() && !Boolean.TRUE.equals(config.isIgnoreHidden())) { return true; } + // Check @Hidden annotation - respect ignoreHidden configuration Hidden hidden = method.getAnnotation(Hidden.class); - if (hidden != null) { + if (hidden != null && !Boolean.TRUE.equals(config.isIgnoreHidden())) { return true; } if (config != null && !Boolean.TRUE.equals(config.isReadAllResources()) && apiOperation == null) { diff --git a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java index c39bda56f7..cfb5a80893 100644 --- a/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java +++ b/modules/swagger-jaxrs2/src/main/java/io/swagger/v3/jaxrs2/integration/SwaggerLoader.java @@ -59,6 +59,8 @@ public class SwaggerLoader { private String openAPIVersion; + private Boolean ignoreHidden = Boolean.FALSE; + /** * @since 2.0.6 */ @@ -318,6 +320,20 @@ public void setOpenAPIVersion(String openAPIVersion) { this.openAPIVersion = openAPIVersion; } + /** + * @since 2.2.40 + */ + public Boolean getIgnoreHidden() { + return ignoreHidden; + } + + /** + * @since 2.2.40 + */ + public void setIgnoreHidden(Boolean ignoreHidden) { + this.ignoreHidden = ignoreHidden; + } + public Map resolve() throws Exception{ Set ignoredRoutesSet = null; @@ -370,7 +386,8 @@ public Map resolve() throws Exception{ .alwaysResolveAppPath(alwaysResolveAppPath) .skipResolveAppPath(skipResolveAppPath) .openAPI31(openAPI31) - .convertToOpenAPI31(convertToOpenAPI31); + .convertToOpenAPI31(convertToOpenAPI31) + .ignoreHidden(ignoreHidden); if (schemaResolution != null) { config.schemaResolution(Schema.SchemaResolution.valueOf(schemaResolution)); } diff --git a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/annotations/operations/AnnotatedOperationMethodTest.java b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/annotations/operations/AnnotatedOperationMethodTest.java index aa2f63c48d..a22e4462c3 100644 --- a/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/annotations/operations/AnnotatedOperationMethodTest.java +++ b/modules/swagger-jaxrs2/src/test/java/io/swagger/v3/jaxrs2/annotations/operations/AnnotatedOperationMethodTest.java @@ -1,5 +1,6 @@ package io.swagger.v3.jaxrs2.annotations.operations; +import io.swagger.v3.jaxrs2.Reader; import io.swagger.v3.jaxrs2.annotations.AbstractAnnotationTest; import io.swagger.v3.jaxrs2.resources.GenericResponsesResource; import io.swagger.v3.jaxrs2.resources.HiddenAnnotatedUserResource; @@ -19,12 +20,16 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.OpenAPI; import org.testng.annotations.Test; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.POST; import java.io.IOException; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; public class AnnotatedOperationMethodTest extends AbstractAnnotationTest { @Test @@ -1557,4 +1562,42 @@ static class DeprecatedSimpleGetOperationTest { public void simpleGet() { } } + + @Test(description = "reads hidden operation when ignoreHidden is true") + public void testHiddenUserResourceWithIgnoreHiddenConfiguration() throws IOException { + SwaggerConfiguration config = new SwaggerConfiguration() + .openAPI(new OpenAPI()) + .ignoreHidden(Boolean.TRUE); + Reader reader = new Reader(config); + OpenAPI openAPI = reader.read(HiddenUserResource.class); + + assertNotNull(openAPI); + assertNotNull(openAPI.getPaths()); + assertNotNull(openAPI.getPaths().get("/user")); + assertNotNull(openAPI.getPaths().get("/user").getPost()); + assertEquals(openAPI.getPaths().get("/user").getPost().getSummary(), "Create user"); + } + + @Test(description = "includes operation with @Operation(hidden=true) when ignoreHidden is true") + public void testOperationHiddenTrueWithIgnoreHiddenConfiguration() throws IOException { + SwaggerConfiguration config = new SwaggerConfiguration() + .openAPI(new OpenAPI()) + .ignoreHidden(Boolean.TRUE); + Reader reader = new Reader(config); + OpenAPI openAPI = reader.read(ResourceWithHiddenOperation.class); + + assertNotNull(openAPI); + assertNotNull(openAPI.getPaths()); + assertNotNull(openAPI.getPaths().get("/test")); + assertNotNull(openAPI.getPaths().get("/test").getPost()); + assertEquals(openAPI.getPaths().get("/test").getPost().getSummary(), "Hidden operation"); + } + + @Path("/test") + static class ResourceWithHiddenOperation { + @POST + @Operation(hidden = true, summary = "Hidden operation") + public void hiddenOperation() { + } + } } diff --git a/modules/swagger-maven-plugin/README.md b/modules/swagger-maven-plugin/README.md index 6c9f45c029..2abd191c92 100644 --- a/modules/swagger-maven-plugin/README.md +++ b/modules/swagger-maven-plugin/README.md @@ -205,6 +205,7 @@ Parameter | Description | Required | Default `openapi31`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| false | `schemaResolution`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| DEFAULT | `openAPIVersion`|see [configuration property](https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Integration-and-Configuration#configuration-properties)| `3.0.1/3.1.0` | +`ignoreHidden`|Include ignored Operations/Parameters in the OpenApi output|false|false *** @@ -220,3 +221,4 @@ Since version 2.2.24, `schemaResolution` parameter is available, allowing to spe Since version 2.2.28, `openAPIVersion` parameter is available, allowing to specify the version of the OpenAPI specification to be used for the resolved spec. Since version 2.2.29, `groupsValidationStrategy` parameter is available, allowing to specify the strategy for resolving Validation annotations (`never`, `always`, `neverIfNoContext`). Since version 2.2.29, `validatorProcessorClass` parameter is available, allowing to specify a custom validator processor class, implementation of `io.swagger.v3.core.util.ValidatorProcessor`. +Since version 2.2.40, `ignoreHidden` parameter is available, if set to true all ignored Operations/Parameters will be included in the OpenApi document \ No newline at end of file diff --git a/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java b/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java index 6bb3e37a34..9d73709435 100644 --- a/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java +++ b/modules/swagger-maven-plugin/src/main/java/io/swagger/v3/plugin/maven/SwaggerMojo.java @@ -165,6 +165,9 @@ private void setDefaultsIfMissing(SwaggerConfiguration config) { if (convertToOpenAPI31 == null) { convertToOpenAPI31 = Boolean.FALSE; } + if (ignoreHidden == null) { + ignoreHidden = Boolean.FALSE; + } if (config.isPrettyPrint() == null) { config.prettyPrint(prettyPrint); } @@ -186,6 +189,9 @@ private void setDefaultsIfMissing(SwaggerConfiguration config) { if (config.isConvertToOpenAPI31() == null) { config.setConvertToOpenAPI31(convertToOpenAPI31); } + if (config.isIgnoreHidden() == null) { + config.setIgnoreHidden(ignoreHidden); + } } @@ -376,6 +382,10 @@ private SwaggerConfiguration mergeConfig(OpenAPI openAPIInput, SwaggerConfigurat config.openAPIVersion(openAPIVersion); } + if (ignoreHidden != null) { + config.ignoreHidden(ignoreHidden); + } + return config; } @@ -499,6 +509,12 @@ private boolean isCollectionNotBlank(Collection collection) { @Parameter(property = "resolve.openAPIVersion") private String openAPIVersion; + /** + * @since 2.2.40 + */ + @Parameter(property = "resolve.ignoreHidden") + private Boolean ignoreHidden; + private String projectEncoding = "UTF-8"; private SwaggerConfiguration config;