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

Parameters as numbers #1888

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.swagger.v3.parser;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.callbacks.Callback;
Expand Down Expand Up @@ -109,10 +111,13 @@ public ResolverCache(OpenAPI openApi, List<AuthorizationValue> auths, String par
public <T> T loadRef(String ref, RefFormat refFormat, Class<T> expectedType) {
if (refFormat == RefFormat.INTERNAL) {
//we don't need to go get anything for internal refs
Object loadedRef = loadInternalRef(ref);
Object loadedRef = loadInternalRef(ref, expectedType);

try{
return expectedType.cast(loadedRef);
if (loadedRef != null){
return expectedType.cast(loadedRef);
}
return null;
}
catch (Exception e) {
return null;
Expand Down Expand Up @@ -304,8 +309,52 @@ protected String merge(String host, String ref) {
}
return host + ref;
}
private <T> T loadInternalRef(String ref, Class<T> expectedType) {
T result = null;
final String[] refParts = ref.split("#/");

if (refParts.length > 2) {
throw new RuntimeException("Invalid ref format: " + ref);
}

final String file = refParts[0];
final String definitionPath = refParts.length == 2 ? refParts[1] : null;

SwaggerParseResult deserializationUtilResult = new SwaggerParseResult();
String contents = Yaml.pretty(openApi);

if (contents != null) {
JsonNode tree = DeserializationUtils.deserializeIntoTree(contents, file, parseOptions, deserializationUtilResult);
String[] jsonPathElements = definitionPath.split("/");
for (String jsonPathElement : jsonPathElements) {
if (tree.isArray()) {
try {
tree = tree.get(Integer.valueOf(jsonPathElement));
} catch (NumberFormatException numberFormatException) {
//
}
} else {
tree = tree.get(unescapePointer(jsonPathElement));
}

//if at any point we do find an element we expect, print and error and abort
if (tree == null) {
throw new RuntimeException("Could not find " + definitionPath + " in contents of " + file);
}
}
if (parseOptions.isValidateExternalRefs()) {
result = deserializeFragment(tree, expectedType, file, definitionPath);
} else {
if (expectedType.equals(Schema.class)) {
OpenAPIDeserializer deserializer = new OpenAPIDeserializer();
result = (T) deserializer.getSchema((ObjectNode) tree, definitionPath.replace("/", "."), new OpenAPIDeserializer.ParseResult().openapi31(openapi31));
} else {
result = DeserializationUtils.deserialize(tree, file, expectedType, openapi31);
}
}
}
return result;
}

private Object loadInternalRef(String ref) {
Object result = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,6 @@ public List<Parameter> processParameters(List<Parameter> parameters) {
if (parameter.get$ref() != null) {
RefFormat refFormat = computeRefFormat(parameter.get$ref());
final Parameter resolvedParameter = cache.loadRef(parameter.get$ref(), refFormat, Parameter.class);
if (parameter.get$ref().startsWith("#") && parameter.get$ref().indexOf("#/components/parameters") <= -1) {
//TODO: Not possible to add warning during resolve doesn't accept result as an input. Hence commented below line.
//result.warning(location, "The parameter should use Reference Object to link to parameters that are defined at the OpenAPI Object's components/parameters.");
continue;
}

if(resolvedParameter == null) {
// can't resolve it!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,19 @@ public void testIssue1780() {

}

@Test
public void testInternalParametersAndResponsesAsNumbers() throws Exception {
ParseOptions options = new ParseOptions();
options.setResolve(true);
SwaggerParseResult result = new OpenAPIV3Parser().readLocation("src/test/resources/internalParametersAsNumbers/swagger.yaml", null, options);

Assert.assertNotNull(result);
Assert.assertNotNull(result.getOpenAPI());
Yaml.prettyPrint(result.getOpenAPI());
Assert.assertEquals(result.getOpenAPI().getPaths().get("/api/deal/{dealId}").getGet().getParameters().get(0).getName(), "dealId");
Assert.assertEquals(result.getOpenAPI().getPaths().get("/api/deal/{dealId}").getGet().getResponses().get("200").getDescription(), "Success");
}

@Test
public void testParametersAndResponsesAsNumbers() throws Exception {
ParseOptions options = new ParseOptions();
Expand Down Expand Up @@ -3408,7 +3421,7 @@ public void testValidateExternalRefsTrue() {
OpenAPI openAPI = result.getOpenAPI();
assertNotNull(openAPI);
assertNotNull(result.getMessages());
assertEquals(result.getMessages().size(), 19);
assertEquals(result.getMessages().size(), 20);
assertTrue(result.getMessages().contains("attribute components.requestBodies.NewItem.asdasd is unexpected (./ref.yaml)"));
assertTrue(result.getMessages().contains("attribute components.requestBodies.NewItem.descasdasdription is unexpected (./ref.yaml)"));
assertTrue(result.getMessages().contains("attribute components.responses.GeneralError.descrsaiption is unexpected (./ref.yaml)"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.PathItem;
Expand Down Expand Up @@ -181,7 +182,8 @@ public void testLoadInternalParameterRef(@Injectable Parameter mockedParameter)
}

@Test
public void testLoadInternalParameterRefWithSpaces(@Injectable Parameter mockedParameter) throws Exception {
public void testLoadInternalParameterRefWithSpaces() throws Exception {
Parameter mockedParameter = new Parameter();
OpenAPI openAPI = new OpenAPI();
openAPI.components(new Components().addParameters("foo bar", mockedParameter));

Expand All @@ -191,8 +193,9 @@ public void testLoadInternalParameterRefWithSpaces(@Injectable Parameter mockedP
}

@Test
public void testLoadInternalDefinitionRef(@Injectable Schema mockedModel) throws Exception {
public void testLoadInternalDefinitionRef() throws Exception {
OpenAPI openAPI = new OpenAPI();
Schema mockedModel = new Schema();
openAPI.components(new Components().addSchemas("foo", mockedModel));

ResolverCache cache = new ResolverCache(openAPI, auths, null);
Expand All @@ -204,8 +207,9 @@ public void testLoadInternalDefinitionRef(@Injectable Schema mockedModel) throws
}

@Test
public void testLoadInternalDefinitionRefWithSpaces(@Injectable Schema mockedModel) throws Exception {
public void testLoadInternalDefinitionRefWithSpaces() throws Exception {
OpenAPI openAPI = new OpenAPI();
Schema mockedModel = new Schema();
openAPI.components(new Components().addSchemas("foo bar", mockedModel));

ResolverCache cache = new ResolverCache(openAPI, auths, null);
Expand All @@ -214,13 +218,17 @@ public void testLoadInternalDefinitionRefWithSpaces(@Injectable Schema mockedMod
}

@Test
public void testLoadInternalDefinitionRefWithEscapedCharacters(@Injectable Schema mockedModel) throws Exception {
public void testLoadInternalDefinitionRefWithEscapedCharacters(@Injectable PathItem mockedPath) throws Exception {
OpenAPI openAPI = new OpenAPI();

Schema mockedModel = new Schema();
openAPI.path("/test", mockedPath);
openAPI.components(new Components().addSchemas("foo~bar/baz~1", mockedModel));

ResolverCache cache = new ResolverCache(openAPI, auths, null);
Schema actualResult = cache.loadRef("#/components/schemas/foo~0bar~1baz~01", RefFormat.INTERNAL, Schema.class);
assertEquals(actualResult, mockedModel);
assertEquals( actualResult, mockedModel);
Yaml.pretty(mockedModel);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
openapi: 3.0.1
servers:
# Added by API Auto Mocking Plugin
- description: SwaggerHub API Auto Mocking
url: https://demo.com
info:
description: api
version: v1
title: API
contact:
name: John Doe
email: [email protected]
license:
name: Apache 2.0
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
tags:
- name: Payments
description: Request payments
- name: Incentives
description: Retrieve incentives
- name: Deals
description: Save, update and search deals
paths:
/api/deal/{dealId}:
get:
tags:
- Deals
summary: Gets the latest version of a deal (based on TimeStamp) or the specified version or the transactional deal by state
description: "Versions of a deal are ordered by the TimeStamp, if the TimeStamp is not set, the Timestamp is\r\nset to the creation time."
operationId: DealGetVersionOne
parameters:
- $ref: '#/paths/~1GetDeal/get/parameters/0' #access by position
responses:
'200':
$ref: '#/paths/~1GetDeal/get/responses/200' #access by name
'401':
description: Unauthorized
'403':
description: Forbidden
'503':
description: Server Error
/GetDeal:
get:
tags:
- Deal
summary: Gets the latest version of a deal (based on TimeStamp) or the specified version or the transactional deal by state
description: "Versions of a deal are ordered by the TimeStamp, if the TimeStamp is not set, the Timestamp is\r\nset to the creation time."
operationId: DealGetVersionOne
parameters:
- name: dealId
in: path
description: The deal identifier.
required: true
style: simple
explode: false
schema:
maxLength: 250
type: string
description: The deal identifier.
- name: version
in: query
description: The version of the deal
required: false
style: form
explode: true
schema:
type: string
description: The version of the deal
nullable: true
- name: transform
in: query
description: 'The transform''s name used to format the output data. <p>Example: vinCRM</p>'
required: false
style: form
explode: true
schema:
type: string
description: The transform's name used to format the output data
nullable: true
responses:
'200':
description: Success
content:
application/vnd.common.v3+json:
schema:
$ref: '#/components/schemas/GetDealResponse'
'401':
description: Unauthorized
'403':
description: Forbidden
'404':
description: Not Found
content:
application/vnd.common.v3+json:
schema:
$ref: '#/components/schemas/inlineResponse4041'
'503':
description: Server Error
components:
schemas:
ReferenceKeyModel:
type: object
properties:
name:
type: string
nullable: true
createdByClientId:
type: string
nullable: true
value:
type: string
nullable: true
createdByDateTime:
type: string
format: date-time
additionalProperties: false
GetDealResponse:
type: object
properties:
id:
type: string
description: Deal API specified unique identifier
nullable: true
version:
type: string
description: The current version of the deal represented as a hash of the contract
nullable: true
href:
type: string
description: The href/url for the deal as it was created
nullable: true
references:
type: object
additionalProperties:
uniqueItems: true
type: array
items:
$ref: '#/components/schemas/ReferenceKeyModel'
description: Gets or sets the reference keys.
nullable: true
createdDate:
type: string
description: Gets the creation date and time of the deal version
format: date-time
nullable: true
additionalProperties: false
description: Full deal model