Skip to content

Commit

Permalink
Merge pull request #1606 from lnash94/complex_path
Browse files Browse the repository at this point in the history
[master] Fix for service and client generation when OAS include parameterized path segmentes in paths
  • Loading branch information
lnash94 authored Mar 6, 2024
2 parents 20bd75c + b42865e commit b49a2fd
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public class BallerinaCodeGenerator {
private String licenseHeader = "";
private boolean includeTestFiles;

private static final PrintStream outStream = System.err;
private static final PrintStream outStream = System.out;

/**
* Generates ballerina source for provided Open API Definition in {@code definitionPath}.
Expand All @@ -106,28 +106,17 @@ public void generateClientAndService(String definitionPath, String serviceName,
OpenAPI openAPIDef = GeneratorUtils.normalizeOpenAPI(openAPIPath, !isResource);
checkOpenAPIVersion(openAPIDef);
// Generate service
String concatTitle = serviceName.toLowerCase(Locale.ENGLISH);
String srcFile = concatTitle + "_service.bal";
OASServiceMetadata oasServiceMetadata = new OASServiceMetadata.Builder()
.withOpenAPI(openAPIDef)
.withFilters(filter)
.withNullable(nullable)
.withGenerateServiceType(generateServiceType)
.withGenerateWithoutDataBinding(generateWithoutDataBinding)
.build();
BallerinaServiceGenerator serviceGenerator = new BallerinaServiceGenerator(oasServiceMetadata);
String serviceContent = Formatter.format
(serviceGenerator.generateSyntaxTree()).toSourceCode();
sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage, srcFile,
(licenseHeader.isBlank() ? DEFAULT_FILE_HEADER : licenseHeader) + serviceContent));

if (generateServiceType) {
BallerinaServiceObjectGenerator ballerinaServiceObjectGenerator = new
BallerinaServiceObjectGenerator(serviceGenerator.getFunctionList());
String serviceType = Formatter.format(ballerinaServiceObjectGenerator.generateSyntaxTree()).toSourceCode();
sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage,
"service_type.bal", (licenseHeader.isBlank() ? DO_NOT_MODIFY_FILE_HEADER :
licenseHeader) + serviceType));
String serviceTitle = serviceName.toLowerCase(Locale.ENGLISH);
String srcFile = String.format("%s_service.bal", serviceTitle);
List<String> complexPaths = GeneratorUtils.getComplexPaths(openAPIDef);
if (!complexPaths.isEmpty()) {
isResource = false;
outStream.println("WARNING: remote function(s) will be generated for client and the service" +
" generation can not be proceed due to the given openapi definition contains following" +
" complex path(s):");
for (String path: complexPaths) {
outStream.println(path);
}
}
// Generate client.
// Generate ballerina client remote.
Expand All @@ -154,10 +143,35 @@ public void generateClientAndService(String definitionPath, String serviceName,
//Update type definition list
List<TypeDefinitionNode> preGeneratedTypeDefNodes = new ArrayList<>(
clientGenerator.getBallerinaAuthConfigGenerator().getAuthRelatedTypeDefinitionNodes());
List<TypeDefinitionNode> typeInclusionRecords = serviceGenerator.getTypeInclusionRecords();
List<TypeDefinitionNode> typeDefinitionNodeList = clientGenerator.getTypeDefinitionNodeList();
preGeneratedTypeDefNodes.addAll(typeInclusionRecords);
preGeneratedTypeDefNodes.addAll(typeDefinitionNodeList);
String serviceContent = "";
if (complexPaths.isEmpty()) {
OASServiceMetadata oasServiceMetadata = new OASServiceMetadata.Builder()
.withOpenAPI(openAPIDef)
.withFilters(filter)
.withNullable(nullable)
.withGenerateServiceType(generateServiceType)
.withGenerateWithoutDataBinding(generateWithoutDataBinding)
.build();
BallerinaServiceGenerator serviceGenerator = new BallerinaServiceGenerator(oasServiceMetadata);
serviceContent = Formatter.format
(serviceGenerator.generateSyntaxTree()).toSourceCode();
sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage, srcFile,
(licenseHeader.isBlank() ? DEFAULT_FILE_HEADER : licenseHeader) + serviceContent));

if (generateServiceType) {
BallerinaServiceObjectGenerator ballerinaServiceObjectGenerator = new
BallerinaServiceObjectGenerator(serviceGenerator.getFunctionList());
String serviceType = Formatter.format(ballerinaServiceObjectGenerator.generateSyntaxTree()).
toSourceCode();
sourceFiles.add(new GenSrcFile(GenSrcFile.GenFileType.GEN_SRC, srcPackage,
"service_type.bal", (licenseHeader.isBlank() ? DO_NOT_MODIFY_FILE_HEADER :
licenseHeader) + serviceType));
}
List<TypeDefinitionNode> typeInclusionRecords = serviceGenerator.getTypeInclusionRecords();
preGeneratedTypeDefNodes.addAll(typeInclusionRecords);
}

// Generate ballerina types.
// Generate ballerina records to represent schemas.
Expand Down Expand Up @@ -222,7 +236,9 @@ public void generateClient(String definitionPath, String outPath, Filter filter,
Path srcPath = Paths.get(outPath);
Path implPath = CodegenUtils.getImplPath(srcPackage, srcPath);
List<GenSrcFile> genFiles = generateClientFiles(Paths.get(definitionPath), filter, nullable, isResource);
writeGeneratedSources(genFiles, srcPath, implPath, GEN_CLIENT);
if (!genFiles.isEmpty()) {
writeGeneratedSources(genFiles, srcPath, implPath, GEN_CLIENT);
}
}

/**
Expand All @@ -244,6 +260,9 @@ public void generateService(String definitionPath, String serviceName, String ou
Path implPath = CodegenUtils.getImplPath(srcPackage, srcPath);
List<GenSrcFile> genFiles = generateBallerinaService(Paths.get(definitionPath), serviceName,
filter, nullable, generateServiceType, generateWithoutDataBinding);
if (genFiles.isEmpty()) {
return;
}
writeGeneratedSources(genFiles, srcPath, implPath, GEN_SERVICE);
}

Expand Down Expand Up @@ -339,6 +358,16 @@ private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, boolea
// Normalize OpenAPI definition
OpenAPI openAPIDef = GeneratorUtils.normalizeOpenAPI(openAPI, !isResource);
checkOpenAPIVersion(openAPIDef);
// Validate the service generation
List<String> complexPaths = GeneratorUtils.getComplexPaths(openAPIDef);
if (!complexPaths.isEmpty()) {
outStream.println("WARNING: remote function(s) will be generated for client due to the given openapi " +
"definition contains following complex path(s):");
for (String path: complexPaths) {
outStream.println(path);
}
isResource = false;
}
// Generate ballerina service and resources.
OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder();
OASClientConfig oasClientConfig = clientMetaDataBuilder
Expand Down Expand Up @@ -417,7 +446,16 @@ public List<GenSrcFile> generateBallerinaService(Path openAPI, String serviceNam
} else {
openAPIDef.getInfo().setTitle(serviceName);
}

// Validate the service generation
List<String> complexPaths = GeneratorUtils.getComplexPaths(openAPIDef);
if (!complexPaths.isEmpty()) {
outStream.println("service generation can not be done due to the given openapi definition contains" +
" following complex path(s):");
for (String path: complexPaths) {
outStream.println(path);
}
return new ArrayList<>();
}
List<GenSrcFile> sourceFiles = new ArrayList<>();
String concatTitle = serviceName == null ?
openAPIDef.getInfo().getTitle().toLowerCase(Locale.ENGLISH) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@
import org.testng.annotations.Test;
import picocli.CommandLine;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -43,10 +45,14 @@
public class OpenAPICmdTest extends OpenAPICommandTest {

private static final String LINE_SEPARATOR = System.lineSeparator();
private final PrintStream standardOut = System.out;
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();


@BeforeTest(description = "This will create a new ballerina project for testing below scenarios.")
public void setupBallerinaProject() throws IOException {
super.setup();
System.setOut(new PrintStream(outputStream));
}

@Test(description = "Test openapi command with help flag")
Expand Down Expand Up @@ -650,9 +656,91 @@ public void getRelativePath() {
equals("..\\..\\dir2\\dir3\\dir4\\test.txt"));
}

@Test(description = "service generation for the parameterized path in OAS")
public void testForComplexPathInService() {
Path yamlContract = resourceDir.resolve(Paths.get("complexPath.yaml"));
String[] args = {"--input", yamlContract.toString(), "-o", this.tmpDir.toString(), "--mode", "service"};
OpenApiCmd cmd = new OpenApiCmd(standardOut, tmpDir, false);
new CommandLine(cmd).parseArgs(args);
cmd.execute();
String expectedOutput = "service generation can not be done due to the given" +
" openapi definition contains following complex path(s):" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyFrom" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}.{sheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/payroll/v1/workers/{associateoid}/organizational-pay-statements/{payStatementId}/images/" +
"{imageId}.{imageExtension}" + System.lineSeparator() +
"/v3/ClientGroups/GetClientGroupByUserDefinedIdentifier(UserDefinedIdentifier=" +
"'{userDefinedIdentifier}')" + System.lineSeparator() +
"/companies({company_id})/items({item_id})";

Assert.assertTrue(outputStream.toString().contains(expectedOutput));
}

@Test(description = "service type generation for the parameterized path in OAS")
public void testForComplexPathInServiceType() {
Path yamlContract = resourceDir.resolve(Paths.get("complexPath.yaml"));
String[] args = {"--input", yamlContract.toString(), "-o", this.tmpDir.toString(), "--mode", "service",
"--with-service-type"};
OpenApiCmd cmd = new OpenApiCmd(standardOut, tmpDir, false);
new CommandLine(cmd).parseArgs(args);
cmd.execute();
Assert.assertTrue(outputStream.toString().contains("service generation can not be done due to the " +
"given openapi definition contains following complex path(s):" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyFrom" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}.{sheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/payroll/v1/workers/{associateoid}/organizational-pay-statements/{payStatementId}/images/" +
"{imageId}.{imageExtension}" + System.lineSeparator() +
"/v3/ClientGroups/GetClientGroupByUserDefinedIdentifier(UserDefinedIdentifier='" +
"{userDefinedIdentifier}')" + System.lineSeparator() +
"/companies({company_id})/items({item_id})"));
}

@Test(description = "client generation for the parameterized path in OAS")
public void testForComplexPathInClient() {
Path yamlContract = resourceDir.resolve(Paths.get("complexPath.yaml"));
String[] args = {"--input", yamlContract.toString(), "-o", this.tmpDir.toString(), "--mode", "client"};
OpenApiCmd cmd = new OpenApiCmd(standardOut, tmpDir, false);
new CommandLine(cmd).parseArgs(args);
cmd.execute();
Assert.assertTrue(outputStream.toString().contains("WARNING: remote function(s) will be generated for client " +
"due to the given openapi definition contains following complex path(s):" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyFrom" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}.{sheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/payroll/v1/workers/{associateoid}/organizational-pay-statements/{payStatementId}/images/" +
"{imageId}.{imageExtension}" + System.lineSeparator() +
"/v3/ClientGroups/GetClientGroupByUserDefinedIdentifier(UserDefinedIdentifier=" +
"'{userDefinedIdentifier}')" + System.lineSeparator() +
"/companies({company_id})/items({item_id})" + System.lineSeparator() +
"Client generated successfully."));
}

@Test(description = "both client and service generation for the parameterized path in OAS")
public void testForComplexPathInBothClientAndService() {
Path yamlContract = resourceDir.resolve(Paths.get("complexPath.yaml"));
String[] args = {"--input", yamlContract.toString(), "-o", this.tmpDir.toString()};
OpenApiCmd cmd = new OpenApiCmd(standardOut, tmpDir, false);
new CommandLine(cmd).parseArgs(args);
cmd.execute();
Assert.assertTrue(outputStream.toString().contains("WARNING: remote function(s) will be generated for client" +
" and the service generation can not be proceed due to the given openapi definition contains" +
" following complex path(s):" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyFrom" + System.lineSeparator() +
"/v4/spreadsheets/{spreadsheetId}.{sheetId}/sheets/{sheetId}:copyTo" + System.lineSeparator() +
"/payroll/v1/workers/{associateoid}/organizational-pay-statements/{payStatementId}/images/" +
"{imageId}.{imageExtension}" + System.lineSeparator() +
"/v3/ClientGroups/GetClientGroupByUserDefinedIdentifier(UserDefinedIdentifier=" +
"'{userDefinedIdentifier}')" + System.lineSeparator() +
"/companies({company_id})/items({item_id})" + System.lineSeparator() +
"Following files were created."));
}

@AfterTest
public void clean() {
System.setErr(null);
System.setOut(null);
System.setOut(standardOut);
}
}
Loading

0 comments on commit b49a2fd

Please sign in to comment.