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

Fix NPE when operation ID is missing for operations with complex path #1751

Merged
merged 4 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 @@ -108,10 +108,7 @@ public void generateClientAndService(String definitionPath, String serviceName,

List<GenSrcFile> sourceFiles = new ArrayList<>();
Path openAPIPath = Path.of(definitionPath);
// Normalize OpenAPI definition, in the client generation we suppose to terminate code generation when the
// absence of the operationId in operation. Therefor we enable client flag true as default code generation.
// if resource is enabled, we avoid checking operationId.
OpenAPI openAPIDef = GeneratorUtils.normalizeOpenAPI(openAPIPath, !options.isResource, options.isSanitizedOas);
OpenAPI openAPIDef = GeneratorUtils.getOpenAPIFromOpenAPIV3Parser(openAPIPath);
checkOpenAPIVersion(openAPIDef);
// Add typeHandler
TypeHandler.createInstance(openAPIDef, options.nullable);
Expand All @@ -129,14 +126,18 @@ public void generateClientAndService(String definitionPath, String serviceName,
outStream.println(path);
}
}
// Normalize OpenAPI definition, in the client generation we suppose to terminate code generation when the
// absence of the operationId in operation. Therefore, we enable client flag true as default code generation.
// if resource is enabled, we avoid checking operationId.
OpenAPI normalizedOpenAPI = GeneratorUtils.normalizeOpenAPI(openAPIDef, isResource, options.isSanitizedOas);
TharmiganK marked this conversation as resolved.
Show resolved Hide resolved
// Generate client.
// Generate ballerina client remote.
OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder();
OASClientConfig oasClientConfig = clientMetaDataBuilder
.withFilters(filter)
.withNullable(options.nullable)
.withPlugin(false)
.withOpenAPI(openAPIDef)
.withOpenAPI(normalizedOpenAPI)
.withResourceMode(isResource)
.withStatusCodeBinding(options.statusCodeBinding)
.withMock(options.isMock).build();
Expand All @@ -161,7 +162,7 @@ public void generateClientAndService(String definitionPath, String serviceName,

if (complexPaths.isEmpty()) {
OASServiceMetadata oasServiceMetadata = new OASServiceMetadata.Builder()
.withOpenAPI(openAPIDef)
.withOpenAPI(normalizedOpenAPI)
.withFilters(filter)
.withNullable(options.nullable)
.withGenerateServiceType(options.generateServiceType)
Expand Down Expand Up @@ -394,8 +395,7 @@ private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, Client
srcPackage = DEFAULT_CLIENT_PKG;
}
List<GenSrcFile> sourceFiles = new ArrayList<>();
// Normalize OpenAPI definition
OpenAPI openAPIDef = GeneratorUtils.normalizeOpenAPI(openAPI, !options.isResource, options.isSanitizedOas);
OpenAPI openAPIDef = GeneratorUtils.getOpenAPIFromOpenAPIV3Parser(openAPI);
checkOpenAPIVersion(openAPIDef);
// Validate the service generation
List<String> complexPaths = GeneratorUtils.getComplexPaths(openAPIDef);
Expand All @@ -408,13 +408,15 @@ private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, Client
}
isResource = false;
}
// Validate and Normalize OpenAPI definition
OpenAPI normalizedOpenAPI = GeneratorUtils.normalizeOpenAPI(openAPIDef, !isResource, options.isSanitizedOas);
// Generate ballerina service and resources.
OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder();
OASClientConfig oasClientConfig = clientMetaDataBuilder
.withFilters(filter)
.withNullable(options.nullable)
.withPlugin(false)
.withOpenAPI(openAPIDef)
.withOpenAPI(normalizedOpenAPI)
.withResourceMode(isResource)
.withStatusCodeBinding(options.statusCodeBinding)
.withMock(options.isMock)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,18 @@ public void testForInvalidDefinition() throws IOException, BallerinaOpenApiExcep
generator.generateClient(definitionPath, "", filter, defaultClientOptions);
}

@Test(description = "Test client resource generation with complex path and missing operation ids",
expectedExceptions = BallerinaOpenApiException.class,
expectedExceptionsMessageRegExp = "the configured generation mode requires operation ids for all " +
"operations: \\ROperationId is missing in the resource path: .*")
public void testMissingOperationIdWithComplexPath() throws IOException, BallerinaOpenApiException,
FormatterException, OASTypeGenException {
String definitionPath = RES_DIR.resolve("petstore_with_complex_path_and_without_operation_id.yaml").toString();
BallerinaCodeGenerator generator = new BallerinaCodeGenerator();
ClientGeneratorOptions options = new ClientGeneratorOptions(false, true, false, false, false, false);
generator.generateClient(definitionPath, "", filter, options);
}

@Test(description = "Functionality tests when swagger 1.2 contract is given as input",
expectedExceptions = BallerinaOpenApiException.class,
expectedExceptionsMessageRegExp = "Provided OpenAPI contract version is not supported in the tool. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class RemoteFunctionNameValidationTests {
expectedExceptions = BallerinaOpenApiException.class,
expectedExceptionsMessageRegExp = "the configured generation mode requires operation ids for all " +
"operations: \\ROperationId is missing in the resource path: .*", enabled = false)
public void testMissionOperationId() throws IOException, BallerinaOpenApiException, ClientException {
public void testMissingOperationId() throws IOException, BallerinaOpenApiException, ClientException {
Path definitionPath = RESDIR.resolve("petstore_without_operation_id.yaml");
OpenAPI openAPI = GeneratorUtils.normalizeOpenAPI(definitionPath, true, false);
OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: OpenApi Petstore
license:
name: MIT
servers:
- url: http://localhost:9090/petstore/v1
paths:
/pet:
post:
description: Add pet
operationId: createPet
responses:
'200':
description: Expected response to a valid request
requestBody:
description: test
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
get:
description: Get pet
responses:
'200':
description: Expected response to a valid request


/pets(id='{petId}'):
get:
summary: Info for a specific pet
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
delete:
description: deletes a single pet based on the ID supplied
parameters:
- name: petId
in: path
description: ID of pet to delete
required: true
schema:
type: integer
responses:
"202":
description: pet deleted
/pets/{petId}/Image:
get:
summary: Get pet image
tags:
- pets
parameters:
- name: petId
in: path
description: ID of pet to delete
required: true
schema:
type: integer
responses:
'200':
description: An paged array of pets

components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,19 @@ private static boolean isConstraintExists(Schema<?> propertyValue) {
public static OpenAPI normalizeOpenAPI(Path openAPIPath, boolean validateOpIds, boolean isSanitized) throws
IOException, BallerinaOpenApiException {
OpenAPI openAPI = getOpenAPIFromOpenAPIV3Parser(openAPIPath);
return normalizeOpenAPI(openAPI, validateOpIds, isSanitized);
}

/**
* Normalized OpenAPI specification with adding proper naming to schema.
*
* @param openAPI - openAPI specification
* @return - openAPI specification
* @throws IOException
* @throws BallerinaOpenApiException
*/
public static OpenAPI normalizeOpenAPI(OpenAPI openAPI, boolean validateOpIds, boolean isSanitized)
throws BallerinaOpenApiException {
io.swagger.v3.oas.models.Paths openAPIPaths = openAPI.getPaths();
if (validateOpIds) {
validateOperationIds(openAPIPaths.entrySet());
Expand Down
Loading