Skip to content

Commit

Permalink
Merge pull request #4762 from swagger-api/Ticket-4737-removeBrokenRef…
Browse files Browse the repository at this point in the history
…erences

refs-#4737 add scanning for other OAS elements besides paths
  • Loading branch information
micryc authored Oct 24, 2024
2 parents 7d5ec61 + 5402f1c commit dc8785e
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -350,38 +350,98 @@ private void addPathItemSchemaRef(PathItem pathItem, Set<String> referencedDefin
Map<PathItem.HttpMethod, Operation> ops = pathItem.readOperationsMap();
for (Operation op : ops.values()) {
if (op.getRequestBody() != null) {
addContentSchemaRef(op.getRequestBody().getContent(), referencedDefinitions);
addRequestBodySchemaRef(op.getRequestBody(), referencedDefinitions);
}
if (op.getResponses() != null) {
for (String keyResponses : op.getResponses().keySet()) {
ApiResponse response = op.getResponses().get(keyResponses);
if (response.getHeaders() != null) {
for (String keyHeaders : response.getHeaders().keySet()) {
Header header = response.getHeaders().get(keyHeaders);
addSchemaRef(header.getSchema(), referencedDefinitions);
addContentSchemaRef(header.getContent(), referencedDefinitions);
}
}
addContentSchemaRef(response.getContent(), referencedDefinitions);
addApiResponseSchemaRef(response, referencedDefinitions);
}
}
if (op.getParameters() != null) {
for (Parameter parameter : op.getParameters()) {
addSchemaRef(parameter.getSchema(), referencedDefinitions);
addContentSchemaRef(parameter.getContent(), referencedDefinitions);
addParameterSchemaRef(parameter, referencedDefinitions);
}
}
if (op.getCallbacks() != null) {
for (String keyCallback : op.getCallbacks().keySet()) {
Callback callback = op.getCallbacks().get(keyCallback);
for (PathItem callbackPathItem : callback.values()) {
addPathItemSchemaRef(callbackPathItem, referencedDefinitions);
}
addCallbackSchemaRef(callback, referencedDefinitions);
}
}
}
}

private void addApiResponseSchemaRef(ApiResponse response, Set<String> referencedDefinitions) {
if (response.getHeaders() != null) {
for (String keyHeaders : response.getHeaders().keySet()) {
Header header = response.getHeaders().get(keyHeaders);
addHeaderSchemaRef(header, referencedDefinitions);
}
}
addContentSchemaRef(response.getContent(), referencedDefinitions);
}

private void addRequestBodySchemaRef(RequestBody requestBody, Set<String> referencedDefinitions) {
addContentSchemaRef(requestBody.getContent(), referencedDefinitions);
}

private void addParameterSchemaRef(Parameter parameter, Set<String> referencedDefinitions) {
addSchemaRef(parameter.getSchema(), referencedDefinitions);
addContentSchemaRef(parameter.getContent(), referencedDefinitions);
}

private void addHeaderSchemaRef(Header header, Set<String> referencedDefinitions) {
addSchemaRef(header.getSchema(), referencedDefinitions);
addContentSchemaRef(header.getContent(), referencedDefinitions);
}

private void addCallbackSchemaRef(Callback callback, Set<String> referencedDefinitions){
for (PathItem callbackPathItem : callback.values()) {
addPathItemSchemaRef(callbackPathItem, referencedDefinitions);
}
}

private void addComponentsSchemaRef(Components components, Set<String> referencedDefinitions){

if (components.getResponses() != null){
for (String resourcePath : components.getResponses().keySet()) {
ApiResponse apiResponse = components.getResponses().get(resourcePath);
addApiResponseSchemaRef(apiResponse, referencedDefinitions);
}
}
if (components.getRequestBodies() != null){
for (String requestBody : components.getRequestBodies().keySet()) {
RequestBody requestBody1 = components.getRequestBodies().get(requestBody);
addRequestBodySchemaRef(requestBody1, referencedDefinitions);
}
}
if (components.getParameters() != null){
for (String parameter : components.getParameters().keySet()) {
Parameter resourceParam = components.getParameters().get(parameter);
addParameterSchemaRef(resourceParam, referencedDefinitions);
}
}
if (components.getHeaders() != null){
for (String header : components.getHeaders().keySet()) {
Header resourceHeader = components.getHeaders().get(header);
addHeaderSchemaRef(resourceHeader, referencedDefinitions);
}
}
if (components.getCallbacks() != null){
for (String callback : components.getCallbacks().keySet()){
Callback resourceCallback = components.getCallbacks().get(callback);
addCallbackSchemaRef(resourceCallback, referencedDefinitions);
}
}
if (components.getPathItems() != null){
for (String resourcePath : components.getPathItems().keySet()){
PathItem pathItem = components.getPathItems().get(resourcePath);
addPathItemSchemaRef(pathItem, referencedDefinitions);
}
}
}

protected OpenAPI removeBrokenReferenceDefinitions(OpenAPI openApi) {

if (openApi == null || openApi.getComponents() == null || openApi.getComponents().getSchemas() == null) {
Expand All @@ -395,6 +455,16 @@ protected OpenAPI removeBrokenReferenceDefinitions(OpenAPI openApi) {
addPathItemSchemaRef(pathItem, referencedDefinitions);
}
}
if (openApi.getWebhooks() != null){
for (String resourcePath : openApi.getWebhooks().keySet()) {
PathItem pathItem = openApi.getWebhooks().get(resourcePath);
addPathItemSchemaRef(pathItem, referencedDefinitions);
}
}
if (openApi.getComponents() != null){
Components components = openApi.getComponents();
addComponentsSchemaRef(components, referencedDefinitions);
}

referencedDefinitions.addAll(resolveAllNestedRefs(referencedDefinitions, referencedDefinitions, openApi));
openApi.getComponents()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Json31;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.core.util.Yaml31;
import io.swagger.v3.core.util.ResourceUtils;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
Expand Down Expand Up @@ -46,6 +48,8 @@ public class SpecFilterTest {
private static final String RESOURCE_RECURSIVE_MODELS = "specFiles/recursivemodels.json";
private static final String RESOURCE_PATH = "specFiles/petstore-3.0-v2.json";
private static final String RESOURCE_PATH_3303 = "specFiles/petstore-3.0-v2-ticket-3303.json";
private static final String RESOURCE_WITH_REF_DEFINITION_4737 = "specFiles/3.1.0/issue-4737-3.1.yaml";
private static final String RESOURCE_WITH_REFERRED_DEFINITIONS= "specFiles/3.1.0/specWithReferredSchemas-3.1.yaml";
private static final String RESOURCE_PATH_LIST = "specFiles/3.1.0/list-3.1.json";
private static final String RESOURCE_PATH_COMPOSED_SCHEMA = "specFiles/3.1.0/composed-schema-3.1.json";
private static final String RESOURCE_REFERRED_SCHEMAS = "specFiles/petstore-3.0-referred-schemas.json";
Expand Down Expand Up @@ -450,6 +454,30 @@ public void filterWithNullDefinitions() throws IOException {
assertNotNull(filtered);
}

@Test(description = "RemoveUnreferencedDefinitionsFilter should not remove schema definition if ref used in Webhook")
public void testTicket4737() throws IOException {
final OpenAPI openAPI = getOpenAPIYaml31(RESOURCE_WITH_REF_DEFINITION_4737);
final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);
assertNotNull(filtered.getComponents().getSchemas().get("RequestDto"));
}

@Test
public void shouldNotRemoveUsedDefinitions() throws IOException {
final OpenAPI openAPI = getOpenAPIYaml31(RESOURCE_WITH_REFERRED_DEFINITIONS);
final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);
assertNotNull(filtered.getComponents().getSchemas().get("ResponseDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("WebhookResponseDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("WebhookOperationDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("RequestBodyDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("ParameterDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("HeaderDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("CallbackDefinition"));
assertNotNull(filtered.getComponents().getSchemas().get("PathItemDefinition"));
assertNull(filtered.getComponents().getSchemas().get("UnusedDefinition"));
}

private Set getTagNames(OpenAPI openAPI) {
Set<String> result = new HashSet<>();
if (openAPI.getTags() != null) {
Expand All @@ -469,4 +497,14 @@ private OpenAPI getOpenAPI31(String path) throws IOException {
final String json = ResourceUtils.loadClassResource(getClass(), path);
return Json31.mapper().readValue(json, OpenAPI.class);
}

private OpenAPI getOpenAPIYaml(String path) throws IOException {
final String yaml = ResourceUtils.loadClassResource(getClass(), path);
return Yaml.mapper().readValue(yaml, OpenAPI.class);
}

private OpenAPI getOpenAPIYaml31(String path) throws IOException {
final String yaml = ResourceUtils.loadClassResource(getClass(), path);
return Yaml31.mapper().readValue(yaml, OpenAPI.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
openapi: 3.1.0
info:
title: OpenAPI definition
version: v0
servers:
- url: http://localhost
description: Generated server url
paths: {}
components:
schemas:
RequestDto:
properties:
personalNumber:
type: string
webhooks:
newPet:
post:
requestBody:
description: Information about a new pet in the system
content:
application/json:
schema:
$ref: '#/components/schemas/RequestDto'
description: Webhook Pet
responses:
'200':
description: >-
Return a 200 status to indicate that the data was received
successfully
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
openapi: 3.1.0
info:
title: OpenAPI definition
version: v0
servers:
- url: http://localhost
description: Generated server url
paths:
/users:
get:
summary: Get list of all users
operationId: usersList
responses:
200:
description: OK
content:
application/json:
schema:
type: string
maxLength: 100
minLength: 1
components:
schemas:
ResponseDefinition:
properties:
personalNumber:
type: string
WebhookResponseDefinition:
properties:
personalNumber:
type: integer
WebhookOperationDefinition:
properties:
personalNumber:
type: number
RequestBodyDefinition:
properties:
personalNumber:
type: string
ParameterDefinition:
properties:
parameterName:
type: string
HeaderDefinition:
type: string
description: header ref
CallbackDefinition:
description: callback ref
type: string
PathItemDefinition:
description: pathItem ref
type: string
UnusedDefinition:
description: unused definition
type: string
minLength: 1
maxLength: 100
responses:
OK:
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/ResponseDefinition"
requestBodies:
requestBody1:
description: request body
content:
application/json:
schema:
$ref: "#/components/schemas/RequestBodyDefinition"
parameters:
parameter1:
description: request body
name: parameter
in: query
content:
application/json:
schema:
$ref: "#/components/schemas/ParameterDefinition"
headers:
header1:
content:
application/json:
schema:
$ref: "#/components/schemas/HeaderDefinition"
callbacks:
myEvent: # Event name
"{$request.body#/callbackUrl}":
post:
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CallbackDefinition"
pathItems:
pathItem1:
get:
summary: Get list of all users
operationId: usersList
responses:
200:
description: OK
content:
application/json:
schema:
$ref: "#/components/schemas/PathItemDefinition"
webhooks:
newPet:
post:
requestBody:
description: Information about a new pet in the system
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookOperationDefinition'
description: Webhook Pet
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookResponseDefinition'

0 comments on commit dc8785e

Please sign in to comment.