Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refs-#4737 add scanning for other OAS elements besides paths #4762

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'
Loading