Skip to content

Commit

Permalink
Merge pull request #1767 from ballerina-platform/fix-default-value-is…
Browse files Browse the repository at this point in the history
…sues

Fix issues with default values in OpenAPI/Code generation
  • Loading branch information
TharmiganK authored Sep 5, 2024
2 parents 8d59a52 + 0228000 commit 26c4b84
Show file tree
Hide file tree
Showing 14 changed files with 766 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import static io.ballerina.compiler.syntax.tree.SyntaxKind.MAPPING_CONSTRUCTOR;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.NUMERIC_LITERAL;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.STRING_LITERAL;
import static io.ballerina.compiler.syntax.tree.SyntaxKind.UNARY_EXPRESSION;
import static io.ballerina.openapi.service.mapper.Constants.BALLERINA;
import static io.ballerina.openapi.service.mapper.Constants.EMPTY;
import static io.ballerina.openapi.service.mapper.Constants.HTTP;
Expand All @@ -112,7 +113,7 @@
public class MapperCommonUtils {

private static final SyntaxKind[] validExpressionKind = {STRING_LITERAL, NUMERIC_LITERAL, BOOLEAN_LITERAL,
LIST_CONSTRUCTOR, MAPPING_CONSTRUCTOR};
LIST_CONSTRUCTOR, MAPPING_CONSTRUCTOR, UNARY_EXPRESSION};

public static String generateRelativePath(ResourceFunction resourceFunction) {
StringBuilder relativePath = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,59 @@ public void testSanitizeOptionWithMixedCasedTypeName() {
}
}

@Test
public void testDefaultValueGenerationWithClient() {
String definitionPath = RES_DIR.resolve("default_value_generation.yaml").toString();
BallerinaCodeGenerator generator = new BallerinaCodeGenerator();
try {
String expectedClientContent = getStringFromGivenBalFile(
expectedDirPath, "default_value_generation_client.bal");
generator.generateClient(definitionPath, resourcePath.toString(), filter,
new ClientGeneratorOptions(false, true, false, false,
true, false));
if (Files.exists(resourcePath.resolve("client.bal"))) {
String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal");
generatedClient = (generatedClient.trim()).replaceAll("\\s+", "");
expectedClientContent = (expectedClientContent.trim()).replaceAll("\\s+", "");
Assert.assertTrue(generatedClient.contains(expectedClientContent));
} else {
Assert.fail("Client was not generated");
}
} catch (IOException | BallerinaOpenApiException |
OASTypeGenException | FormatterException e) {
Assert.fail("Error while generating the client: " + e.getMessage());
} finally {
deleteGeneratedFiles("client.bal");
}
}

@Test
public void testDefaultValueGenerationWithServiceContract() {
String definitionPath = RES_DIR.resolve("default_value_generation.yaml").toString();
BallerinaCodeGenerator generator = new BallerinaCodeGenerator();
try {
String serviceName = "default_value_generation";
String expectedServiceContractContent = getStringFromGivenBalFile(
expectedDirPath, "default_value_generation_service_contract.bal");
ServiceGeneratorOptions options = new ServiceGeneratorOptions(false, false,
true, false, true, false);
generator.generateService(definitionPath, serviceName, resourcePath.toString(), filter, options);
if (Files.exists(resourcePath.resolve("default_value_generation_service.bal"))) {
String generatedServiceContract = getStringFromGivenBalFile(resourcePath,
"default_value_generation_service.bal");
generatedServiceContract = (generatedServiceContract.trim()).replaceAll("\\s+", "");
expectedServiceContractContent = (expectedServiceContractContent.trim()).replaceAll("\\s+", "");
Assert.assertTrue(generatedServiceContract.contains(expectedServiceContractContent));
} else {
Assert.fail("Service contract was not generated");
}
} catch (IOException | BallerinaOpenApiException | FormatterException e) {
Assert.fail("Error while generating the service contract: " + e.getMessage());
} finally {
deleteGeneratedFiles("default_value_generation_service.bal");
}
}

private String getStringFromGivenBalFile(Path expectedServiceFile, String s) throws IOException {

Stream<String> expectedServiceLines = Files.lines(expectedServiceFile.resolve(s));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ public void testRestFieldInRecord() throws IOException {
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/record_rest_param.yaml");
}

@Test(description = "Test for record with default values")
public void testRecordWithDefaultValues() throws IOException {
Path ballerinaFilePath = RES_DIR.resolve("record/record_with_default_values.bal");
TestUtils.compareWithGeneratedFile(ballerinaFilePath, "record/record_with_default_values.yaml");
}

@AfterMethod
public void cleanUp() {
TestUtils.deleteDirectory(this.tempDir);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
openapi: 3.0.1
info:
title: PayloadV
version: 0.0.0
servers:
- url: "http://{server}:{port}/payloadV"
variables:
server:
default: localhost
port:
default: "8080"
paths:
/albums/{id}:
get:
operationId: getAlbumsId
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: q1
in: query
schema:
type: string
default: query1
- name: q2
in: query
schema:
type: integer
format: int64
default: -1
- name: X-HEADER
in: header
schema:
type: string
default: header1
responses:
"200":
description: Ok
content:
application/json:
schema:
$ref: "#/components/schemas/Album"
"400":
description: BadRequest
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorPayload"
components:
schemas:
Album:
required:
- author
- title
type: object
properties:
iid:
type: integer
format: int64
default: -1
title:
type: string
author:
type: string
genre:
allOf:
- $ref: "#/components/schemas/Genre"
default:
name: Unknown
description: Unknown
iid: -1
tags:
type: array
items:
type: string
default:
- tag1
- tag2
ratings:
type: array
items:
oneOf:
- type: string
- type: integer
format: int64
default:
- unrated
- 5
- 4
price:
type: number
format: double
default: 100.55
available:
type: boolean
default: true
additionalProperties: false
ErrorPayload:
required:
- message
- method
- path
- reason
- status
- timestamp
type: object
properties:
timestamp:
type: string
status:
type: integer
format: int64
reason:
type: string
message:
type: string
path:
type: string
method:
type: string
Genre:
required:
- description
- iid
- name
type: object
properties:
name:
type: string
description:
oneOf:
- type: string
- type: array
items:
type: string
iid:
type: integer
format: int64
additionalProperties: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import ballerina/http;

public type Genre record {|
string name;
string|string[] description;
int iid;
|};

public type Album record {|
int iid = -1;
string title;
string author;
Genre genre = {
name: "Unknown",
description: "Unknown",
iid: -1
};
string[] tags = ["tag1", "tag2"];
(string|int)[] ratings = ["unrated", 5, 4];
decimal price = 100.55;
boolean available = true;
|};

@http:ServiceConfig {
basePath: "/payloadV"
}
type AlbumService service object {
*http:ServiceContract;

resource function get albums/[string id](string q1 = "query1", int q2 = -1, @http:Header {name: "X-HEADER"} string h1 = "header1") returns Album;
};
118 changes: 118 additions & 0 deletions openapi-cli/src/test/resources/default_value_generation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
openapi: 3.0.1
info:
title: PayloadV
version: 0.0.0
servers:
- url: "http://{server}:{port}/payloadV"
variables:
server:
default: localhost
port:
default: "8080"
paths:
/albums/{id}:
get:
operationId: getAlbumsId
parameters:
- name: id
in: path
required: true
schema:
type: string
- name: q1
in: query
required: true
schema:
type: string
default: query1
- name: q2
in: query
schema:
type: integer
format: int64
default: -1
- name: X-HEADER
in: header
required: true
schema:
type: string
default: header1
responses:
"200":
description: Ok
content:
application/json:
schema:
$ref: "#/components/schemas/Album"
components:
schemas:
Album:
required:
- author
- title
type: object
properties:
iid:
type: integer
format: int64
default: -1
title:
type: string
author:
type: string
genre:
allOf:
- $ref: "#/components/schemas/Genre"
default:
name: Unknown
description: Unknown
iid: -1
tags:
type: array
items:
type: string
default:
- tag1
- tag2
ratings:
type: array
items:
oneOf:
- type: string
- type: integer
format: int64
default:
- unrated
- 5
- 4
price:
type: number
format: double
default: 100.55
available:
type: boolean
default: true
additionalProperties: false
Genre:
required:
- description
- iid
- name
type: object
properties:
name:
type: string
default: Unknown
description:
oneOf:
- type: string
- type: array
items:
type: string
default:
- Unknown
- Unknown
iid:
type: integer
format: int64
additionalProperties: false
Loading

0 comments on commit 26c4b84

Please sign in to comment.