Skip to content

Commit

Permalink
Merge pull request #1584 from ballerina-platform/built-in-sub-type-1.7.x
Browse files Browse the repository at this point in the history
[1.7.x] Add built-in subtype support in record fields for OAS generation
  • Loading branch information
lnash94 authored Dec 18, 2023
2 parents 70102fe + 39bf860 commit 0a52da8
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@
public class Constants {
public static final String ATTR_HOST = "host";
public static final String INT = "int";
public static final String INT_SIGNED8 = "int_signed8";
public static final String INT_SIGNED16 = "int_signed16";
public static final String INT_SIGNED32 = "int_signed32";
public static final String INT_UNSIGNED8 = "int_unsigned8";
public static final String INT_UNSIGNED16 = "int_unsigned16";
public static final String INT_UNSIGNED32 = "int_unsigned32";
public static final String XML_COMMENT = "xml_comment";
public static final String XML_ELEMENT = "xml_element";
public static final String XML_PROCESSING_INSTRUCTION = "xml_processing_instruction";
public static final String XML_TEXT = "xml_text";
public static final String STRING_CHAR = "string_char";
public static final String INTEGER = "integer";
public static final String NUMBER = "number";
public static final String STRING = "string";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ public void createComponentSchema(Map<String, Schema> schema, TypeSymbol typeSym
}
TypeReferenceTypeSymbol typeRef = (TypeReferenceTypeSymbol) typeSymbol;
TypeSymbol type = typeRef.typeDescriptor();
if (type instanceof TypeReferenceTypeSymbol && isBuiltInSubTypes((TypeReferenceTypeSymbol) type)) {
type = ((TypeReferenceTypeSymbol) type).typeDescriptor();
}

if (type.typeKind() == TypeDescKind.INTERSECTION) {
type = excludeReadonlyIfPresent(type);
Expand All @@ -123,18 +126,38 @@ public void createComponentSchema(Map<String, Schema> schema, TypeSymbol typeSym
}
break;
case STRING:
schema.put(componentName, new StringSchema().description(typeDoc));
case STRING_CHAR:
Schema stringSchema = new StringSchema().description(typeDoc);
schema.put(componentName, stringSchema);
components.setSchemas(schema);
break;
case JSON:
case XML:
case XML_ELEMENT:
case XML_PROCESSING_INSTRUCTION:
case XML_TEXT:
case XML_COMMENT:
schema.put(componentName, new ObjectSchema().description(typeDoc));
components.setSchemas(schema);
break;
case INT:
schema.put(componentName, new IntegerSchema().description(typeDoc));
components.setSchemas(schema);
break;
case INT_SIGNED32:
Schema int32Schema = new IntegerSchema().description(typeDoc).format("int32");
schema.put(componentName, int32Schema);
components.setSchemas(schema);
break;
case INT_UNSIGNED32:
case INT_UNSIGNED16:
case INT_SIGNED16:
case INT_UNSIGNED8:
case INT_SIGNED8:
Schema subIntSchema = new IntegerSchema().description(typeDoc).format(null);
schema.put(componentName, subIntSchema);
components.setSchemas(schema);
break;
case DECIMAL:
schema.put(componentName, new NumberSchema().format(DOUBLE).description(typeDoc));
components.setSchemas(schema);
Expand Down Expand Up @@ -196,6 +219,26 @@ public void createComponentSchema(Map<String, Schema> schema, TypeSymbol typeSym
}
}

public static boolean isBuiltInSubTypes(TypeReferenceTypeSymbol typeSymbol) {
TypeSymbol referredType = typeSymbol.typeDescriptor();
switch (referredType.typeKind()) {
case INT_SIGNED8:
case INT_SIGNED16:
case INT_SIGNED32:
case INT_UNSIGNED8:
case INT_UNSIGNED16:
case INT_UNSIGNED32:
case XML_COMMENT:
case XML_ELEMENT:
case XML_PROCESSING_INSTRUCTION:
case XML_TEXT:
case STRING_CHAR:
return true;
default:
return false;
}
}

/**
* Remove readonly from the type symbol.
*
Expand Down Expand Up @@ -325,28 +368,33 @@ private ObjectSchema generateObjectSchemaFromRecordFields(Map<String, Schema> sc
if (!field.getValue().isOptional()) {
required.add(fieldName);
}
TypeDescKind fieldTypeKind = field.getValue().typeDescriptor().typeKind();
TypeSymbol fieldType = field.getValue().typeDescriptor();
if (fieldType instanceof TypeReferenceTypeSymbol &&
isBuiltInSubTypes((TypeReferenceTypeSymbol) fieldType)) {
fieldType = ((TypeReferenceTypeSymbol) fieldType).typeDescriptor();
}
TypeDescKind fieldTypeKind = fieldType.typeKind();
String type = fieldTypeKind.toString().toLowerCase(Locale.ENGLISH);
Schema property = ConverterCommonUtils.getOpenApiSchema(type);

if (fieldTypeKind == TypeDescKind.TYPE_REFERENCE) {
TypeReferenceTypeSymbol typeReference = (TypeReferenceTypeSymbol) field.getValue().typeDescriptor();
TypeReferenceTypeSymbol typeReference = (TypeReferenceTypeSymbol) fieldType;
property = handleTypeReference(schema, typeReference, property,
isSameRecord(ConverterCommonUtils.unescapeIdentifier(
typeReference.definition().getName().get()), typeReference));
schema = components.getSchemas();
} else if (fieldTypeKind == TypeDescKind.UNION) {
property = handleUnionType((UnionTypeSymbol) field.getValue().typeDescriptor(), property,
property = handleUnionType((UnionTypeSymbol) fieldType, property,
componentName);
schema = components.getSchemas();
} else if (fieldTypeKind == TypeDescKind.MAP) {
MapTypeSymbol mapTypeSymbol = (MapTypeSymbol) field.getValue().typeDescriptor();
MapTypeSymbol mapTypeSymbol = (MapTypeSymbol) fieldType;
property = handleMapType(schema, componentName, property, mapTypeSymbol);
schema = components.getSchemas();
}
if (property instanceof ArraySchema && !(((ArraySchema) property).getItems() instanceof ComposedSchema)) {
Boolean nullable = property.getNullable();
property = mapArrayToArraySchema(schema, field.getValue().typeDescriptor(), componentName);
property = mapArrayToArraySchema(schema, fieldType, componentName);
property.setNullable(nullable);
schema = components.getSchemas();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ public static Schema<?> getOpenApiSchema(String type) {
Schema<?> schema;
switch (type) {
case Constants.STRING:
case Constants.STRING_CHAR:
case Constants.PLAIN:
schema = new StringSchema();
break;
Expand All @@ -125,6 +126,18 @@ public static Schema<?> getOpenApiSchema(String type) {
schema = new IntegerSchema();
schema.setFormat("int64");
break;
case Constants.INT_SIGNED32:
schema = new IntegerSchema();
schema.setFormat("int32");
break;
case Constants.INT_UNSIGNED32:
case Constants.INT_UNSIGNED16:
case Constants.INT_SIGNED16:
case Constants.INT_UNSIGNED8:
case Constants.INT_SIGNED8:
schema = new IntegerSchema();
schema.setFormat(null);
break;
case Constants.BYTE_ARRAY:
case Constants.OCTET_STREAM:
schema = new StringSchema();
Expand All @@ -151,6 +164,10 @@ public static Schema<?> getOpenApiSchema(String type) {
case Constants.TYPE_REFERENCE:
case Constants.TYPEREFERENCE:
case Constants.XML:
case Constants.XML_ELEMENT:
case Constants.XML_PROCESSING_INSTRUCTION:
case Constants.XML_TEXT:
case Constants.XML_COMMENT:
case Constants.JSON:
default:
schema = new Schema<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public void testForTupleType() throws IOException {
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "data_type/tuple_type.yaml");
}

@Test(description = "test for Ballerina built-in subtypes as record fields")
public void testForBuiltInSubTypes() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("data_type/built_in_sub_types_in_record.bal");
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "data_type/built_in_sub_types_in_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,79 @@
import ballerina/http;

public type BalSignedInts record {|
int:Signed32 signed32;
int:Signed16 signed16;
int:Signed8 signed8;
|};

public type BalUnsignedInts record {|
int:Unsigned32 unsigned32;
int:Unsigned16 unsigned16;
int:Unsigned8 unsigned8;
|};

public type BalInts record {|
BalSignedInts signed;
BalUnsignedInts unsigned;
|};

public type BalXmls record {|
xml:Comment comment;
xml:Element element;
xml:ProcessingInstruction processingInstruction;
xml:Text text;
|};

public type BalSubTypes record {|
string:Char char;
BalInts ints;
BalXmls xmls;
|};

type Unsigned8 int:Unsigned8;
type Unsigned16 int:Unsigned16;
type Unsigned32 int:Unsigned32;
type Signed8 int:Signed8;
type Signed16 int:Signed16;
type Signed32 int:Signed32;
type Char string:Char;
type XmlElement xml:Element;
type XmlComment xml:Comment;
type XmlText xml:Text;
type XmlProcessingInstruction xml:ProcessingInstruction;


service /payloadV on new http:Listener(9090) {

resource function get path1() returns BalSubTypes {
return {
char: "a",
ints: {
signed: {
signed32: 32,
signed16: 16,
signed8: 8
},
unsigned: {
unsigned32: 32,
unsigned16: 16,
unsigned8: 8
}
},
xmls: {
comment: xml`<!-- comment -->`,
element: xml`<element>element</element>`,
processingInstruction: xml`<?processing instruction?>`,
text: xml`text`
}
};
}

resource function get path2() returns Unsigned8|Unsigned16|Unsigned32|
Signed8|Signed16|Signed32|
XmlElement|XmlComment|XmlText|XmlProcessingInstruction|
Char {

return 32;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
openapi: 3.0.1
info:
title: PayloadV
version: 0.0.0
servers:
- url: "{server}:{port}/payloadV"
variables:
server:
default: http://localhost
port:
default: "9090"
paths:
/path1:
get:
operationId: getPath1
responses:
"200":
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/BalSubTypes'
/path2:
get:
operationId: getPath2
responses:
"200":
description: Ok
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Unsigned8'
- $ref: '#/components/schemas/Unsigned16'
- $ref: '#/components/schemas/Unsigned32'
- $ref: '#/components/schemas/Signed8'
- $ref: '#/components/schemas/Signed16'
- $ref: '#/components/schemas/Signed32'
- $ref: '#/components/schemas/XmlElement'
- $ref: '#/components/schemas/XmlComment'
- $ref: '#/components/schemas/XmlText'
- $ref: '#/components/schemas/XmlProcessingInstruction'
- $ref: '#/components/schemas/Char'
components:
schemas:
BalInts:
required:
- signed
- unsigned
type: object
properties:
signed:
$ref: '#/components/schemas/BalSignedInts'
unsigned:
$ref: '#/components/schemas/BalUnsignedInts'
Signed32:
type: integer
format: int32
Signed8:
type: integer
BalSubTypes:
required:
- char
- ints
- xmls
type: object
properties:
char:
type: string
ints:
$ref: '#/components/schemas/BalInts'
xmls:
$ref: '#/components/schemas/BalXmls'
Unsigned8:
type: integer
Signed16:
type: integer
BalUnsignedInts:
required:
- unsigned16
- unsigned32
- unsigned8
type: object
properties:
unsigned32:
type: integer
unsigned16:
type: integer
unsigned8:
type: integer
XmlProcessingInstruction:
type: object
Unsigned32:
type: integer
BalXmls:
required:
- comment
- element
- processingInstruction
- text
type: object
properties:
comment: {}
element: {}
processingInstruction: {}
text: {}
Char:
type: string
XmlComment:
type: object
XmlElement:
type: object
BalSignedInts:
required:
- signed16
- signed32
- signed8
type: object
properties:
signed32:
type: integer
format: int32
signed16:
type: integer
signed8:
type: integer
Unsigned16:
type: integer
XmlText:
type: object

0 comments on commit 0a52da8

Please sign in to comment.