package io.swagger.core.filter; import io.swagger.model.ApiDescription; import io.swagger.models.ArrayModel; import io.swagger.models.ComposedModel; import io.swagger.models.Model; import io.swagger.models.Operation; import io.swagger.models.Path; import io.swagger.models.RefModel; import io.swagger.models.Response; import io.swagger.models.Swagger; import io.swagger.models.Tag; import io.swagger.models.parameters.BodyParameter; import io.swagger.models.parameters.Parameter; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.MapProperty; import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; public class SpecFilter { public Swagger filter(Swagger swagger, SwaggerSpecFilter filter, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) { Swagger clone = new Swagger(); clone.info(swagger.getInfo()) .tags(swagger.getTags() == null ? null : new ArrayList<Tag>(swagger.getTags())) .host(swagger.getHost()) .basePath(swagger.getBasePath()) .schemes(swagger.getSchemes()) .consumes(swagger.getConsumes()) .produces(swagger.getProduces()) .externalDocs(swagger.getExternalDocs()) .vendorExtensions(swagger.getVendorExtensions()); final Set<String> filteredTags = new HashSet<String>(); final Set<String> allowedTags = new HashSet<String>(); for (String resourcePath : swagger.getPaths().keySet()) { Path path = swagger.getPaths().get(resourcePath); Map<String, Operation> ops = new HashMap<String, Operation>(); ops.put("get", path.getGet()); ops.put("head", path.getHead()); ops.put("put", path.getPut()); ops.put("post", path.getPost()); ops.put("delete", path.getDelete()); ops.put("patch", path.getPatch()); ops.put("options", path.getOptions()); Path clonedPath = new Path(); for (String key : ops.keySet()) { Operation op = ops.get(key); if (op != null) { ApiDescription desc = new ApiDescription(resourcePath, key); final Set<String> tags; if (filter.isOperationAllowed(op, desc, params, cookies, headers)) { clonedPath.set(key, filterOperation(filter, op, desc, params, cookies, headers)); tags = allowedTags; } else { tags = filteredTags; } if (op.getTags() != null) { tags.addAll(op.getTags()); } } } if (!clonedPath.isEmpty()) { clone.path(resourcePath, clonedPath); } } final List<Tag> tags = clone.getTags(); filteredTags.removeAll(allowedTags); if (tags != null && !filteredTags.isEmpty()) { for (Iterator<Tag> it = tags.iterator(); it.hasNext(); ) { if (filteredTags.contains(it.next().getName())) { it.remove(); } } if (clone.getTags().isEmpty()) { clone.setTags(null); } } Map<String, Model> definitions = filterDefinitions(filter, swagger.getDefinitions(), params, cookies, headers); clone.setSecurityDefinitions(swagger.getSecurityDefinitions()); clone.setSecurity(swagger.getSecurity()); clone.setDefinitions(definitions); // isRemovingUnreferencedDefinitions is not defined in SwaggerSpecFilter to avoid breaking compatibility with // existing client filters directly implementing SwaggerSpecFilter. if (filter instanceof AbstractSpecFilter) { if (((AbstractSpecFilter)filter).isRemovingUnreferencedDefinitions()) { clone = removeBrokenReferenceDefinitions (clone); } } return clone; } private Swagger removeBrokenReferenceDefinitions (Swagger swagger) { if (swagger.getDefinitions() == null || swagger.getDefinitions().isEmpty()) return swagger; Set<String> referencedDefinitions = new TreeSet<String>(); if (swagger.getResponses() != null) { for (Response response: swagger.getResponses().values()) { String propertyRef = getPropertyRef(response.getSchema()); if (propertyRef != null) { referencedDefinitions.add(propertyRef); } } } if (swagger.getParameters() != null) { for (Parameter p: swagger.getParameters().values()) { if (p instanceof BodyParameter) { BodyParameter bp = (BodyParameter) p; Set<String> modelRef = getModelRef(bp.getSchema()); if (modelRef != null) { referencedDefinitions.addAll(modelRef); } } } } if (swagger.getPaths() != null) { for (Path path : swagger.getPaths().values()) { if (path.getParameters() != null) { for (Parameter p: path.getParameters()) { if (p instanceof BodyParameter) { BodyParameter bp = (BodyParameter) p; Set<String> modelRef = getModelRef(bp.getSchema()); if (modelRef != null) { referencedDefinitions.addAll(modelRef); } } } } if (path.getOperations() != null) { for (Operation op: path.getOperations()) { if (op.getResponses() != null) { for (Response response: op.getResponses().values()) { String propertyRef = getPropertyRef(response.getSchema()); if (propertyRef != null) { referencedDefinitions.add(propertyRef); } } } if (op.getParameters() != null) { for (Parameter p: op.getParameters()) { if (p instanceof BodyParameter) { BodyParameter bp = (BodyParameter) p; Set<String> modelRef = getModelRef(bp.getSchema()); if (modelRef != null) { referencedDefinitions.addAll(modelRef); } } } } } } } } if (swagger.getDefinitions() != null) { Set<String> nestedReferencedDefinitions = new TreeSet<String>(); for (String ref : referencedDefinitions){ locateReferencedDefinitions(ref, nestedReferencedDefinitions, swagger); } referencedDefinitions.addAll(nestedReferencedDefinitions); swagger.getDefinitions().keySet().retainAll(referencedDefinitions); } return swagger; } private void locateReferencedDefinitions (Map<String, Property> props, Set<String> nestedReferencedDefinitions, Swagger swagger) { if (props == null) return; for (String keyProp: props.keySet()) { Property p = props.get(keyProp); String ref = getPropertyRef(p); if (ref != null) { locateReferencedDefinitions(ref, nestedReferencedDefinitions, swagger); } } } private void locateReferencedDefinitions(String ref, Set<String> nestedReferencedDefinitions, Swagger swagger) { // if not already processed so as to avoid infinite loops if (!nestedReferencedDefinitions.contains(ref)) { nestedReferencedDefinitions.add(ref); Model model = swagger.getDefinitions().get(ref); if (model != null) { locateReferencedDefinitions(model.getProperties(), nestedReferencedDefinitions, swagger); } } } public Map<String, Model> filterDefinitions(SwaggerSpecFilter filter, Map<String, Model> definitions, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) { if (definitions == null) { return null; } Map<String, Model> clonedDefinitions = new LinkedHashMap<String, Model>(); for (String key : definitions.keySet()) { Model definition = definitions.get(key); Map<String, Property> clonedProperties = new LinkedHashMap<String, Property>(); if (definition.getProperties() != null) { for (String propName : definition.getProperties().keySet()) { Property property = definition.getProperties().get(propName); if (property != null) { boolean shouldInclude = filter.isPropertyAllowed(definition, property, propName, params, cookies, headers); if (shouldInclude) { clonedProperties.put(propName, property); } } } } Model clonedModel = (Model) definition.clone(); if (clonedModel.getProperties() != null) { clonedModel.getProperties().clear(); } if( definition.getVendorExtensions() != null && clonedModel.getVendorExtensions() != null ){ clonedModel.getVendorExtensions().putAll( definition.getVendorExtensions()); } clonedModel.setProperties(clonedProperties); clonedDefinitions.put(key, clonedModel); } return clonedDefinitions; } public Operation filterOperation(SwaggerSpecFilter filter, Operation op, ApiDescription api, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) { Operation clonedOperation = new Operation() .summary(op.getSummary()) .description(op.getDescription()) .operationId(op.getOperationId()) .schemes(op.getSchemes()) .consumes(op.getConsumes()) .produces(op.getProduces()) .tags(op.getTags()) .externalDocs(op.getExternalDocs()) .vendorExtensions(op.getVendorExtensions()) .deprecated(op.isDeprecated()); List<Parameter> clonedParams = new ArrayList<Parameter>(); if (op.getParameters() != null) { for (Parameter param : op.getParameters()) { if (filter.isParamAllowed(param, op, api, params, cookies, headers)) { clonedParams.add(param); } } } clonedOperation.setParameters(clonedParams); clonedOperation.setSecurity(op.getSecurity()); clonedOperation.setResponses(op.getResponses()); return clonedOperation; } private String getPropertyRef(Property property) { if (property instanceof ArrayProperty && ((ArrayProperty) property).getItems() != null) { return getPropertyRef(((ArrayProperty) property).getItems()); } else if (property instanceof MapProperty && ((MapProperty) property).getAdditionalProperties() != null) { return getPropertyRef(((MapProperty) property).getAdditionalProperties()); } else if (property instanceof RefProperty) { return ((RefProperty) property).getSimpleRef(); } return null; } private Set<String> getModelRef(Model model) { if (model instanceof ArrayModel && ((ArrayModel) model).getItems() != null) { String propertyRef = getPropertyRef(((ArrayModel) model).getItems()); if (propertyRef != null) { return new HashSet<String>(Arrays.asList(propertyRef)); } } else if (model instanceof ComposedModel && ((ComposedModel) model).getAllOf() != null) { Set<String> refs = new LinkedHashSet<String>(); ComposedModel cModel = (ComposedModel) model; for (Model ref: cModel.getAllOf()) { if (ref instanceof RefModel) { refs.add(((RefModel)ref).getSimpleRef()); } } return refs; } else if (model instanceof RefModel) { return new HashSet<String>(Arrays.asList(((RefModel) model).getSimpleRef())); } return null; } }