Skip to content

Commit

Permalink
Merge pull request #865 from lnash94/beta_6_choreo
Browse files Browse the repository at this point in the history
[BallerinaToOpenAPI][Beta6] Fix cyclic record resolving issue
  • Loading branch information
hevayo authored Jan 21, 2022
2 parents b04fa6c + fa95efb commit a00632c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,12 @@ private ObjectSchema generateObjectSchemaFromRecordFields(Map<String, Schema> sc
Schema property = ConverterCommonUtils.getOpenApiSchema(type);
if (field.getValue().typeDescriptor().typeKind() == TypeDescKind.TYPE_REFERENCE) {
TypeReferenceTypeSymbol typeReference = (TypeReferenceTypeSymbol) field.getValue().typeDescriptor();
property = handleTypeReference(schema, typeReference, property);
property = handleTypeReference(schema, typeReference, property, isSameRecord(componentName,
typeReference));
schema = components.getSchemas();
} else if (field.getValue().typeDescriptor().typeKind() == TypeDescKind.UNION) {
property = handleUnionType((UnionTypeSymbol) field.getValue().typeDescriptor(), property);
property = handleUnionType((UnionTypeSymbol) field.getValue().typeDescriptor(), property,
componentName);
schema = components.getSchemas();
}
if (property instanceof ArraySchema) {
Expand Down Expand Up @@ -214,13 +216,15 @@ private ObjectSchema generateObjectSchemaFromRecordFields(Map<String, Schema> sc
* This function uses to handle the field datatype has TypeReference(ex: Record or Enum).
*/
private Schema handleTypeReference(Map<String, Schema> schema, TypeReferenceTypeSymbol typeReferenceSymbol,
Schema property) {
Schema property, boolean isCyclicRecord) {
if (typeReferenceSymbol.definition().kind() == SymbolKind.ENUM) {
EnumSymbol enumSymbol = (EnumSymbol) typeReferenceSymbol.definition();
property = mapEnumValues(enumSymbol);
} else {
property.set$ref(typeReferenceSymbol.getName().orElseThrow().trim());
createComponentSchema(schema, typeReferenceSymbol);
if (!isCyclicRecord) {
createComponentSchema(schema, typeReferenceSymbol);
}
}
return property;
}
Expand All @@ -233,7 +237,7 @@ private Schema handleTypeReference(Map<String, Schema> schema, TypeReferenceType
* };
* </pre>
*/
private Schema handleUnionType(UnionTypeSymbol unionType, Schema property) {
private Schema handleUnionType(UnionTypeSymbol unionType, Schema property, String parentComponentName) {
List<TypeSymbol> unionTypes = unionType.userSpecifiedMemberTypes();
List<Schema> properties = new ArrayList<>();
boolean nullable = false;
Expand All @@ -243,7 +247,8 @@ private Schema handleUnionType(UnionTypeSymbol unionType, Schema property) {
} else if (union.typeKind() == TypeDescKind.TYPE_REFERENCE) {
property = ConverterCommonUtils.getOpenApiSchema(union.typeKind().getName().trim());
TypeReferenceTypeSymbol typeReferenceTypeSymbol = (TypeReferenceTypeSymbol) union;
property = handleTypeReference(this.components.getSchemas(), typeReferenceTypeSymbol, property);
property = handleTypeReference(this.components.getSchemas(), typeReferenceTypeSymbol, property,
isSameRecord(parentComponentName, typeReferenceTypeSymbol));
properties.add(property);
// TODO: uncomment after fixing ballerina lang union type handling issue
// } else if (union.typeKind() == TypeDescKind.UNION) {
Expand All @@ -261,6 +266,14 @@ private Schema handleUnionType(UnionTypeSymbol unionType, Schema property) {
return property;
}

private boolean isSameRecord(String parentComponentName, TypeReferenceTypeSymbol typeReferenceTypeSymbol) {
if (parentComponentName == null) {
return false;
}
return typeReferenceTypeSymbol.getName().isPresent() &&
parentComponentName.equals(typeReferenceTypeSymbol.getName().get().trim());
}

/**
* This function generate oneOf composed schema for record fields.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@
* This test class for the covering the unit tests for record scenarios.
*/
public class RecordTests {
private static final Path RES_DIR =
Paths.get("src/test/resources/ballerina-to-openapi").toAbsolutePath();
private static final Path RES_DIR = Paths.get("src/test/resources/ballerina-to-openapi").toAbsolutePath();
private Path tempDir;

@BeforeMethod
Expand Down Expand Up @@ -89,6 +88,12 @@ public void testUnionTypeWithNullable() throws OpenApiConverterException, IOExce
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/union.yaml");
}

@Test(description = "When the record field has cyclic record type")
public void testCyclicRecord() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("record/cyclic_record.bal");
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/cyclic_record.yaml");
}

@AfterMethod
public void cleanUp() {
TestUtils.deleteDirectory(this.tempDir);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
openapi: 3.0.1
info:
title: PayloadV
version: 0.0.0
servers:
- url: "{server}:{port}/payloadV"
variables:
server:
default: petstore.swagger.io
port:
default: "443"
paths:
/pet:
post:
operationId: operation_post_/pet
responses:
"200":
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/ResponseError'
"202":
description: Accepted
/pet02:
post:
operationId: operation_post_/pet02
responses:
"200":
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/ResponseError02'
"202":
description: Accepted
components:
schemas:
ResponseError:
required:
- id
type: object
properties:
id:
type: integer
format: int32
nullable: true
resError:
$ref: '#/components/schemas/ResponseError'
ResponseError02:
required:
- id
type: object
properties:
id:
type: integer
format: int32
nullable: true
resError:
oneOf:
- $ref: '#/components/schemas/ResponseError02'
- type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ballerina/http;

type ResponseError record {|
int? id;
ResponseError resError?;
|};

type ResponseError02 record {|
int? id;
ResponseError02|string resError?;
|};

listener http:Listener ep0 = new (443, config = {host: "petstore.swagger.io"});

service /payloadV on ep0 {
resource function post pet() returns ResponseError|http:Accepted {
http:Accepted accept = {body: ()};
return accept;
}
resource function post pet02() returns ResponseError02|http:Accepted {
http:Accepted accept = {body: ()};
return accept;
}
}

0 comments on commit a00632c

Please sign in to comment.