Skip to content

Commit

Permalink
Merge pull request #1714 from lnash94/mock-client
Browse files Browse the repository at this point in the history
[master] Add Mock Client Generation Support to OpenAPI Tool
  • Loading branch information
lnash94 authored Jun 11, 2024
2 parents 69bed2c + b539504 commit 0779c3f
Show file tree
Hide file tree
Showing 32 changed files with 7,448 additions and 110 deletions.
6 changes: 3 additions & 3 deletions module-ballerina-openapi/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
org= "ballerina"
name= "openapi"
version= "1.9.0"
version= "@toml.version@"

[[platform.java17.dependency]]
path = "../openapi-validator/build/libs/openapi-validator-1.9.0-SNAPSHOT.jar"
path = "../openapi-validator/build/libs/openapi-validator-@project.version@.jar"
groupId = "ballerina"
artifactId = "openapi"
version = "1.9.0-SNAPSHOT"
version = "@project.version@"
4 changes: 2 additions & 2 deletions module-ballerina-openapi/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id = "openapi-tools"
class = "io.ballerina.openapi.validator.OpenAPIValidatorPlugin"

[[dependency]]
path = "../openapi-validator/build/libs/openapi-validator-1.9.0-SNAPSHOT.jar"
path = "../openapi-validator/build/libs/openapi-validator-@project.version@.jar"
groupId = "ballerina"
artifactId = "openapi"
version = "1.9.0-SNAPSHOT"
version = "@project.version@."
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class Constants {
public static final String CLIENT = "client";
public static final String CACHE_FILE = "openapi-cache.txt";
public static final String STATUS_CODE_BINDING = "statusCodeBinding";
public static final String MOCK = "mock";

/**
* Enum class for containing diagnostic messages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import io.ballerina.openapi.core.generators.client.BallerinaClientGeneratorWithStatusCodeBinding;
import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic;
import io.ballerina.openapi.core.generators.client.exception.ClientException;
import io.ballerina.openapi.core.generators.client.mock.AdvanceMockClientGenerator;
import io.ballerina.openapi.core.generators.client.mock.BallerinaMockClientGenerator;
import io.ballerina.openapi.core.generators.client.model.OASClientConfig;
import io.ballerina.openapi.core.generators.common.TypeHandler;
import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException;
Expand Down Expand Up @@ -77,6 +79,7 @@
import static io.ballerina.openapi.bal.tool.Constants.CLIENT;
import static io.ballerina.openapi.bal.tool.Constants.CLIENT_METHODS;
import static io.ballerina.openapi.bal.tool.Constants.LICENSE;
import static io.ballerina.openapi.bal.tool.Constants.MOCK;
import static io.ballerina.openapi.bal.tool.Constants.MODE;
import static io.ballerina.openapi.bal.tool.Constants.NULLABLE;
import static io.ballerina.openapi.bal.tool.Constants.OPERATIONS;
Expand Down Expand Up @@ -296,6 +299,9 @@ public static ImmutablePair<OASClientConfig, OASServiceMetadata> extractOptionDe
case STATUS_CODE_BINDING:
clientMetaDataBuilder.withStatusCodeBinding(value.contains(TRUE));
break;
case MOCK:
clientMetaDataBuilder.withMock(value.contains(TRUE));
break;
default:
break;
}
Expand Down Expand Up @@ -330,8 +336,7 @@ private void generateClient(ToolContext toolContext, ImmutablePair<OASClientConf
}
}
OASClientConfig clientConfig = codeGeneratorConfig.getLeft();
List<GenSrcFile> sources = generateClientFiles(clientConfig, toolContext, location,
codeGeneratorConfig.getLeft().isStatusCodeBinding());
List<GenSrcFile> sources = generateClientFiles(clientConfig, toolContext, location);
Path outputPath = toolContext.outputPath();
writeGeneratedSources(sources, outputPath);
// Update the cache file
Expand Down Expand Up @@ -453,7 +458,8 @@ private static String getHashValue(OASClientConfig clientConfig, String targetPa
.append(clientConfig.isResourceMode())
.append(clientConfig.getLicense())
.append(clientConfig.isNullable())
.append(clientConfig.isStatusCodeBinding());
.append(clientConfig.isStatusCodeBinding())
.append(clientConfig.isMock());
List<String> tags = clientConfig.getFilter().getTags();
tags.sort(String.CASE_INSENSITIVE_ORDER);
for (String str : tags) {
Expand All @@ -472,15 +478,15 @@ private static String getHashValue(OASClientConfig clientConfig, String targetPa
* This will return list of (client.bal, util.bal, types.bal) {@code GenSrcFile}.
*/
private static List<GenSrcFile> generateClientFiles(OASClientConfig oasClientConfig, ToolContext toolContext,
Location location, boolean statusCodeBinding) throws
Location location) throws
BallerinaOpenApiException, IOException, FormatterException, ClientException {

List<GenSrcFile> sourceFiles = new ArrayList<>();

// Generate ballerina client files.
TypeHandler.createInstance(oasClientConfig.getOpenAPI(), oasClientConfig.isNullable());
String licenseContent = oasClientConfig.getLicense();
BallerinaClientGenerator ballerinaClientGenerator = getClientGenerator(oasClientConfig, statusCodeBinding);
BallerinaClientGenerator ballerinaClientGenerator = getClientGenerator(oasClientConfig);
String mainContent = Formatter.format(ballerinaClientGenerator.generateSyntaxTree()).toString();

List<ClientDiagnostic> clientDiagnostic = ballerinaClientGenerator.getDiagnostics();
Expand Down Expand Up @@ -526,12 +532,20 @@ private static List<GenSrcFile> generateClientFiles(OASClientConfig oasClientCon
return sourceFiles;
}

private static BallerinaClientGenerator getClientGenerator(OASClientConfig oasClientConfig,
boolean statusCodeBinding) {
if (!statusCodeBinding) {
return new BallerinaClientGenerator(oasClientConfig);
private static BallerinaClientGenerator getClientGenerator(OASClientConfig oasClientConfig) {
boolean statusCodeBinding = oasClientConfig.isStatusCodeBinding();
boolean isMock = oasClientConfig.isMock();

if (statusCodeBinding && isMock) {
return new AdvanceMockClientGenerator(oasClientConfig);
}
if (statusCodeBinding) {
return new BallerinaClientGeneratorWithStatusCodeBinding(oasClientConfig);
}
if (isMock) {
return new BallerinaMockClientGenerator(oasClientConfig);
}
return new BallerinaClientGeneratorWithStatusCodeBinding(oasClientConfig);
return new BallerinaClientGenerator(oasClientConfig);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
},
"statusCodeBinding": {
"type": "boolean"
},
"mock": {
"type": "boolean"
}
},
"additionalProperties": false
Expand Down
8 changes: 7 additions & 1 deletion openapi-cli/src/main/java/io/ballerina/openapi/cmd/Add.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ private CmdOptions collectCLIOptions() {
.withOperations(getOperations())
.withTags(getTags())
.withLicensePath(baseCmd.licenseFilePath)
.withStatusCodeBinding(baseCmd.statusCodeBinding).build();
.withStatusCodeBinding(baseCmd.statusCodeBinding)
.withMock(baseCmd.mock)
.build();
}

/**
Expand Down Expand Up @@ -252,6 +254,10 @@ private NodeList<DocumentMemberDeclarationNode> populateOpenAPITomlConfig(CmdOpt
moduleMembers = moduleMembers.add(SampleNodeGenerator.createBooleanKV("options.statusCodeBinding",
optionsBuilder.getStatusCodeBinding(), null));
}
if (optionsBuilder.getMock()) {
moduleMembers = moduleMembers.add(SampleNodeGenerator.createBooleanKV("options.mock",
optionsBuilder.getMock(), null));
}
moduleMembers = CmdUtils.addNewLine(moduleMembers, 2);
return moduleMembers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import io.ballerina.openapi.core.generators.client.BallerinaTestGenerator;
import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic;
import io.ballerina.openapi.core.generators.client.exception.ClientException;
import io.ballerina.openapi.core.generators.client.mock.AdvanceMockClientGenerator;
import io.ballerina.openapi.core.generators.client.mock.BallerinaMockClientGenerator;
import io.ballerina.openapi.core.generators.client.model.OASClientConfig;
import io.ballerina.openapi.core.generators.common.GeneratorUtils;
import io.ballerina.openapi.core.generators.common.TypeHandler;
Expand Down Expand Up @@ -98,7 +100,7 @@ public class BallerinaCodeGenerator {
public void generateClientAndService(String definitionPath, String serviceName,
String outPath, Filter filter, boolean nullable,
boolean isResource, boolean generateServiceType,
boolean generateWithoutDataBinding, boolean statusCodeBinding)
boolean generateWithoutDataBinding, boolean statusCodeBinding, boolean isMock)
throws IOException, FormatterException, BallerinaOpenApiException,
OASTypeGenException, ClientException {
Path srcPath = Paths.get(outPath);
Expand Down Expand Up @@ -134,9 +136,11 @@ public void generateClientAndService(String definitionPath, String serviceName,
.withNullable(nullable)
.withPlugin(false)
.withOpenAPI(openAPIDef)
.withResourceMode(isResource).build();
.withResourceMode(isResource)
.withStatusCodeBinding(statusCodeBinding)
.withMock(isMock).build();

BallerinaClientGenerator clientGenerator = getBallerinaClientGenerator(oasClientConfig, statusCodeBinding);
BallerinaClientGenerator clientGenerator = getBallerinaClientGenerator(oasClientConfig);
String clientContent = Formatter.format(clientGenerator.generateSyntaxTree()).toSourceCode();

//Update type definition list with auth related type definitions
Expand Down Expand Up @@ -230,13 +234,14 @@ public static <T> Predicate<T> distinctByKey(
* @throws BallerinaOpenApiException when code generator fails
*/
public void generateClient(String definitionPath, String outPath, Filter filter, boolean nullable,
boolean isResource, boolean statusCodeBinding)
boolean isResource, boolean statusCodeBinding, boolean isMock)
throws IOException, FormatterException, BallerinaOpenApiException, OASTypeGenException {
Path srcPath = Paths.get(outPath);
Path implPath = getImplPath(srcPackage, srcPath);
List<GenSrcFile> genFiles = null;
try {
genFiles = generateClientFiles(Paths.get(definitionPath), filter, nullable, isResource, statusCodeBinding);
genFiles = generateClientFiles(Paths.get(definitionPath), filter, nullable, isResource, statusCodeBinding,
isMock);
if (!genFiles.isEmpty()) {
writeGeneratedSources(genFiles, srcPath, implPath, GEN_CLIENT);
}
Expand Down Expand Up @@ -357,7 +362,7 @@ private void writeGeneratedSources(List<GenSrcFile> sources, Path srcPath, Path
* @throws IOException when code generation with specified templates fails
*/
private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, boolean nullable, boolean isResource,
boolean statusCodeBinding)
boolean statusCodeBinding, boolean isMock)
throws IOException, BallerinaOpenApiException, FormatterException, ClientException {
if (srcPackage == null || srcPackage.isEmpty()) {
srcPackage = DEFAULT_CLIENT_PKG;
Expand Down Expand Up @@ -385,12 +390,12 @@ private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, boolea
.withOpenAPI(openAPIDef)
.withResourceMode(isResource)
.withStatusCodeBinding(statusCodeBinding)
.withMock(isMock)
.build();
//Take default DO NOT modify
licenseHeader = licenseHeader.isBlank() ? DO_NOT_MODIFY_FILE_HEADER : licenseHeader;
TypeHandler.createInstance(openAPIDef, nullable);
BallerinaClientGenerator clientGenerator = getBallerinaClientGenerator(oasClientConfig,
statusCodeBinding);
BallerinaClientGenerator clientGenerator = getBallerinaClientGenerator(oasClientConfig);
String mainContent = Formatter.format(clientGenerator.generateSyntaxTree()).toSourceCode();
//Update type definition list with auth related type definitions
List<TypeDefinitionNode> authNodes = clientGenerator.getBallerinaAuthConfigGenerator()
Expand Down Expand Up @@ -439,12 +444,20 @@ private List<GenSrcFile> generateClientFiles(Path openAPI, Filter filter, boolea
return sourceFiles;
}

private static BallerinaClientGenerator getBallerinaClientGenerator(OASClientConfig oasClientConfig,
boolean statusCodeBinding) {
if (!statusCodeBinding) {
return new BallerinaClientGenerator(oasClientConfig);
private static BallerinaClientGenerator getBallerinaClientGenerator(OASClientConfig oasClientConfig) {
boolean statusCodeBinding = oasClientConfig.isStatusCodeBinding();
boolean isMock = oasClientConfig.isMock();

if (statusCodeBinding && isMock) {
return new AdvanceMockClientGenerator(oasClientConfig);
}
if (statusCodeBinding) {
return new BallerinaClientGeneratorWithStatusCodeBinding(oasClientConfig);
}
if (isMock) {
return new BallerinaMockClientGenerator(oasClientConfig);
}
return new BallerinaClientGeneratorWithStatusCodeBinding(oasClientConfig);
return new BallerinaClientGenerator(oasClientConfig);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,9 @@ public class BaseCmd {
@CommandLine.Option(names = {"--status-code-binding"}, description = "Generate the client methods with " +
"status code response binding")
public boolean statusCodeBinding;


@CommandLine.Option(names = {"--mock"}, hidden = true,
description = "Generate mock client with given response example")
public boolean mock;
}
11 changes: 11 additions & 0 deletions openapi-cli/src/main/java/io/ballerina/openapi/cmd/CmdOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class CmdOptions {
private final boolean nullable;
private final String licensePath;
private boolean statusCodeBinding;
private boolean mock;

private CmdOptions(CmdOptionsBuilder builder) {
this.input = builder.input;
Expand All @@ -48,6 +49,7 @@ private CmdOptions(CmdOptionsBuilder builder) {
this.nullable = builder.nullable;
this.licensePath = builder.licensePath;
this.statusCodeBinding = builder.statusCodeBinding;
this.mock = builder.mock;
}

public String getInput() {
Expand Down Expand Up @@ -95,6 +97,10 @@ public boolean getStatusCodeBinding() {
return statusCodeBinding;
}

public boolean getMock() {
return mock;
}

/**
* CMD options builder class.
*/
Expand All @@ -110,6 +116,7 @@ public static class CmdOptionsBuilder {
private String input;
private String licensePath;
private boolean statusCodeBinding;
private boolean mock;

public CmdOptionsBuilder withOutputModule(String outputModule) {
this.outputModule = outputModule;
Expand Down Expand Up @@ -166,6 +173,10 @@ public CmdOptionsBuilder withStatusCodeBinding(boolean statusCodeBinding) {
return this;
}

public CmdOptionsBuilder withMock(boolean mock) {
this.mock = mock;
return this;
}
public CmdOptions build() {
return new CmdOptions(this);
}
Expand Down
16 changes: 12 additions & 4 deletions openapi-cli/src/main/java/io/ballerina/openapi/cmd/OpenApiCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public class OpenApiCmd implements BLauncherCmd {
description = "Generate service without data binding")
private boolean generateWithoutDataBinding;


@CommandLine.Parameters
private List<String> argList;

Expand Down Expand Up @@ -405,7 +406,7 @@ private void generatesClientFile(BallerinaCodeGenerator generator, Path resource
boolean resourceMode, boolean statusCodeBinding) {
try {
generator.generateClient(resourcePath.toString(), targetOutputPath.toString(), filter, baseCmd.nullable,
resourceMode, statusCodeBinding);
resourceMode, statusCodeBinding, baseCmd.mock);
} catch (IOException | FormatterException | BallerinaOpenApiException |
OASTypeGenException e) {
if (e.getLocalizedMessage() != null) {
Expand All @@ -429,8 +430,15 @@ private void generateServiceFile(BallerinaCodeGenerator generator, String servic

try {
assert resourcePath != null;
generator.generateService(resourcePath.toString(), serviceName, targetOutputPath.toString(), filter,
baseCmd.nullable, generateServiceType, generateWithoutDataBinding);
if (baseCmd.mock) {
//Brining some format for info message from ballerina info ex: `ballerina: `
outStream.println("openapi: Mock server generation is currently not supported via the ballerina " +
"openapi tool.");
exitError(this.exitWhenFinish);
} else {
generator.generateService(resourcePath.toString(), serviceName, targetOutputPath.toString(), filter,
baseCmd.nullable, generateServiceType, generateWithoutDataBinding);
}
} catch (IOException | FormatterException | BallerinaOpenApiException e) {
outStream.println("Error occurred when generating service for openAPI contract at " + baseCmd.inputPath +
"." + e.getMessage() + ".");
Expand All @@ -450,7 +458,7 @@ private void generateBothFiles(BallerinaCodeGenerator generator, String fileName
assert resourcePath != null;
generator.generateClientAndService(resourcePath.toString(), fileName, targetOutputPath.toString(), filter,
baseCmd.nullable, generateClientResourceFunctions, generateServiceType, generateWithoutDataBinding,
statusCodeBinding);
statusCodeBinding, baseCmd.mock);
} catch (BallerinaOpenApiException e) {
outStream.println(e.getMessage());
exitError(this.exitWhenFinish);
Expand Down
Loading

0 comments on commit 0779c3f

Please sign in to comment.