diff --git a/module-ballerina-openapi/Ballerina.toml b/module-ballerina-openapi/Ballerina.toml index 3f7db9168..49e3f3887 100644 --- a/module-ballerina-openapi/Ballerina.toml +++ b/module-ballerina-openapi/Ballerina.toml @@ -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@" diff --git a/module-ballerina-openapi/CompilerPlugin.toml b/module-ballerina-openapi/CompilerPlugin.toml index 0e743c2c6..c4c4205c0 100644 --- a/module-ballerina-openapi/CompilerPlugin.toml +++ b/module-ballerina-openapi/CompilerPlugin.toml @@ -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@." diff --git a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java index a94ffcb6f..f00eec2e3 100644 --- a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java +++ b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/Constants.java @@ -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. diff --git a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPICodeGeneratorTool.java b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPICodeGeneratorTool.java index 9e10a7ab1..0f6650655 100644 --- a/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPICodeGeneratorTool.java +++ b/openapi-bal-task-plugin/src/main/java/io/ballerina/openapi/bal/tool/OpenAPICodeGeneratorTool.java @@ -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; @@ -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; @@ -296,6 +299,9 @@ public static ImmutablePair extractOptionDe case STATUS_CODE_BINDING: clientMetaDataBuilder.withStatusCodeBinding(value.contains(TRUE)); break; + case MOCK: + clientMetaDataBuilder.withMock(value.contains(TRUE)); + break; default: break; } @@ -330,8 +336,7 @@ private void generateClient(ToolContext toolContext, ImmutablePair sources = generateClientFiles(clientConfig, toolContext, location, - codeGeneratorConfig.getLeft().isStatusCodeBinding()); + List sources = generateClientFiles(clientConfig, toolContext, location); Path outputPath = toolContext.outputPath(); writeGeneratedSources(sources, outputPath); // Update the cache file @@ -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 tags = clientConfig.getFilter().getTags(); tags.sort(String.CASE_INSENSITIVE_ORDER); for (String str : tags) { @@ -472,7 +478,7 @@ private static String getHashValue(OASClientConfig clientConfig, String targetPa * This will return list of (client.bal, util.bal, types.bal) {@code GenSrcFile}. */ private static List generateClientFiles(OASClientConfig oasClientConfig, ToolContext toolContext, - Location location, boolean statusCodeBinding) throws + Location location) throws BallerinaOpenApiException, IOException, FormatterException, ClientException { List sourceFiles = new ArrayList<>(); @@ -480,7 +486,7 @@ private static List generateClientFiles(OASClientConfig oasClientCon // 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 = ballerinaClientGenerator.getDiagnostics(); @@ -526,12 +532,20 @@ private static List 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); } /** diff --git a/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json b/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json index 796d1f01f..e525592fa 100644 --- a/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json +++ b/openapi-bal-task-plugin/src/main/resources/openapi-options-schema.json @@ -28,6 +28,9 @@ }, "statusCodeBinding": { "type": "boolean" + }, + "mock": { + "type": "boolean" } }, "additionalProperties": false diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/Add.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/Add.java index 9b935c15b..b9753689f 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/Add.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/Add.java @@ -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(); } /** @@ -252,6 +254,10 @@ private NodeList 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; } diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java index 1face1c4d..5889adc39 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BallerinaCodeGenerator.java @@ -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; @@ -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); @@ -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 @@ -230,13 +234,14 @@ public static Predicate 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 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); } @@ -357,7 +362,7 @@ private void writeGeneratedSources(List sources, Path srcPath, Path * @throws IOException when code generation with specified templates fails */ private List 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; @@ -385,12 +390,12 @@ private List 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 authNodes = clientGenerator.getBallerinaAuthConfigGenerator() @@ -439,12 +444,20 @@ private List 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); } diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BaseCmd.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BaseCmd.java index 330fa6cc3..7385b68bd 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BaseCmd.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/BaseCmd.java @@ -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; } diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/CmdOptions.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/CmdOptions.java index 5c56e0313..14c8380ae 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/CmdOptions.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/CmdOptions.java @@ -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; @@ -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() { @@ -95,6 +97,10 @@ public boolean getStatusCodeBinding() { return statusCodeBinding; } + public boolean getMock() { + return mock; + } + /** * CMD options builder class. */ @@ -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; @@ -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); } diff --git a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/OpenApiCmd.java b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/OpenApiCmd.java index 048c61628..56422a31f 100644 --- a/openapi-cli/src/main/java/io/ballerina/openapi/cmd/OpenApiCmd.java +++ b/openapi-cli/src/main/java/io/ballerina/openapi/cmd/OpenApiCmd.java @@ -121,6 +121,7 @@ public class OpenApiCmd implements BLauncherCmd { description = "Generate service without data binding") private boolean generateWithoutDataBinding; + @CommandLine.Parameters private List argList; @@ -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) { @@ -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() + "."); @@ -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); diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/CodeGeneratorTest.java b/openapi-cli/src/test/java/io/ballerina/openapi/CodeGeneratorTest.java index 1ba21a66b..0917cd946 100644 --- a/openapi-cli/src/test/java/io/ballerina/openapi/CodeGeneratorTest.java +++ b/openapi-cli/src/test/java/io/ballerina/openapi/CodeGeneratorTest.java @@ -99,7 +99,7 @@ public void generateClient() { try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "generate_client.bal"); generator.generateClient(definitionPath, resourcePath.toString(), filter, - false, false, false); + false, false, false, false); if (Files.exists(resourcePath.resolve("client.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal"); generatedClient = (generatedClient.trim()).replaceAll("\\s+", ""); @@ -152,7 +152,8 @@ public void generateClientForOAS2() { try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "petstore_client_swagger.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, + false, false); if (Files.exists(resourcePath.resolve("client.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal"); generatedClient = (generatedClient.trim()).replaceAll("\\s+", ""); @@ -204,7 +205,8 @@ public void generateClientWithInitDocComments() { BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "x_init_description.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, + false, false); if (Files.exists(resourcePath.resolve("client.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal"); @@ -228,7 +230,8 @@ public void generateTypesWithNullableFields() { BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "nullable_types.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, + false, false); if (Files.exists(resourcePath.resolve("types.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "types.bal"); @@ -252,7 +255,8 @@ public void generateTypesWithNullableFieldsAndGlobalNullableTrue() { BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "nullable_false_types.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, + false, false); if (Files.exists(resourcePath.resolve("types.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "types.bal"); @@ -276,7 +280,8 @@ public void generateUtilsFile() { BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "utils.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, + false, false); if (Files.exists(resourcePath.resolve("utils.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "utils.bal"); @@ -301,7 +306,8 @@ public void generateConfigFile() { try { String expectedConfigContent = getStringFromGivenBalFile(expectedDirPath, "api_key_config.toml"); generator.setIncludeTestFiles(true); - generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, true, false, + false, false); if (Files.exists(resourcePath.resolve("tests/Config.toml"))) { String generateConfigContent = getStringFromGivenBalFile(resourcePath, "tests/Config.toml"); @@ -331,7 +337,7 @@ public void generateFilteredClient() { String expectedSchemaContent = getStringFromGivenBalFile(expectedDirPath, "type_filtered_by_tags.bal"); generator.generateClient(definitionPath, resourcePath.toString(), filterCustom, - false, false, false); + false, false, false, false); if (Files.exists(resourcePath.resolve("client.bal")) && Files.exists(resourcePath.resolve("types.bal"))) { String generatedSchema = getStringFromGivenBalFile(resourcePath, "types.bal"); @@ -356,7 +362,8 @@ public void generateClientwithRequestBody() { try { String expectedClientContent = getStringFromGivenBalFile(expectedDirPath, "generate_client_requestbody.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, false, false, + false, false); if (Files.exists(resourcePath.resolve("client.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal"); @@ -441,7 +448,8 @@ public void generateClientForResourceWithCatchAllPath() { try { String expectedClientContent = getStringFromGivenBalFile( expectedDirPath, "petstore_catch_all_path_client.bal"); - generator.generateClient(definitionPath, resourcePath.toString(), filter, false, true, false); + generator.generateClient(definitionPath, resourcePath.toString(), filter, false, true, false, + false); if (Files.exists(resourcePath.resolve("client.bal"))) { String generatedClient = getStringFromGivenBalFile(resourcePath, "client.bal"); generatedClient = (generatedClient.trim()).replaceAll("\\s+", ""); @@ -698,7 +706,8 @@ public void testForInvalidDefinition() throws IOException, BallerinaOpenApiExcep FormatterException { String definitionPath = RES_DIR.resolve("invalid_petstore.yaml").toString(); BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); - generator.generateClient(definitionPath, "", filter, false, false, false); + generator.generateClient(definitionPath, "", filter, false, false, + false, false); } @Test(description = "Functionality tests when swagger 1.2 contract is given as input", @@ -709,7 +718,8 @@ public void testGenerationForUnsupportedOpenAPIVersion() throws IOException, Bal OASTypeGenException, FormatterException { String definitionPath = RES_DIR.resolve("petstore_swagger_1.2.json").toString(); BallerinaCodeGenerator generator = new BallerinaCodeGenerator(); - generator.generateClient(definitionPath, "", filter, false, false, false); + generator.generateClient(definitionPath, "", filter, false, false, + false, false); } @Test diff --git a/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/MockClientGenerationTests.java b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/MockClientGenerationTests.java new file mode 100644 index 000000000..c6b53910a --- /dev/null +++ b/openapi-cli/src/test/java/io/ballerina/openapi/generators/client/MockClientGenerationTests.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.openapi.generators.client; + +import io.ballerina.compiler.syntax.tree.FunctionBodyNode; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +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.mock.MockFunctionBodyGenerator; +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; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static io.ballerina.openapi.generators.common.GeneratorTestUtils.compareGeneratedSyntaxTreeWithExpectedSyntaxTree; +import static io.ballerina.openapi.generators.common.GeneratorTestUtils.getOpenAPI; + +/** + * Test for mock client generation. + */ +public class MockClientGenerationTests { + private static final Path RES_DIR = Paths.get("src/test/resources/generators/client/mock").toAbsolutePath(); + + @Test + public void mockClientTestForExamplesAttribute() throws IOException, BallerinaOpenApiException { + Path definitionPath = RES_DIR.resolve("basic_response_examples.yaml"); + OpenAPI openapi = getOpenAPI(definitionPath); + TypeHandler.createInstance(openapi, false); + String path = "/api/v1/payment_run_schedules"; + Set> pathItem = openapi.getPaths().get(path). + readOperationsMap().entrySet(); + Iterator> iterator = pathItem.iterator(); + Map.Entry operation = iterator.next(); + + MockFunctionBodyGenerator mockFunctionBodyGenerator = new MockFunctionBodyGenerator(path, operation, openapi, + false); + Optional functionBodyNode = mockFunctionBodyGenerator.getFunctionBodyNode(); + FunctionBodyNode body = functionBodyNode.get(); + String node = body.toString(); + Assert.assertEquals("{return {\"success\":true,\"size\":3,\"schedules\":[{\"id\":6,\"status\":\"Active\"" + + ",\"filter\":\"Account.BillCycleDay = 8\",\"schedule\":\"At 6:00 AM, only on Monday and" + + " Tuesday\"}]};}", node); + } + + @Test + public void mockClientTestWithReferenceExample() throws IOException, BallerinaOpenApiException, ClientException { + Path definitionPath = RES_DIR.resolve("ref_example.json"); + Path expectedPath = RES_DIR.resolve("reference_example.bal"); + OpenAPI openapi = getOpenAPI(definitionPath); + TypeHandler.createInstance(openapi, false); + OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder(); + OASClientConfig oasClientConfig = clientMetaDataBuilder + .withPlugin(false) + .withOpenAPI(openapi) + .withMock(true).build(); + BallerinaMockClientGenerator mockClientGenerator = new BallerinaMockClientGenerator(oasClientConfig); + SyntaxTree syntaxTree = mockClientGenerator.generateSyntaxTree(); + compareGeneratedSyntaxTreeWithExpectedSyntaxTree(expectedPath, syntaxTree); + } + + @Test + public void mockClientTestForRemoteFunction() throws IOException, BallerinaOpenApiException, ClientException { + Path definitionPath = RES_DIR.resolve("basic_response_examples.yaml"); + Path expectedPath = RES_DIR.resolve("mock_client_for_remote.bal"); + OpenAPI openapi = getOpenAPI(definitionPath); + TypeHandler.createInstance(openapi, false); + OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder(); + OASClientConfig oasClientConfig = clientMetaDataBuilder + .withPlugin(false) + .withOpenAPI(openapi) + .withResourceMode(false) + .withMock(true).build(); + BallerinaMockClientGenerator mockClientGenerator = new BallerinaMockClientGenerator(oasClientConfig); + SyntaxTree syntaxTree = mockClientGenerator.generateSyntaxTree(); + compareGeneratedSyntaxTreeWithExpectedSyntaxTree(expectedPath, syntaxTree); + } + + @Test + public void mockClientTestForExampleAttribute() throws IOException, BallerinaOpenApiException, ClientException { + Path definitionPath = RES_DIR.resolve("basic_response_example.yaml"); + Path expectedPath = RES_DIR.resolve("basic_response_example.bal"); + OpenAPI openapi = getOpenAPI(definitionPath); + TypeHandler.createInstance(openapi, false); + OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder(); + OASClientConfig oasClientConfig = clientMetaDataBuilder + .withPlugin(false) + .withOpenAPI(openapi) + .withMock(true).build(); + BallerinaMockClientGenerator mockClientGenerator = new BallerinaMockClientGenerator(oasClientConfig); + SyntaxTree syntaxTree = mockClientGenerator.generateSyntaxTree(); + compareGeneratedSyntaxTreeWithExpectedSyntaxTree(expectedPath, syntaxTree); + } + + @Test + public void advanceMockClientTest() throws IOException, BallerinaOpenApiException, ClientException { + Path definitionPath = RES_DIR.resolve("basic_response_examples.yaml"); + Path expectedPath = RES_DIR.resolve("mock_client_for_advance_return_type.bal"); + OpenAPI openapi = getOpenAPI(definitionPath); + TypeHandler.createInstance(openapi, false); + OASClientConfig.Builder clientMetaDataBuilder = new OASClientConfig.Builder(); + OASClientConfig oasClientConfig = clientMetaDataBuilder + .withPlugin(false) + .withOpenAPI(openapi) + .withMock(true).build(); + AdvanceMockClientGenerator mockClientGenerator = new AdvanceMockClientGenerator(oasClientConfig); + SyntaxTree syntaxTree = mockClientGenerator.generateSyntaxTree(); + compareGeneratedSyntaxTreeWithExpectedSyntaxTree(expectedPath, syntaxTree); + } +} diff --git a/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.bal b/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.bal new file mode 100644 index 000000000..694269aa6 --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.bal @@ -0,0 +1,26 @@ +public isolated client class Client { + # Gets invoked to initialize the `connector`. + # + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(string serviceUrl, ConnectionConfig config = {}) returns error? { + return; + } + + # Get user + # + # + headers - Headers to be sent with the request + # + return - OK + resource isolated function get user(map headers = {}) returns ABC|error { + return {"id": 10, "name": "Jessica Smith"}; + } + + # Get a user + # + # + headers - Headers to be sent with the request + # + return - OK + resource isolated function get users(map headers = {}) returns inline_response_200|error { + return {"id": 10, "name": "Sam Smith"}; + } +} diff --git a/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.yaml b/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.yaml new file mode 100644 index 000000000..b6c12ef61 --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/basic_response_example.yaml @@ -0,0 +1,45 @@ +openapi: 3.0.0 +info: + version: 2021-08 + title: 'API Reference: Collections' +paths: + /user: + get: + summary: Get user + responses: + '200': + description: OK + content: + application/json: + schema: # Request body contents + $ref: "#/components/schemas/ABC" + example: # Sample object + id: 10 + name: "Jessica Smith" + /users: + get: + summary: Get a user + responses: + '200': + description: "OK" + content: + application/json: + schema: # Request body contents + type: object + properties: + id: + type: integer + name: + type: string + example: # Sample object + id: 10 + name: Sam Smith +components: + schemas: + ABC: + type: object + properties: + id: + type: integer + name: + type: string diff --git a/openapi-cli/src/test/resources/generators/client/mock/basic_response_examples.yaml b/openapi-cli/src/test/resources/generators/client/mock/basic_response_examples.yaml new file mode 100644 index 000000000..e98720a08 --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/basic_response_examples.yaml @@ -0,0 +1,3158 @@ +openapi: 3.0.0 +info: + version: 2021-08 + title: 'API Reference: Collections' +tags: + - name: Advanced Payment Manager + - name: Collections Window + - name: Configurable Payment Retry + - name: Notes + - name: Statement Generator +security: + - BasicAuth: [] +paths: + /api/v1/subscription_payment_runs/{payment_run_id}: + get: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + operationId: getPaymentRun + summary: Get a payment run + description: > + Gets details about a particular payment run based on a payment run id in + Advanced Payment Manager. + tags: + - Advanced Payment Manager + parameters: + - in: path + name: payment_run_id + required: true + schema: + type: integer + description: >- + The payment run ID. A payment run id in Advanced Payment Manager is + different from a payment run id in Zuora. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETPaymentRunResponse' + examples: + response: + value: + success: true + id: 6 + status: Complete + target_date: 2018-01-02T00:00:00.000Z + filter: Account.Currency = 'CAD' + payment_run_schedule_id: Adhoc + invoices_held: {} + metrics: + documents: 0 + payments: 0 + failed: 0 + skipped: 0 + amount: 0 + credit: 0 + /api/v1/subscription_payment_runs: + post: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + description: > + Creates a payment run to be executed immediately. You must specify the + **target_date** field. You can specify one or more filters to determine + which receivables are to be paid in the payment run. + + + If you use a custom filter, you cannot use other filters. + summary: Create a payment run + operationId: createPaymentRun + tags: + - Advanced Payment Manager + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTPaymentRun' + required: true + responses: + '200': + description: '' + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTPaymentRunResponse' + application/json: + examples: + response: + value: + id: 6 + success: 'true' + /api/v1/payment_run_schedules: + get: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + operationId: getPaymentRunSchedules + summary: Get all payment run schedules + description: | + Retrieves all payment run schedules in Advanced Payment Manager. + tags: + - Advanced Payment Manager + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETPaymentRunSchedulesResponse' + examples: + response: + value: + success: true + size: 3 + schedules: + - id: 6 + status: Active + filter: Account.BillCycleDay = 8 + schedule: At 6:00 AM, only on Monday and Tuesday + post: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + description: | + Creates a payment run schedule. + summary: Create a payment run schedule + operationId: createPaymentRunSchedule + tags: + - Advanced Payment Manager + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTPaymentRunSchedule' + required: true + responses: + '200': + description: '' + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTPaymentRunScheduleResponse' + application/json: + examples: + response: + value: + id: 6 + success: 'true' + /api/v1/payment_run_schedules/{schedule_id}: + get: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + operationId: getPaymentRunSchedule + summary: Get a payment run schedule + description: | + Gets details about a particular payment run schedule. + tags: + - Advanced Payment Manager + parameters: + - in: path + name: schedule_id + required: true + schema: + type: integer + description: The schedule ID + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETPaymentRunScheduleResponse' + examples: + response: + value: + success: true + id: 6 + status: Active + filter: Account.BillCycleDay = 8 + schedule: At 6:00 AM, only on Monday and Tuesday + put: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + operationId: updatePaymentRunSchedule + summary: Update a payment run schedule + description: | + Updates details about a particular payment run schedule. + tags: + - Advanced Payment Manager + parameters: + - in: path + name: schedule_id + required: true + schema: + type: integer + description: The schedule ID + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/PUTPaymentRunSchedule' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/POSTPaymentRunScheduleResponse' + examples: + response: + value: + id: 6 + success: true + delete: + servers: + - url: https://advanced-payment-manager.apps.zuora.com + operationId: deletePaymentRunSchedule + summary: Cancel a payment run schedule + description: | + Cancels a payment run schedule. + tags: + - Advanced Payment Manager + parameters: + - in: path + name: schedule_id + required: true + schema: + type: integer + description: The schedule ID + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/DELETEPaymentRunScheduleResponse' + examples: + response: + value: + success: true + /api/v1/fetch_statement?statement_number={statement_number}: + get: + servers: + - url: https://statement-generator.apps.zuora.com + operationId: getStatement + summary: Get a statement + description: > + Gets details about a particular statement. The statement will be + available for download in the response. + tags: + - Statement Generator + parameters: + - in: path + name: statement_number + required: true + schema: + type: integer + description: The statement number + responses: + '200': + description: '' + /api/v1/fetch_settings: + get: + servers: + - url: https://statement-generator.apps.zuora.com + operationId: getSettings + summary: Get Statement Generator settings + description: > + Gets several key settings of Statement Generator, including the + available templates, whether Invoice Settlement is enabled on the + tenant, the name of the default template, and the statement cycle being + used. + tags: + - Statement Generator + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETStatementSettingsResponse' + examples: + response: + value: + success: true + templates: + - name: Default Template + - name: Template for end consumers + default_template: Default Template + default_cycle: Month + /api/v1/run: + post: + servers: + - url: https://statement-generator.apps.zuora.com + operationId: createAdhocStatementRun + summary: Create an ad hoc statement run + description: | + Create an ad hoc statement run. + tags: + - Statement Generator + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTAdhocStatementRun' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/POSTAdhocStatementRunResponse' + examples: + response: + value: + success: true + code: 200 + message: Success + statement_number: STA00000008 + /api/v1/execute: + post: + servers: + - url: https://statement-generator.apps.zuora.com + operationId: createCustomStatementRun + summary: Create a custom statement run + description: | + Create a custom statement run. + tags: + - Statement Generator + requestBody: + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/POSTCustomStatementRun' + required: true + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/POSTCustomStatementRunResponse' + examples: + response: + value: + success: true + code: 200 + message: Success + execution_number: '2' + report_file: + /api/v1/accounts/{account_id}: + get: + servers: + - url: https://collections-window.apps.zuora.com + operationId: getAccount + summary: Get an account + description: | + Gets details about a particular account. + tags: + - Collections Window + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: The account ID. + example: 2c92c0f86680fd090166a578754812bd + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionAccount' + examples: + response: + value: + name: Testing Account + zuora_id: 2c92c0f863f2b1680163f579b7e705da + in_collections: true + collections_agent: 2018-01-02T00:00:00.000Z + account_currency: CAD + home_currency: USD + amount_due_account_currency: 15540 + amount_due_home_currency: 800.55 + last_open_invoice_date: '2018-06-12' + average_debt_age: 194.4 days + statuses: + In Collections: true + Pending: false + In Dispute: false + Paid In Full: false + /api/v1/accounts: + get: + servers: + - url: https://collections-window.apps.zuora.com + operationId: getAccounts + summary: Get all accounts + description: | + Gets all accounts that are currently or have been in collection. + tags: + - Collections Window + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETCollectionAccountsResponse' + examples: + response: + value: + accounts: + - name: Testing Account + zuora_id: 2c92c0f863f2b1680163f579b7e705da + in_collections: true + collections_agent: 2018-01-02T00:00:00.000Z + account_currency: CAD + home_currency: USD + amount_due_account_currency: 15540 + amount_due_home_currency: 800.55 + last_open_invoice_date: '2018-06-12' + average_debt_age: 194.4 days + statuses: + In Collections: 0 + Pending: 0 + In Dispute: 0 + Paid In Full: 0 + pagination: + page: 1 + page_length: 20 + next_page: >- + https://collections-window.apps.zuora.com/api/v1/accounts?page=2&page_length=20 + /api/v1/users/{email}: + get: + servers: + - url: https://collections-window.apps.zuora.com + operationId: getCollectionsAgent + summary: Get a collections agent by email + description: > + Gets details about a collections agent, including the amount in + collections, and the statuses of the accounts that this agent is + assigned to. + tags: + - Collections Window + parameters: + - in: path + name: email + required: true + schema: + type: string + description: The email of the collections agent. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionAgent' + examples: + response: + value: + name: Testing User + email: test@zuora.com + zuora_identity_id: 2c92c0f96178a7a901619b10f5d12345 + amount_in_collections: 800.55 + accounts: 2 + account_statuses: + In Collections: 1 + Pending: 0 + In Dispute: 1 + Paid In Full: 0 + /api/v1/users: + get: + servers: + - url: https://collections-window.apps.zuora.com + operationId: getCollectionsAgents + summary: Get all collections agents + description: | + Gets the details of all collections agents. + tags: + - Collections Window + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETCollectionAgentsResponse' + examples: + response: + value: + accounts: + - name: Testing User + email: test@zuora.com + zuora_identity_id: 2c92c0f96178a7a901619b10f5d12345 + amount_in_collections: 800.55 + accounts: 2 + account_statuses: + In Collections: 1 + Pending: 0 + In Dispute: 1 + Paid In Full: 0 + pagination: + page: 1 + page_length: 20 + next_page: >- + https://collections-window.apps.zuora.com/api/v1/users?page=2&page_length=20 + /api/v1/collections_info: + get: + servers: + - url: https://collections-window.apps.zuora.com + operationId: getCollectionsInfo + summary: Get an overview of collections + description: | + Gets an overview of the collections. + tags: + - Collections Window + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/CollectionsInfo' + examples: + response: + value: + accounts_in_collections: 24 + home_currency: USD + total_debt: '8379.78' + largest_debts: + - Test Account - 12438.00 USD + - Jimmy John - 8000.00 USD + - James Smith - 2450.55 USD + - Bob Roberts - 1000.00 USD + - Jim Reynolds - 829.00 USD + oldest_debts: + - Test Account - 2662 days + - Jimbo - 1494 days + - Steve Smith - 942 days + - Jason Williams - 678 days + - Will Jasons - 365 days + statuses: + In Collections: 24 + Pending: 2 + In Dispute: 5 + Paid in Full: 0 + /api/v1/accounts/{account_id}/update_status: + post: + servers: + - url: https://collections-window.apps.zuora.com + operationId: UpdateAccount + summary: Update account status + description: > + Updates the status of an account. You must use the two query parameters + to specify the status that you want to update, and the new value of the + status. + tags: + - Collections Window + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: The account ID. + example: 2c92c0f86680fd090166a578754812bd + - in: query + name: status + required: true + schema: + type: string + description: The status that you want to update for the specified account. + example: in_dispute + - in: query + name: value + schema: + type: boolean + description: >- + The new value of the status. 0 indicates false, while 1 indicates + true. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Whether the operation is successful. + examples: + response: + value: + success: true + /api/v1/accounts/{account_id}/update_agent: + post: + servers: + - url: https://collections-window.apps.zuora.com + operationId: UpdateAccountAgent + summary: Update account agent + description: > + Updates the collections agent that is displayed within the UI of + Collections Window, and the custom field for the agent displayed in + account details in the Zuora UI. + tags: + - Collections Window + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: The account ID. + example: 2c92c0f86680fd090166a578754812bd + - in: query + name: agent_email + required: true + schema: + type: string + description: The email of the agent. + example: jdoe@company.com + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Whether the operation is successful. + examples: + response: + value: + success: true + /api/v1/payments/active_invoice_cycle_information/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getActiveInvoiceCycle + summary: Get active retry cycles for an invoice + description: > + Gets information of active retry cycles that have not been completed for + an invoice in Configurable Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: invoice_id + required: true + schema: + type: string + description: ID of an invoice. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETActiveInvoiceCycleResponse' + /api/v1/payments/active_debit_memo_cycle_information/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getActiveDebitMemoCycle + summary: Get active retry cycles for a debit memo + description: > + Gets information of active retry cycles that have not been completed for + a debit memo in Configurable Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: debit_memo_id + required: true + schema: + type: string + description: ID of a debit memo. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETActiveDebitMemoCycleResponse' + /api/v1/payments/active_account_cycle_information/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getActiveAccountCycle + summary: Get active retry cycles for an account + description: > + Gets information of active retry cycles that have not been completed for + an account in Configurable Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: ID of an account. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETActiveAccountCycleResponse' + /api/v1/payments/invoice_cycle_history/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getInvoiceCycleHistory + summary: Get retry cycle history for an invoice + description: > + Gets information of all retry cycles for an invoice in Configurable + Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: invoice_id + required: true + schema: + type: string + description: ID of an invoice. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETInvoiceCycleHistoryResponse' + /api/v1/payments/debit_memo_cycle_history/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getDebitMemoCycleHistory + summary: Get retry cycle history for a debit memo + description: > + Gets information of all retry cycles for a debit memo in Configurable + Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: debit_memo_id + required: true + schema: + type: string + description: ID of a debit memo. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETDebitMemoCycleHistoryResponse' + /api/v1/payments/account_cycle_history/: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getAccountCycleHistory + summary: Get retry cycle history for an account + description: > + Gets information of all retry cycles for an account in Configurable + Payment Retry. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: ID of an account. + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETAccountCycleHistoryResponse' + /api/v1/payments/execute_invoice_payment/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: executeExecuteInvoicePayment + summary: Execute invoice payment + description: > + For all active retry cycles associated with an invoice, schedules the + next payment retry attempt to occur in the next hourly payment processor + run. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: invoice_id + required: true + schema: + type: string + description: ID of an invoice. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs enqueued for processing: + [290, 291] + /api/v1/payments/execute_debit_memo_payment/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: executeExecuteDebitMemoPayment + summary: Execute debit memo payment + description: > + For all active retry cycles associated with a debit memo, schedules the + next payment retry attempt to occur in the next hourly payment processor + run. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: debit_memo_id + required: true + schema: + type: string + description: ID of a debit memo. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs enqueued for processing: + [300] + /api/v1/payments/execute_account_payments/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: executeExecuteAccountPayments + summary: Execute account payments + description: > + For all active retry cycles associated with an account, schedules the + next payment retry attempt to occur in the next hourly payment processor + run. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: ID of an account. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs enqueued for processing: + [310, 311, 312] + /api/v1/payments/execute_payments: + post: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: UpdateExecutePayments + summary: Execute payments + description: > + For all active retry cycles associated with the invoice, debit memo, + and/or account IDs provided, schedules the next payment retry attempt to + occur in the next hourly payment processor run. + tags: + - Configurable Payment Retry + requestBody: + content: + application/json: + schema: + type: object + properties: + account_ids: + type: array + items: + type: string + description: IDs of accounts. + debit_memo_ids: + description: IDs of debit memos. + type: array + items: + type: string + invoice_ids: + type: array + items: + type: string + description: IDs of invoices. + example: + account_ids: + - 2c92c0f96bd69165016bdcbf55ad5e62 + invoice_ids: + - 2c92c0fa7853052701785a38c6622473 + - 2c92c0fa7849b40a01784bc5de0f760f + debit_memo_ids: + - 2c92c0fa7853052701785a38f3bb267f + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs enqueued for processing: + [100, 101, 110, 111, 121] + /api/v1/payments/remove_invoice_from_retry_cycle/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: removeRemoveInoviceFromCycle + summary: Remove an invoice from retry cycle + description: | + Stops any active retry cycles associated with the invoice provided. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: invoice_id + required: true + schema: + type: string + description: ID of an invoice. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs have been removed from the + retry cycle: [290, 291] + /api/v1/payments/remove_debit_memo_from_retry_cycle/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: removeDebitMemoFromCycle + summary: Remove a debit memo from retry cycle + description: | + Stops any active retry cycles associated with the debit memo provided. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: debit_memo_id + required: true + schema: + type: string + description: ID of a debit memo. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs have been removed from the + retry cycle: [301] + /api/v1/payments/remove_account_from_retry_cycle/: + put: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: removeAccountFromCycle + summary: Remove an account from retry cycle + description: | + Stops any active retry cycles associated with the account provided. + tags: + - Configurable Payment Retry + parameters: + - in: path + name: account_id + required: true + schema: + type: string + description: ID of an account. + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: >- + Payments with the following IDs have been removed from the + retry cycle: [310, 311, 312] + /api/v1/payments/submit_failed_payment: + post: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: UpdateSubmitPaymentToCycle + summary: Submit a payment to retry cycle + description: > + Submit a failed payment into the retry cycle of Configurable Payment + Retry. + tags: + - Configurable Payment Retry + requestBody: + content: + application/json: + schema: + type: object + properties: + payment_id: + type: string + description: ID of a failed payment. + example: + payment_id: 2c92c0867849d42301784bc9ce806c31 + responses: + '200': + description: '' + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + description: Indicates whether the operation is successful. + message: + type: string + description: The response message. + example: + success: true + message: Payment entered into retry process + /api/v1/metrics/baseline: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getBaselineMetrics + summary: Get baseline metrics + description: > + Gets baseline metrics for Configurable Payment Retry, including Retry + Success Rate and trend, Document Success Rate and trend, and Average + Days Outstanding and trend. See Response Schema for detailed + descriptions of the metrics. + tags: + - Configurable Payment Retry + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETBaselineMetricsResponse' + /api/v1/metrics/amount_recovered: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getAmountRecovered + summary: Get the Amount Recovered metrics + description: > + Gets the Amount Recovered metrics, including the total amount recovered + and the amount recovered over the last 30 days broken down by currency. + tags: + - Configurable Payment Retry + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETAmountRecoveredResponse' + /api/v1/metrics/customer_group_over_time: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getDocumentSuccessRateByCustomerGroup + summary: Get the Document Success Rate metrics by customer group + description: > + Gets the Document Success Rate timeseries for each customer group over + the past 6 months. The data of the current month will not be included. + For example, if it is April 15th today, the data for April will not be + included. Data for March and earlier will be shown. + tags: + - Configurable Payment Retry + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: >- + #/components/schemas/GETDocumentSuccessRateByCustomerGroupResponse + /api/v1/metrics/customer_group: + get: + servers: + - url: https://payment-retry.apps.zuora.com + operationId: getCustomerGroupMetrics + summary: Get the Customer Group metrics + description: > + Gets the following metrics for each customer group that is in the active + status: + - Document Success Rate and trend + - Retry Success Rate + - Average Attempts + tags: + - Configurable Payment Retry + responses: + '200': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/GETCustomerGroupMetricsResponse' +components: + schemas: + Customer_Groups: + type: object + properties: + id: + type: integer + name: + type: string + smart_retry: + type: string + document_success_rate: + type: string + document_success_rate_trend: + type: string + retry_success_rate: + type: string + average_attempts: + type: string + Customer_Groups_Detail: + type: array + items: + type: string + + GETCustomerGroupMetricsResponse: + type: object + properties: + customer_groups: + type: array + items: + $ref: '#/components/schemas/Customer_Groups' + description: > + An array containing the following information for each customer + group. + - Customer group ID + - Customer group name + - Whether Smart Retry is enabled for this customer group + - Document Success Rate indicating the percentage of billing documents that are successfully collected + - The trend of change in Document Success Rate over the last 30 days + - Retry Success Rate indicating the percentage of the successful payment attempts + - Average Attempts indicating the average number of attempts required to recover funds + success: + type: boolean + description: > + Indicates whether the metrics are retrieved successfully. + + + If no data is loaded yet, a 200 response code will be returned with + the success field set to `true`. All expected fields will be present + but the value will be `null`. + example: + customer_groups: + - id: 1 + name: batch22 + smart_retry: false + document_success_rate: '17.17' + document_success_rate_trend: up + retry_success_rate: '21.76' + average_attempts: '4.11' + - id: 2 + name: Smart Retry + smart_retry: true + document_success_rate: '74.17' + document_success_rate_trend: down + retry_success_rate: '81.21' + average_attempts: '1.32' + - id: 4 + name: All Remaining Customers + smart_retry: false + document_success_rate: '16.35' + document_success_rate_trend: up + retry_success_rate: '15.21' + average_attempts: '3.32' + success: true + GETDocumentSuccessRateByCustomerGroupResponse: + type: object + properties: + customer_groups: + type: array + items: + $ref: '#/components/schemas/Customer_Groups_Detail' + description: > + An array containing the Document Success Rate timeseries for each + customer group represented by the customer group ID. + + A timeseries over the past 6 months is present in the format of + `: `. The data of the current + month is not included. For example, if it is April 15th today, the + data for April will not be included. Data for March and earlier will + be shown. + success: + type: boolean + description: > + Indicates whether the metrics are retrieved successfully. + + + If no data is loaded yet, a 200 response code will be returned with + the success field set to `true`. All expected fields will be present + but the value will be `null`. + example: + customer_groups: + '1': + '05_21': '17.53' + '04_21': '13.21' + '03_21': '14.92' + '02_21': '8.99' + '01_21': '34.25' + '12_20': '12.30' + '2': + '05_21': '11.11' + '04_21': '7.87' + '03_21': '26.00' + '02_21': '11.06' + '01_21': '13.43' + '12_20': '17.92' + '4': + '05_21': '11.13' + '04_21': '9.17' + '03_21': '17.20' + '02_21': '19.06' + '01_21': '12.43' + '12_20': '15.92' + success: true + GETAmountRecoveredResponse: + type: object + properties: + currency: + type: array + items: + type: string + description: > + An array containing the following information for each available + currency. + + If no payment of a given currency has been processed over the last + 30 days, the currency will not appear in the array. + - The total amount recovered to date + - The amount recovered over the last 30 days + success: + type: boolean + description: > + Indicates whether the metrics are retrieved successfully. + + + If no data is loaded yet, a 200 response code will be returned with + the success field set to `true`. All expected fields will be present + but the value will be `null`. + example: + currency: + USD: + total_amount: '77515.21' + last_30_days: '1100.01' + EUR: + total_amount: '337.19' + last_30_days: '17.17' + CAD: + total_amount: '123954.10' + last_30_days: '5132.87' + success: true + GETBaselineMetricsResponse: + type: object + properties: + retry_success_rate: + type: number + description: > + The percentage of successful payment attempts for all customer + groups on this Configurable Payment Retry instance so far. + + + Retry Success Rate = Number of successful retry payment attempts / + Total number of retry payments attempted + + + Payment attempts here are any payment attempts executed by + Configurable Payment Retry, excluding the initial payment run. + retry_success_rate_trend: + type: string + description: | + The trend of change in Retry Success Rate over the last 30 days. + document_success_rate: + type: number + description: > + The percentage of billing documents that are successfully collected, + for all customer groups on this Configurable Payment Retry instance + so far. + + + Document Success Rate = Number of billing documents collected / + Total number of billing documents attempted + + + Billing documents collected are any invoices and debit memos that + were paid through the payment attempt initiated by Configurable + Payment Retry. + document_success_rate_trend: + type: string + description: | + The trend of change in Document Success Rate over the last 30 days. + average_days_outstanding: + type: number + description: > + The average number of days invoices and debit memos are unpaid + before collection, for all customer groups on this Configurable + Payment Retry instance so far. + average_days_outstanding_trend: + type: string + description: > + The trend of change in Average Days Outstanding over the last 30 + days. + success: + type: boolean + description: > + Indicates whether the metrics are retrieved successfully. + + + If no data is loaded yet, a 200 response code will be returned with + the success field set to `true`. All expected fields will be present + but the value will be `null`. + example: + retry_success_rate: '11.90' + retry_success_rate_trend: down + document_success_rate: '13.54' + document_success_rate_trend: neutral + average_days_outstanding: '4.76' + average_days_outstanding_trend: up + success: true + GETActiveInvoiceCycleResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETInvoiceCycleElementResponse' + description: > + An array containing information of all active retry cycles for an + invoice. See the schema and example for details. + example: + cycles: + - account_id: 2c92c0f96bd69165016bdccdd6ce2f29 + invoice_id: 2c92c0f8778bf8cd017798168cb50e0b + payment_method_id: 2c92c0f9774f2b3e01775f6f06d87b61 + currency: USD + status: Cycle Incomplete + current_attempt_number: 2 + next_attempt: '2021-04-01T19:27:34.648Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c0867849d42301784bc9ca076c21 + time_of_execution: '2021-03-19T18:42:20.103Z' + source: PR-00000371 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-04-01T19:27:34.648-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - attempt_number: 2 + zuora_payment_id: 2c92c09c7849d3c101784bce0d0a06d5 + time_of_execution: '2021-03-19T18:52:24.137Z' + source: PR-00000372 + cpr_generated: true + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-19T09:53:39.845-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETInvoiceCycleElementResponse: + type: object + title: retry cycle object + properties: + account_id: + type: string + description: | + The ID of the customer account. + invoice_id: + type: string + description: | + The ID of the invoice. + payment_method_id: + type: string + description: | + The ID of the payment method. + currency: + type: string + description: | + The type of the currency used. + status: + type: string + description: | + The status of the retry cycle. + current_attempt_number: + type: number + description: | + The number of the current retry attempt. + next_attempt: + type: string + format: date-time + description: > + The date and time of the next retry attempt, in `yyyy-mm-dd + hh:mm:ss` format. + customer_group: + type: string + description: | + The name of the customer group. + attempts: + type: array + description: > + An array containing information of the retry attempts that have been + made. See the schema and example for details. + items: + $ref: '#/components/schemas/GETCycleElementAttemptResponse' + GETCycleElementAttemptResponse: + type: object + title: retry attempt object + properties: + attempt_number: + type: number + description: | + The number of the retry attempt. + zuora_payment_id: + type: string + description: | + The ID of the payment in Zuora Payments. + time_of_execution: + type: string + format: date-time + description: > + The date and time when the retry attempt was made, in `yyyy-mm-dd + hh:mm:ss` format. + source: + type: string + description: | + The number of the failed payment run. + cpr_generated: + type: boolean + description: > + Indicates whether the first retry attempt was generated by + Configurable Payment Retry. + + + `true` for the cases of the first retry attempt generated by CPR and + the second or later attempt. + + + `false` for the cases that the retry attempt was made by the payment + run or an external source during retry cycles. + success: + type: boolean + description: | + Indicates whether the retry attempt is successful. + amount_collected: + type: number + description: | + The amount collected in this retry attempt. + action_info: + type: object + description: | + Information about the action of the retry attempt. + properties: + action: + type: string + description: | + The action taken in the retry attempt. + retry_info: + type: object + description: | + Information about the next retry attempt. + properties: + next: + type: string + format: date-time + description: > + The date and time of the next retry attempt as determined by + `criteria`, in `yyyy-mm-dd hh:mm:ss` format. + criteria: + type: string + description: > + The criteria defined in the retry configuration in the CPR UI, + to specify whether the next retry will occur in an incremental + time or at a specific time or day. + mapping_info: + type: object + description: | + Information about the response code mapping for the retry attempt. + properties: + label: + type: string + description: > + The response code label, can be `Hard Decline`, `Soft Decline`, + `System Error`, or custom labels. + level: + type: string + description: > + The response code level for the payment gateway, can be `Code` + or `Description`. + customer_group_id: + type: number + description: | + The ID of the customer group. + gateway_info: + type: object + description: | + Information about the payment gateway. + properties: + id: + type: string + description: | + The ID of the payment gateway. + code: + type: string + description: | + The response code from the gateway. + response: + type: string + description: | + The response message from the gateway. + GETActiveDebitMemoCycleResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETDebitMemoCycleElementResponse' + description: > + An array containing information of all active retry cycles for a + debit memo. See the schema and example for details. + example: + cycles: + - account_id: 2c92c0f868e161e70168e25eb51d755f + debit_memo_id: 2c92c0fa7853052701785a38f3bb267f + payment_method_id: 2c92c0f8774f1afe01775f6e533c4c06 + currency: USD + status: Cycle Incomplete + current_attempt_number: 2 + next_attempt: '2021-04-01T10:22:57.464Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c085785305e201785a51af791979 + time_of_execution: '2021-03-23T16:50:18.878Z' + source: PR-00000376 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-23T07:51:30.380-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - attempt_number: 2 + zuora_payment_id: 2c92c0857881cf3e01788ee263b0331c + time_of_execution: '2021-04-01T19:21:55.207Z' + source: PR-00000380 + cpr_generated: true + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-04-01T10:22:57.464-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETDebitMemoCycleElementResponse: + type: object + title: retry cycle object + properties: + account_id: + type: string + description: | + The ID of the customer account. + debit_memo_id: + type: string + description: | + The ID of the debit memo. + payment_method_id: + type: string + description: | + The ID of the payment method. + currency: + type: string + description: | + The type of the currency used. + status: + type: string + description: | + The status of the retry cycle. + current_attempt_number: + type: number + description: | + The number of the current retry attempt. + next_attempt: + type: string + format: date-time + description: > + The date and time of the next retry attempt, in `yyyy-mm-dd + hh:mm:ss` format. + customer_group: + type: string + description: | + The name of the customer group. + attempts: + type: array + description: > + An array containing information of the retry attempts that have been + made. See the schema and example for details. + items: + $ref: '#/components/schemas/GETCycleElementAttemptResponse' + GETActiveAccountCycleResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETAccountCycleElementResponse' + description: > + An array containing information of all active retry cycles for an + account. See the schema and example for details. + example: + cycles: + - account_id: 2c92c0f96bd69165016bdccdd6ce2f29 + debit_memo_id: 2c92c0fb78532b0001785a38f6427976 + payment_method_id: 2c92c0f9774f2b3e01775f6f06d87b61 + currency: USD + status: Cycle Incomplete + current_attempt_number: 1 + next_attempt: '2021-04-01T19:27:34.473Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c085785305e201785a5199a6192d + time_of_execution: '2021-03-23T16:50:18.878Z' + source: PR-00000376 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-23T07:51:27.521-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - account_id: 2c92c0f96bd69165016bdccdd6ce2f29 + invoice_id: 2c92c0fa7853052701785a38c6622473 + payment_method_id: 2c92c0f9774f2b3e01775f6f06d87b61 + currency: USD + status: Cycle Incomplete + current_attempt_number: 1 + next_attempt: '2021-04-01T19:27:34.436Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c085785305e201785a519d85193b + time_of_execution: '2021-03-23T16:50:18.878Z' + source: PR-00000376 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-23T07:51:28.161-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETAccountCycleElementResponse: + type: object + title: retry cycle object + properties: + account_id: + type: string + description: | + The ID of the customer account. + invoice_id: + type: string + description: | + The ID of the invoice. + debit_memo_id: + type: string + description: | + The ID of the debit memo. + payment_method_id: + type: string + description: | + The ID of the payment method. + currency: + type: string + description: | + The type of the currency used. + status: + type: string + description: | + The status of the retry cycle. + current_attempt_number: + type: number + description: | + The number of the current retry attempt. + next_attempt: + type: string + format: date-time + description: > + The date and time of the next retry attempt, in `yyyy-mm-dd + hh:mm:ss` format. + customer_group: + type: string + description: | + The name of the customer group. + attempts: + type: array + description: > + An array containing information of the retry attempts that have been + made. See the schema and example for details. + items: + $ref: '#/components/schemas/GETCycleElementAttemptResponse' + GETInvoiceCycleHistoryResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETInvoiceCycleElementResponse' + description: > + An array containing information of all completed retry cycles for an + invoice. See the example for details. + example: + cycles: + - account_id: 2c92c0f96bd69165016bdcbf55ad5e62 + invoice_id: 2c92c0fa7849b3ff01784bc5e8ee18b5 + payment_method_id: 2c92c0f9774f2b3e01775f6cf2fb726a + currency: USD + status: Cycle Complete + current_attempt_number: 1 + next_attempt: null + customer_group: Testing Group + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c0867849d42301784bc9ce806c31 + time_of_execution: '2021-04-01T19:11:21.639Z' + source: PR-00000370 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Stop + retry_info: {} + mapping_info: + label: Hard Decline + level: code + customer_group_id: 5 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - account_id: 2c92c0f96bd69165016bdcbf55ad5e62 + invoice_id: 2c92c0fa7849b3ff01784bc5e8ee18b5 + payment_method_id: 2c92c0f9774f2b3e01775f6cf2fb726a + currency: USD + status: Cycle Complete + current_attempt_number: 2 + next_attempt: null + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c0867849d42301784bc9ce806c31 + time_of_execution: '2021-03-19T18:42:20.103Z' + source: PR-00000371 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-19T09:43:28.670-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - attempt_number: 2 + zuora_payment_id: 2c92c09c7849d3c101784bcdfc010671 + time_of_execution: '2021-03-19T18:52:24.137Z' + source: PR-00000372 + cpr_generated: true + success: false + amount_collected: '0.0' + action_info: + action: Stop + retry_info: {} + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETDebitMemoCycleHistoryResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETDebitMemoCycleElementResponse' + description: > + An array containing information of all completed retry cycles for a + debit memo. See the example for details. + example: + cycles: + - account_id: 2c92c0f868e161e70168e25eb51d755f + debit_memo_id: 2c92c0fa7853052701785a38f3bb267f + payment_method_id: 2c92c0f8774f1afe01775f6e533c4c06 + currency: USD + status: Cycle Complete + current_attempt_number: 2 + next_attempt: null + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c085785305e201785a51af791979 + time_of_execution: '2021-03-23T16:50:18.878Z' + source: PR-00000376 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-23T07:51:30.380-09:00' + criteria: incremental_time + api_updated_retry_time: '2021-03-26T14:27:21.107-04:00' + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - attempt_number: 2 + zuora_payment_id: 2c92c0857881cf3e01788ee263b0331c + time_of_execution: '2021-04-01T19:21:55.207Z' + source: PR-00000378 + cpr_generated: true + success: false + amount_collected: '0.0' + action_info: + action: Stop + retry_info: {} + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETAccountCycleHistoryResponse: + type: object + properties: + cycles: + type: array + items: + $ref: '#/components/schemas/GETAccountCycleElementResponse' + description: > + An array containing information of all completed retry cycles for an + account. See the example for details. + example: + cycles: + - account_id: 2c92c0f96bd69165016bdcbf55ad5e62 + invoice_id: 2c92c0fa7849b3ff01784bc5e8ee18b5 + payment_method_id: 2c92c0f9774f2b3e01775f6cf2fb726a + currency: USD + status: Cycle Complete + current_attempt_number: 3 + next_attempt: '2021-03-19T18:53:39.641Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c0867849d42301784bc9ce806c31 + time_of_execution: '2021-03-19T18:42:20.103Z' + source: PR-00000371 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-19T09:43:28.670-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - attempt_number: 2 + zuora_payment_id: 2c92c09c7849d3c101784bcdfc010671 + time_of_execution: '2021-03-19T18:52:24.137Z' + source: PR-00000372 + cpr_generated: true + success: false + amount_collected: '0.0' + action_info: + action: Stop + retry_info: {} + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + - account_id: 2c92c0f96bd69165016bdcbf55ad5e62 + invoice_id: 2c92c0fb78532b0f01785a38cede190a + payment_method_id: 2c92c0f9774f2b3e01775f6cf2fb726a + currency: USD + status: Cycle Incomplete + current_attempt_number: 1 + next_attempt: '2021-03-24T19:34:20.254Z' + customer_group: All Remaining Customers + attempts: + - attempt_number: 1 + zuora_payment_id: 2c92c085785305e201785a51aae41969 + time_of_execution: '2021-03-23T16:50:18.878Z' + source: PR-00000376 + cpr_generated: false + success: false + amount_collected: '0.0' + action_info: + action: Retry + retry_info: + next: '2021-03-23T07:51:30.316-09:00' + criteria: incremental_time + mapping_info: + label: Hard Decline + level: code + customer_group_id: 1 + gateway_info: + id: 2c92c0f85e2d19af015e3a61d8947e5d + code: insufficient_funds + response: Your card has insufficient funds. + GETPaymentRunResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + id: + type: integer + description: | + The ID of the payment run in Advanced Payment Manager. + status: + type: string + description: | + The status of the payment run. + target_date: + type: string + format: date + description: | + The target date of the payment run, in the format of YYYY-MM-DD. + filter: + type: string + description: | + The filter that is used for the payment run. + payment_run_schedule_id: + type: string + description: > + The ID of the payment run schedule. If this is not a scheduled + payment run, the value is **Adhoc**. + invoices_held: + type: object + description: | + Invoices that are held by the payment run for manual processing. + metrics: + type: object + description: | + Metrics of the payment runs after completion. + errors: + type: array + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + items: {} + POSTPaymentRun: + type: object + properties: + target_date: + type: string + format: date + description: > + The target date of the payment run to be created, in the format of + YYYY-MM-DD. + billrun: + type: string + description: > + A bill run name that is used to determine what receivables should be + included in the payment run. Valid only if the custom filter is not + used. + batch: + type: string + description: > + A batch name that is used to determine what receivables should be + included in the payment run. Valid only if the custom filter is not + used. + currency: + type: string + description: > + A three-character currency code that is used to determine what + receivables should be included in the payment run. Valid only if the + custom filter is not used. + payment_gateway: + type: string + description: > + A payment gateway name that is used to determine what receivables + should be included in the payment run. Valid only if the custom + filter is not used. + bill_cycle_day: + type: string + description: > + A bill cycle day value that is used to determine what receivables + should be included in the payment run. Valid only if the custom + filter is not used. + custom: + type: string + description: > + A custom ZOQL query that is used to determine what receivables + should be included in the payment run. It cannot be used with other + filters. + example: + target_date: '2019-02-04' + currency: CAD + POSTPaymentRunResponse: + type: object + properties: + id: + type: integer + description: | + The ID of the payment run in Advanced Payment Manager. + success: + type: boolean + description: | + Whether the operation is successful. + errors: + type: array + items: + type: string + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + GETPaymentRunSchedulesResponse: + type: object + properties: + schedules: + type: array + items: + type: object + description: | + An array containing all available schedules. + size: + type: integer + description: | + The number of schedules retrieved. + success: + type: boolean + description: | + Whether the operation is successful. + errors: + type: array + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + items: {} + GETPaymentRunScheduleResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + id: + type: integer + description: | + The ID of the payment run schedule in Advanced Payment Manager. + status: + type: string + description: | + The status of the payment run schedule. + filter: + type: string + format: date + description: | + The filter that is defined in the payment run schedule. + schedule: + type: string + description: | + The schedule that is defined for this payment run schedule. + errors: + type: array + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + items: {} + POSTPaymentRunSchedule: + type: object + properties: + frequency: + type: string + description: | + The frequency of payment runs in the schedule. + enum: + - Daily + - Weekly + - Monthly + day: + type: string + description: > + The day of a month when payment runs need to be performed. Required + only if **frequency** is **Monthly**. Allowed values are 1-31 or + **L** (the last day of a month). + day_of_week: + type: array + items: + type: integer + description: > + An array of days in a week when payment runs need to be performed. + Required only if **frequency** is **Weekly**. Allowed integers are + 1-7. 1 stands for Monday. 2 stands for Tuesday, etc. + hour: + type: integer + description: > + The hour of a day when payment runs need to be performed. Allowed + values are 1-23. + billrun: + type: string + description: > + A bill run name that is used to determine what receivables should be + included in payment runs in this schedule. Valid only if the custom + filter is not used. + batch: + type: string + description: > + A batch name that is used to determine what receivables should be + included in payment runs in this schedule. Valid only if the custom + filter is not used. + currency: + type: string + description: > + A three-character currency code that is used to determine what + receivables should be included in payment runs in this schedule. + Valid only if the custom filter is not used. + payment_gateway: + type: string + description: > + A payment gateway name that is used to determine what receivables + should be included in payment runs in this schedule. Valid only if + the custom filter is not used. + bill_cycle_day: + type: string + description: > + A bill cycle day value that is used to determine what receivables + should be included in payment runs in this schedule. Valid only if + the custom filter is not used. + custom: + type: string + description: > + A custom ZOQL query that is used to determine what receivables + should be included in payment runs in this schedule. It cannot be + used with other filters. + example: + frequency: Weekly + days_of_week: + - 2 + - 3 + hour: 2 + currency: CAD + PUTPaymentRunSchedule: + type: object + properties: + frequency: + type: string + description: | + The frequency of payment runs in the schedule. + enum: + - Daily + - Weekly + - Monthly + day: + type: string + description: > + The day of a month when payment runs need to be performed. Required + only if **frequency** is **Monthly**. Allowed values are 1-31 or + **L** (the last day of a month). + day_of_week: + type: array + items: + type: integer + description: > + An array of days in a week when payment runs need to be performed. + Required only if **frequency** is **Weekly**. Allowed integers are + 1-7. 1 stands for Monday. 2 stands for Tuesday, etc. + hour: + type: integer + description: > + The hour of a day when payment runs need to be performed. Allowed + values are 1-23. + billrun: + type: string + description: > + A bill run name that is used to determine what receivables should be + included in payment runs in this schedule. Valid only if the custom + filter is not used. + batch: + type: string + description: > + A batch name that is used to determine what receivables should be + included in payment runs in this schedule. Valid only if the custom + filter is not used. + currency: + type: string + description: > + A three-character currency code that is used to determine what + receivables should be included in payment runs in this schedule. + Valid only if the custom filter is not used. + payment_gateway: + type: string + description: > + A payment gateway name that is used to determine what receivables + should be included in payment runs in this schedule. Valid only if + the custom filter is not used. + bill_cycle_day: + type: string + description: > + A bill cycle day value that is used to determine what receivables + should be included in payment runs in this schedule. Valid only if + the custom filter is not used. + custom: + type: string + description: > + A custom ZOQL query that is used to determine what receivables + should be included in payment runs in this schedule. It cannot be + used with other filters. + example: + currency: USD + POSTPaymentRunScheduleResponse: + type: object + properties: + id: + type: integer + description: | + The ID of the payment run schedule in Advanced Payment Manager. + success: + type: boolean + description: | + Whether the operation is successful. + errors: + type: array + items: + type: string + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + DELETEPaymentRunScheduleResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + errors: + type: array + items: + type: string + description: > + Errors that occur in the operation. Available only when **success** + is **false**. + GETStatementsResponse: + type: object + properties: + statements: + type: array + items: + type: object + description: > + An array containing the requested number of statements of the + specified account. Every object contains an Amazon S3 link. You can + click the links to view and download the statements. The links will + expire in 10 minutes. + success: + type: boolean + description: | + Whether the operation is successful. + count: + type: integer + description: | + The number of statements retrieved. + GETStatementSettingsResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + templates: + type: array + items: + type: object + properties: + name: + type: string + description: The name of the template. + description: | + An array containing all available templates. + advanced_ar: + type: boolean + description: | + Whether Invoice Settlement is enabled on the tenant. + default_template: + type: string + description: | + The name of the default template. + default_cycle: + type: string + description: | + The statement cycle being used. + POSTAdhocStatementRun: + type: object + properties: + starting_date: + type: string + format: date + description: > + The starting date of the time period for which the statement is + generated, in the format of MM-DD-YYYY. + ending_date: + type: string + format: date + description: > + The ending date of the time period for which the statement is + generated, in the format of MM-DD-YYYY. + template_name: + type: string + description: | + The name of the template to be used in the statement run. + advanced_ar: + type: boolean + description: | + Specifies whether Invoice Settlement is enabled on the tenant. + account_id: + type: string + description: | + The ID of the account for which the statement run is created. + attach_to_account: + type: boolean + description: > + Indicates whether the generated statement is attached to the + account. + required: + - starting_date + - ending_date + - template_name + - advanced_ar + - account_id + example: + starting_date: 05-01-2018 + ending_date: 08-01-2018 + template_name: Default Name + advanced_ar: false + account_id: 2c92c0f964eaa3ec0164fad1eab730d6 + attach_to_account: true + POSTAdhocStatementRunResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + code: + type: integer + description: | + The response code. + message: + type: string + description: | + The response message. + statement_number: + type: string + description: | + The number of the statement. + POSTCustomStatementRun: + type: object + properties: + starting_date: + type: string + format: date + description: > + The starting date of the time period for which the statement is + generated, in the format of MM-DD-YYYY. + ending_date: + type: string + format: date + description: > + The ending date of the time period for which the statement is + generated, in the format of MM-DD-YYYY. + batch: + type: string + description: > + The billing batch of accounts the statements need to be generated + for. + mode: + type: string + description: | + The mode in which the run has to be executed. + enum: + - open_invoices + - open_balance + - no_invoice + - no_invoice_w_balance + - custom + required: + - starting_date + - ending_date + - batch + - mode + example: + starting_date: 05-01-2018 + ending_date: 08-01-2018 + batch: Batch40 + mode: custom + POSTCustomStatementRunResponse: + type: object + properties: + success: + type: boolean + description: | + Whether the operation is successful. + code: + type: integer + description: | + The response code. + message: + type: string + description: | + The response message. + execution_number: + type: string + description: | + The serial number of the execution. + report_file: + type: string + description: | + The URL pointing to the report of the run. + NotesObject: + type: object + properties: + success: + type: boolean + description: Whether this operation is successful or not. + data: + type: object + properties: + id: + type: integer + description: The ID of the Notes object. + object: + type: string + description: >- + The type of the object. It can be "Accounts" or "Invoices" for + Collections Window. + notesId: + type: string + description: >- + The ID of the Notes object. It is an account number (32-digit + hex) for Accounts object type, and an invoice number for + Invoices object type. + notesParentId: + type: string + description: >- + The ID of the parent object. For "Accounts" object, this + parameter is the account name that the Notes object is created + for. + createdAt: + type: string + format: time-date + description: The time and date when this Notes object is created. + updatedAt: + type: string + format: time-date + description: The last time and date when the Notes object is updated. + NotesObjectWithComment: + type: object + properties: + success: + type: boolean + description: Whether this operation is successful or not. + data: + type: object + properties: + id: + type: integer + description: The ID of the Notes object. + object: + type: string + description: >- + The type of the object. It can be "Accounts" or "Invoices" for + Collections Window. + notesId: + type: string + description: >- + The ID of the notes object. It is an account number (32-digit + hex) for Accounts object type, and an invoice number for + Invoices object type. + notesParentId: + type: string + description: >- + The ID of the parent object. For "Accounts" object, this + parameter is the account name that the Notes object is created + for. + createdAt: + type: string + format: time-date + description: The time and date when this Notes object is created. + updatedAt: + type: string + format: time-date + description: The last time and date when the Notes object is updated. + comments: + type: array + description: The array of comments for this Notes object. + items: + $ref: '#/components/schemas/NotesComment' + NotesUser: + type: object + properties: + id: + type: integer + description: The ID of the Notes user. + username: + type: string + description: The user name. + connectIdentity: + type: string + description: The connect ID of the user. It must be a 32-digit hex number. + email: + type: string + description: The email address of the user. + firstName: + type: string + description: The first name. + lastName: + type: string + description: The last name. + fullName: + type: string + description: The full name of the user. + platformRole: + type: string + description: The Platform role of the user. + timeZone: + type: string + description: The time zone setting for the user. + createdAt: + type: string + format: time-date + description: The time and date when this user record is created. + updatedAt: + type: string + format: time-date + description: The last time and date when the user record is updated. + escalationUser: + type: string + description: >- + The full name of the user that is configured to handle escalations + for the current user. + escalationUserId: + type: string + description: The ID of the escalation user. It must be a 32-digit hex number. + approvalAmount: + type: number + description: The amount of money that this user is allowed to approve. + avatar: + type: string + description: The link to the avatar image file for the user. + NotesReply: + type: object + properties: + id: + type: integer + description: The ID of the reply. + body: + type: string + description: The body of the reply. + image: + type: string + description: >- + The link to the image file. The value is null if no image files are + available. + attachment: + type: string + description: >- + The link to the attachment. The value is null if no attachments are + available. + createdAt: + type: string + format: time-date + description: The time and date when the reply is created. + updatedAt: + type: string + format: time-date + description: The last time and date when the reply is updated. + user: + $ref: '#/components/schemas/NotesUser' + NotesComment: + type: object + properties: + id: + type: integer + description: The ID of the comment. + body: + type: string + description: The body of the comment. + image: + type: string + description: >- + The link to the image file. The value is null if no image files are + available. + attachment: + type: string + description: >- + The link to the attachment. The value is null if no attachments are + available. + createdAt: + type: string + format: time-date + description: The time and date when this comment is created. + updatedAt: + type: string + format: time-date + description: The last time and date when the comment is updated. + user: + description: The creator of the comment. + $ref: '#/components/schemas/NotesUser' + replies: + type: array + description: An array of replies. + items: + $ref: '#/components/schemas/NotesReply' + NotesTasksResponse: + type: object + properties: + success: + type: boolean + description: Whether this operation is successful. + data: + type: object + description: The actual tasks data. + properties: + tasks: + type: array + items: + type: object + properties: + id: + type: integer + description: The ID of the task. + task: + type: string + description: The title of the task. + description: + type: string + description: Additional details about the task. + dueDate: + type: string + format: time-date + description: The due date and time of the task. + createdAt: + type: string + format: time-date + description: The date and time when this task is created. + updatedAt: + type: string + format: time-date + description: The last date and time when the task is updated. + reminderDate: + type: string + format: time-date + description: >- + The data and time when a reminder about this task needs to + be sent. + notifications: + type: string + description: >- + The message to be sent as notifications. It can be null if + no message is defined. + status: + type: string + description: The status of the task. + comment: + type: string + description: The comment about the task. + autoEscalation: + type: string + description: The automatic escalation setting for the task. + createdBy: + $ref: '#/components/schemas/NotesUser' + assignTo: + $ref: '#/components/schemas/NotesUser' + pagination: + type: object + properties: + page: + type: integer + description: The number of the current page. + per_page: + type: integer + description: The number of results on each page. + total_filtered_records: + type: integer + description: The total number of filtered records. + total_records: + type: integer + description: The total number of records. + NotesTasks: + type: object + properties: + id: + type: integer + description: The ID of the task. + task: + type: string + description: The title of the task. + description: + type: string + description: Additional details about the task. + dueDate: + type: string + format: time-date + description: The due date and time of the task. + createdAt: + type: string + format: time-date + description: The date and time when this task is created. + updatedAt: + type: string + format: time-date + description: The last date and time when the task is updated. + reminderDate: + type: string + format: time-date + description: The data and time when a reminder about this task needs to be sent. + notifications: + type: string + description: >- + The message to be sent as notifications. It can be null if no + message is defined. + status: + type: string + description: The status of the task. + comment: + type: string + description: The comment about the task. + autoEscalation: + type: string + description: The automatic escalation setting for the task. + createdBy: + $ref: '#/components/schemas/NotesUser' + assignTo: + $ref: '#/components/schemas/NotesUser' + NotesGroup: + type: object + properties: + id: + type: integer + description: The ID of the Notes group. + name: + type: string + description: The name of the group. + description: + type: string + description: Additional information about the group. + created_at: + type: string + format: time-date + description: The date and time when the group was created. + updated_at: + type: string + format: time-date + description: The last date and time when the group was updated. + users: + type: array + description: A list of all users in the group. + items: + $ref: '#/components/schemas/NotesUser' + NotesGroupsResponse: + type: object + properties: + success: + type: boolean + description: Whether this operation is successful. + groups: + type: array + items: + $ref: '#/components/schemas/NotesGroup' + NotesCreateGroupBody: + type: object + properties: + name: + type: string + description: The name of the group that you want to create. + description: + type: string + description: Additional information about the group. + NotesUpdateGroupBody: + type: object + properties: + name: + type: string + description: The new name of the group. + description: + type: string + description: The new description about the group. + NotesDeleteGroupBody: + type: object + properties: + name: + type: string + description: The name of the group that has been deleted. + description: + type: string + description: The description of the group. + NotesCreateGroupResponse: + type: object + properties: + success: + type: boolean + description: Whether the operation is successful. + id: + type: integer + description: The ID of the Notes group. + name: + type: string + description: The name of the group. + description: + type: string + description: Additional information about the group. + created_at: + type: string + format: time-date + description: The date and time when the group was created. + updated_at: + type: string + format: time-date + description: The last date and time when the group was updated. + users: + type: array + description: An array of users in the group. + items: + $ref: '#/components/schemas/NotesUser' + ResultsPagination: + type: object + description: The pagination information. + properties: + page: + type: integer + description: The current page. + page_length: + type: integer + description: The number of result on each page. + next_page: + type: string + description: The link to the next page of results. + CollectionAccount: + type: object + properties: + name: + type: string + description: The name of the account. + zuora_id: + type: string + description: The Zuora ID of the account. + in_collections: + type: boolean + description: Whether the account is in collections. + account_currency: + type: string + description: The currency that is currently used by the account. + home_currency: + type: string + description: The home currency of the tenant. + amount_due_account_currency: + type: number + description: The amount that is due for the account in the account currency. + amount_due_home_currency: + type: number + description: The amount that is due for the account in the tenant home currency. + last_open_invoice_date: + type: string + format: date + description: The last open invoice date. + average_debt_age: + type: number + description: The average age of all the debt of the account. + statuses: + $ref: '#/components/schemas/CollectionStatuses' + CollectionStatuses: + type: object + description: The status of the account. + properties: + In Collections: + type: boolean + description: Whether this account is in collections. + Pending: + type: boolean + description: Whether this account is pending. + In Dispute: + type: boolean + description: Whether this account is in dispute. + Paid In Full: + type: boolean + description: Whether this account has paid in full. + CollectionAgent: + type: object + properties: + name: + type: string + description: The name of the agent. + email: + type: string + description: The email of the agent. + zuora_identity_id: + type: string + description: The Zuora ID of the agent. + amount_in_collections: + type: number + description: The total amount of debt for accounts that are assigned to the user. + accounts: + type: integer + description: The number of accounts that are assigned to the user. + account_statuses: + type: object + properties: + In Collections: + type: integer + description: The number of accounts that are in collections. + Pending: + type: integer + description: The number of accounts that are pending. + In Disputes: + type: integer + description: The number of accounts that are in dispute. + Paid in Full: + type: integer + description: The number of accounts that have paid in full. + GETCollectionAgentsResponse: + type: object + properties: + accounts: + $ref: '#/components/schemas/CollectionAgent' + pagination: + $ref: '#/components/schemas/ResultsPagination' + GETCollectionAccountsResponse: + type: object + properties: + accounts: + type: array + description: The array of accounts. + items: + $ref: '#/components/schemas/CollectionAccount' + pagination: + $ref: '#/components/schemas/ResultsPagination' + CollectionsInfo: + type: object + properties: + accounts_in_collections: + type: integer + description: The number of accounts that are in collections. + home_currency: + type: string + description: The home currency of the tenant. + total_debt: + type: number + description: The total debt of all accounts that are in collections. + largest_debts: + type: array + description: The five largest debts that are in collections. + items: + type: string + description: An account number and the amount due for the account. + oldest_debts: + type: array + description: The five oldest debts that are in collections. + items: + type: string + description: An account number and the amount due for the account. + statuses: + type: object + properties: + In Collection: + type: integer + description: The number of accounts that are in collections. + Pending: + type: integer + description: The number of accounts that are in pending status. + In Dispute: + type: integer + description: The number of accounts that are in dispute. + Paid In Full: + type: integer + securitySchemes: + BasicAuth: + type: http + scheme: basic diff --git a/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_advance_return_type.bal b/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_advance_return_type.bal new file mode 100644 index 000000000..8773125ab --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_advance_return_type.bal @@ -0,0 +1,421 @@ +import ballerina/http; +import ballerina/jballerina.java; + +function setModule() = @java:Method {'class: "io.ballerina.openapi.client.ModuleUtils"} external; + +function init() { + setModule(); +} + +type ClientMethodImpl record {| + string name; +|}; + +annotation ClientMethodImpl MethodImpl on function; + +type ClientMethodInvocationError http:ClientError; + +public isolated client class Client { + # Gets invoked to initialize the `connector`. + # + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(ConnectionConfig config, string serviceUrl) returns error? { + return; + } + + # Update account agent + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + @MethodImpl {name: "UpdateAccountAgentImpl"} + resource isolated function post api/v1/accounts/[string account_id]/update_agent(map headers = {}, typedesc targetType = <>, *UpdateAccountAgentQueries queries) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Update account status + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + @MethodImpl {name: "UpdateAccountImpl"} + resource isolated function post api/v1/accounts/[string account_id]/update_status(map headers = {}, typedesc targetType = <>, *UpdateAccountQueries queries) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Execute payments + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "UpdateExecutePaymentsImpl"} + resource isolated function post api/v1/payments/execute_payments(payments_execute_payments_body payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Submit a payment to retry cycle + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "UpdateSubmitPaymentToCycleImpl"} + resource isolated function post api/v1/payments/submit_failed_payment(payments_submit_failed_payment_body payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Create an ad hoc statement run + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "createAdhocStatementRunImpl"} + resource isolated function post api/v1/run(POSTAdhocStatementRun payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Create a custom statement run + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "createCustomStatementRunImpl"} + resource isolated function post api/v1/execute(POSTCustomStatementRun payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Create a payment run + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "createPaymentRunImpl"} + resource isolated function post api/v1/subscription_payment_runs(POSTPaymentRun payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Create a payment run schedule + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "createPaymentRunScheduleImpl"} + resource isolated function post api/v1/payment_run_schedules(POSTPaymentRunSchedule payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Cancel a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + @MethodImpl {name: "deletePaymentRunScheduleImpl"} + resource isolated function delete api/v1/payment_run_schedules/[int schedule_id](map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "executeExecuteAccountPaymentsImpl"} + resource isolated function put api/v1/payments/execute_account_payments/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "executeExecuteDebitMemoPaymentImpl"} + resource isolated function put api/v1/payments/execute_debit_memo_payment/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "executeExecuteInvoicePaymentImpl"} + resource isolated function put api/v1/payments/execute_invoice_payment/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "getAccountCycleHistoryImpl"} + resource isolated function get api/v1/payments/account_cycle_history/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get an account + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + @MethodImpl {name: "getAccountImpl"} + resource isolated function get api/v1/accounts/[string account_id](map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get all accounts + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getAccountsImpl"} + resource isolated function get api/v1/accounts(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + @MethodImpl {name: "getActiveAccountCycleImpl"} + resource isolated function get api/v1/payments/active_account_cycle_information/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "getActiveDebitMemoCycleImpl"} + resource isolated function get api/v1/payments/active_debit_memo_cycle_information/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "getActiveInvoiceCycleImpl"} + resource isolated function get api/v1/payments/active_invoice_cycle_information/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get the Amount Recovered metrics + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getAmountRecoveredImpl"} + resource isolated function get api/v1/metrics/amount_recovered(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Get baseline metrics + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getBaselineMetricsImpl"} + resource isolated function get api/v1/metrics/baseline(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Get a collections agent by email + # + # + email - The email of the collections agent. + # + headers - Headers to be sent with the request + @MethodImpl {name: "getCollectionsAgentImpl"} + resource isolated function get api/v1/users/[string email](map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get all collections agents + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getCollectionsAgentsImpl"} + resource isolated function get api/v1/users(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Get an overview of collections + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getCollectionsInfoImpl"} + resource isolated function get api/v1/collections_info(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Get the Customer Group metrics + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getCustomerGroupMetricsImpl"} + resource isolated function get api/v1/metrics/customer_group(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + @MethodImpl {name: "getDebitMemoCycleHistoryImpl"} + resource isolated function get api/v1/payments/debit_memo_cycle_history/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get the Document Success Rate metrics by customer group + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getDocumentSuccessRateByCustomerGroupImpl"} + resource isolated function get api/v1/metrics/customer_group_over_time(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + @MethodImpl {name: "getInvoiceCycleHistoryImpl"} + resource isolated function get api/v1/payments/invoice_cycle_history/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get a payment run + # + # + payment_run_id - The payment run ID. A payment run id in Advanced Payment Manager is different from a payment run id in Zuora. + # + headers - Headers to be sent with the request + @MethodImpl {name: "getPaymentRunImpl"} + resource isolated function get api/v1/subscription_payment_runs/[int payment_run_id](map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + @MethodImpl {name: "getPaymentRunScheduleImpl"} + resource isolated function get api/v1/payment_run_schedules/[int schedule_id](map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Get all payment run schedules + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getPaymentRunSchedulesImpl"} + resource isolated function get api/v1/payment_run_schedules(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + # Get Statement Generator settings + # + # + headers - Headers to be sent with the request + @MethodImpl {name: "getSettingsImpl"} + resource isolated function get api/v1/fetch_settings(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResourceWithoutPath"} external; + + @MethodImpl {name: "removeAccountFromCycleImpl"} + resource isolated function put api/v1/payments/remove_account_from_retry_cycle/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "removeDebitMemoFromCycleImpl"} + resource isolated function put api/v1/payments/remove_debit_memo_from_retry_cycle/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + @MethodImpl {name: "removeRemoveInoviceFromCycleImpl"} + resource isolated function put api/v1/payments/remove_invoice_from_retry_cycle/\(map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + # Update a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + @MethodImpl {name: "updatePaymentRunScheduleImpl"} + resource isolated function put api/v1/payment_run_schedules/[int schedule_id](PUTPaymentRunSchedule payload, map headers = {}, typedesc targetType = <>) returns targetType|error = @java:Method {'class: "io.ballerina.openapi.client.GeneratedClient", name: "invokeResource"} external; + + private isolated function UpdateAccountAgentImpl(string account_id, map headers, typedesc targetType, *UpdateAccountAgentQueries queries) returns http:StatusCodeResponse|error { + return { + body: {"success": true} + }; + } + + private isolated function UpdateAccountImpl(string account_id, map headers, typedesc targetType, *UpdateAccountQueries queries) returns http:StatusCodeResponse|error { + return { + body: {"success": true} + }; + } + + private isolated function UpdateExecutePaymentsImpl(payments_execute_payments_body payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs enqueued for processing: [100, 101, 110, 111, 121]"} + }; + } + + private isolated function UpdateSubmitPaymentToCycleImpl(payments_submit_failed_payment_body payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payment entered into retry process"} + }; + } + + private isolated function createAdhocStatementRunImpl(POSTAdhocStatementRun payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "code": 200, "message": "Success", "statement_number": "STA00000008"} + }; + } + + private isolated function createCustomStatementRunImpl(POSTCustomStatementRun payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "code": 200, "message": "Success", "execution_number": "2", "report_file": ""} + }; + } + + private isolated function createPaymentRunImpl(POSTPaymentRun payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"id": 6, "success": "true"} + }; + } + + private isolated function createPaymentRunScheduleImpl(POSTPaymentRunSchedule payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"id": 6, "success": "true"} + }; + } + + private isolated function deletePaymentRunScheduleImpl(int schedule_id, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true} + }; + } + + private isolated function executeExecuteAccountPaymentsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs enqueued for processing: [310, 311, 312]"} + }; + } + + private isolated function executeExecuteDebitMemoPaymentImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs enqueued for processing: [300]"} + }; + } + + private isolated function executeExecuteInvoicePaymentImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs enqueued for processing: [290, 291]"} + }; + } + + private isolated function getAccountCycleHistoryImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 3, "next_attempt": "2021-03-19T18:53:39.641Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:43:28.670-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bcdfc010671", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fb78532b0f01785a38cede190a", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-03-24T19:34:20.254Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51aae41969", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.316-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getAccountImpl(string account_id, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"name": "Testing Account", "zuora_id": "2c92c0f863f2b1680163f579b7e705da", "in_collections": true, "collections_agent": "2018-01-02T00:00:00.000Z", "account_currency": "CAD", "home_currency": "USD", "amount_due_account_currency": 15540, "amount_due_home_currency": 800.55, "last_open_invoice_date": "2018-06-12", "average_debt_age": "194.4 days", "statuses": {"In Collections": true, "Pending": false, "In Dispute": false, "Paid In Full": false}} + }; + } + + private isolated function getAccountsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"accounts": [{"name": "Testing Account", "zuora_id": "2c92c0f863f2b1680163f579b7e705da", "in_collections": true, "collections_agent": "2018-01-02T00:00:00.000Z", "account_currency": "CAD", "home_currency": "USD", "amount_due_account_currency": 15540, "amount_due_home_currency": 800.55, "last_open_invoice_date": "2018-06-12", "average_debt_age": "194.4 days", "statuses": {"In Collections": 0, "Pending": 0, "In Dispute": 0, "Paid In Full": 0}}], "pagination": {"page": 1, "page_length": 20, "next_page": "https://collections-window.apps.zuora.com/api/v1/accounts?page=2&page_length=20"}} + }; + } + + private isolated function getActiveAccountCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "debit_memo_id": "2c92c0fb78532b0001785a38f6427976", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-04-01T19:27:34.473Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a5199a6192d", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:27.521-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "invoice_id": "2c92c0fa7853052701785a38c6622473", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-04-01T19:27:34.436Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a519d85193b", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:28.161-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getActiveDebitMemoCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f868e161e70168e25eb51d755f", "debit_memo_id": "2c92c0fa7853052701785a38f3bb267f", "payment_method_id": "2c92c0f8774f1afe01775f6e533c4c06", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 2, "next_attempt": "2021-04-01T10:22:57.464Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51af791979", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.380-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c0857881cf3e01788ee263b0331c", "time_of_execution": "2021-04-01T19:21:55.207Z", "source": "PR-00000380", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-04-01T10:22:57.464-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getActiveInvoiceCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "invoice_id": "2c92c0f8778bf8cd017798168cb50e0b", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 2, "next_attempt": "2021-04-01T19:27:34.648Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ca076c21", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-04-01T19:27:34.648-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bce0d0a06d5", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:53:39.845-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getAmountRecoveredImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"currency": {"USD": {"total_amount": "77515.21", "last_30_days": "1100.01"}, "EUR": {"total_amount": "337.19", "last_30_days": "17.17"}, "CAD": {"total_amount": "123954.10", "last_30_days": "5132.87"}}, "success": true} + }; + } + + private isolated function getBaselineMetricsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"retry_success_rate": "11.90", "retry_success_rate_trend": "down", "document_success_rate": "13.54", "document_success_rate_trend": "neutral", "average_days_outstanding": "4.76", "average_days_outstanding_trend": "up", "success": true} + }; + } + + private isolated function getCollectionsAgentImpl(string email, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"name": "Testing User", "email": "test@zuora.com", "zuora_identity_id": "2c92c0f96178a7a901619b10f5d12345", "amount_in_collections": 800.55, "accounts": 2, "account_statuses": {"In Collections": 1, "Pending": 0, "In Dispute": 1, "Paid In Full": 0}} + }; + } + + private isolated function getCollectionsAgentsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"accounts": [{"name": "Testing User", "email": "test@zuora.com", "zuora_identity_id": "2c92c0f96178a7a901619b10f5d12345", "amount_in_collections": 800.55, "accounts": 2, "account_statuses": {"In Collections": 1, "Pending": 0, "In Dispute": 1, "Paid In Full": 0}}], "pagination": {"page": 1, "page_length": 20, "next_page": "https://collections-window.apps.zuora.com/api/v1/users?page=2&page_length=20"}} + }; + } + + private isolated function getCollectionsInfoImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"accounts_in_collections": 24, "home_currency": "USD", "total_debt": "8379.78", "largest_debts": ["Test Account - 12438.00 USD", "Jimmy John - 8000.00 USD", "James Smith - 2450.55 USD", "Bob Roberts - 1000.00 USD", "Jim Reynolds - 829.00 USD"], "oldest_debts": ["Test Account - 2662 days", "Jimbo - 1494 days", "Steve Smith - 942 days", "Jason Williams - 678 days", "Will Jasons - 365 days"], "statuses": {"In Collections": 24, "Pending": 2, "In Dispute": 5, "Paid in Full": 0}} + }; + } + + private isolated function getCustomerGroupMetricsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"customer_groups": [{"id": 1, "name": "batch22", "smart_retry": false, "document_success_rate": "17.17", "document_success_rate_trend": "up", "retry_success_rate": "21.76", "average_attempts": "4.11"}, {"id": 2, "name": "Smart Retry", "smart_retry": true, "document_success_rate": "74.17", "document_success_rate_trend": "down", "retry_success_rate": "81.21", "average_attempts": "1.32"}, {"id": 4, "name": "All Remaining Customers", "smart_retry": false, "document_success_rate": "16.35", "document_success_rate_trend": "up", "retry_success_rate": "15.21", "average_attempts": "3.32"}], "success": true} + }; + } + + private isolated function getDebitMemoCycleHistoryImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f868e161e70168e25eb51d755f", "debit_memo_id": "2c92c0fa7853052701785a38f3bb267f", "payment_method_id": "2c92c0f8774f1afe01775f6e533c4c06", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 2, "next_attempt": null, "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51af791979", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.380-09:00", "criteria": "incremental_time", "api_updated_retry_time": "2021-03-26T14:27:21.107-04:00"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c0857881cf3e01788ee263b0331c", "time_of_execution": "2021-04-01T19:21:55.207Z", "source": "PR-00000378", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getDocumentSuccessRateByCustomerGroupImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"customer_groups": {"1": {"05_21": "17.53", "04_21": "13.21", "03_21": "14.92", "02_21": "8.99", "01_21": "34.25", "12_20": "12.30"}, "2": {"05_21": "11.11", "04_21": "7.87", "03_21": "26.00", "02_21": "11.06", "01_21": "13.43", "12_20": "17.92"}, "4": {"05_21": "11.13", "04_21": "9.17", "03_21": "17.20", "02_21": "19.06", "01_21": "12.43", "12_20": "15.92"}}, "success": true} + }; + } + + private isolated function getInvoiceCycleHistoryImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"cycles": [{"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 1, "next_attempt": null, "customer_group": "Testing Group", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-04-01T19:11:21.639Z", "source": "PR-00000370", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 5}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 2, "next_attempt": null, "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:43:28.670-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bcdfc010671", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]} + }; + } + + private isolated function getPaymentRunImpl(int payment_run_id, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "id": 6, "status": "Complete", "target_date": "2018-01-02T00:00:00.000Z", "filter": "Account.Currency = 'CAD'", "payment_run_schedule_id": "Adhoc", "invoices_held": {}, "metrics": {"documents": 0, "payments": 0, "failed": 0, "skipped": 0, "amount": 0, "credit": 0}} + }; + } + + private isolated function getPaymentRunScheduleImpl(int schedule_id, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "id": 6, "status": "Active", "filter": "Account.BillCycleDay = 8", "schedule": "At 6:00 AM, only on Monday and Tuesday"} + }; + } + + private isolated function getPaymentRunSchedulesImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "size": 3, "schedules": [{"id": 6, "status": "Active", "filter": "Account.BillCycleDay = 8", "schedule": "At 6:00 AM, only on Monday and Tuesday"}]} + }; + } + + private isolated function getSettingsImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "templates": [{"name": "Default Template"}, {"name": "Template for end consumers"}], "default_template": "Default Template", "default_cycle": "Month"} + }; + } + + private isolated function removeAccountFromCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [310, 311, 312]"} + }; + } + + private isolated function removeDebitMemoFromCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [301]"} + }; + } + + private isolated function removeRemoveInoviceFromCycleImpl(map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [290, 291]"} + }; + } + + private isolated function updatePaymentRunScheduleImpl(int schedule_id, PUTPaymentRunSchedule payload, map headers, typedesc targetType) returns http:StatusCodeResponse|error { + return { + body: {"id": 6, "success": true} + }; + } +} diff --git a/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_remote.bal b/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_remote.bal new file mode 100644 index 000000000..d7569004f --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/mock_client_for_remote.bal @@ -0,0 +1,278 @@ + +public isolated client class Client { + # Gets invoked to initialize the `connector`. + # + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(ConnectionConfig config, string serviceUrl) returns error? { + return; + } + + # Update account status + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + remote isolated function UpdateAccount(string account_id, map headers = {}, *UpdateAccountQueries queries) returns inline_response_200|error { + return {"success": true}; + } + + # Update account agent + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + remote isolated function UpdateAccountAgent(string account_id, map headers = {}, *UpdateAccountAgentQueries queries) returns inline_response_200|error { + return {"success": true}; + } + + # Execute payments + # + # + headers - Headers to be sent with the request + remote isolated function UpdateExecutePayments(payments_execute_payments_body payload, map headers = {}) returns inline_response_200_4|error { + return {"success": true, "message": "Payments with the following IDs enqueued for processing: [100, 101, 110, 111, 121]"}; + } + + # Submit a payment to retry cycle + # + # + headers - Headers to be sent with the request + remote isolated function UpdateSubmitPaymentToCycle(payments_submit_failed_payment_body payload, map headers = {}) returns inline_response_200_8|error { + return {"success": true, "message": "Payment entered into retry process"}; + } + + # Create an ad hoc statement run + # + # + headers - Headers to be sent with the request + remote isolated function createAdhocStatementRun(POSTAdhocStatementRun payload, map headers = {}) returns POSTAdhocStatementRunResponse|error { + return {"success": true, "code": 200, "message": "Success", "statement_number": "STA00000008"}; + } + + # Create a custom statement run + # + # + headers - Headers to be sent with the request + remote isolated function createCustomStatementRun(POSTCustomStatementRun payload, map headers = {}) returns POSTCustomStatementRunResponse|error { + return {"success": true, "code": 200, "message": "Success", "execution_number": "2", "report_file": ""}; + } + + # Create a payment run + # + # + headers - Headers to be sent with the request + remote isolated function createPaymentRun(POSTPaymentRun payload, map headers = {}) returns POSTPaymentRunResponse|error { + return {"id": 6, "success": "true"}; + } + + # Create a payment run schedule + # + # + headers - Headers to be sent with the request + remote isolated function createPaymentRunSchedule(POSTPaymentRunSchedule payload, map headers = {}) returns POSTPaymentRunScheduleResponse|error { + return {"id": 6, "success": "true"}; + } + + # Cancel a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + remote isolated function deletePaymentRunSchedule(int schedule_id, map headers = {}) returns DELETEPaymentRunScheduleResponse|error { + return {"success": true}; + } + + # Execute account payments + # + # + account_id - ID of an account. + # + headers - Headers to be sent with the request + remote isolated function executeExecuteAccountPayments(string account_id, map headers = {}) returns inline_response_200_3|error { + return {"success": true, "message": "Payments with the following IDs enqueued for processing: [310, 311, 312]"}; + } + + # Execute debit memo payment + # + # + debit_memo_id - ID of a debit memo. + # + headers - Headers to be sent with the request + remote isolated function executeExecuteDebitMemoPayment(string debit_memo_id, map headers = {}) returns inline_response_200_2|error { + return {"success": true, "message": "Payments with the following IDs enqueued for processing: [300]"}; + } + + # Execute invoice payment + # + # + invoice_id - ID of an invoice. + # + headers - Headers to be sent with the request + remote isolated function executeExecuteInvoicePayment(string invoice_id, map headers = {}) returns inline_response_200_1|error { + return {"success": true, "message": "Payments with the following IDs enqueued for processing: [290, 291]"}; + } + + # Get an account + # + # + account_id - The account ID. + # + headers - Headers to be sent with the request + remote isolated function getAccount(string account_id, map headers = {}) returns CollectionAccount|error { + return {"name": "Testing Account", "zuora_id": "2c92c0f863f2b1680163f579b7e705da", "in_collections": true, "collections_agent": "2018-01-02T00:00:00.000Z", "account_currency": "CAD", "home_currency": "USD", "amount_due_account_currency": 15540, "amount_due_home_currency": 800.55, "last_open_invoice_date": "2018-06-12", "average_debt_age": "194.4 days", "statuses": {"In Collections": true, "Pending": false, "In Dispute": false, "Paid In Full": false}}; + } + + # Get retry cycle history for an account + # + # + account_id - ID of an account. + # + headers - Headers to be sent with the request + remote isolated function getAccountCycleHistory(string account_id, map headers = {}) returns GETAccountCycleHistoryResponse|error { + return {"cycles": [{"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 3, "next_attempt": "2021-03-19T18:53:39.641Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:43:28.670-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bcdfc010671", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fb78532b0f01785a38cede190a", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-03-24T19:34:20.254Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51aae41969", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.316-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get all accounts + # + # + headers - Headers to be sent with the request + remote isolated function getAccounts(map headers = {}) returns GETCollectionAccountsResponse|error { + return {"accounts": [{"name": "Testing Account", "zuora_id": "2c92c0f863f2b1680163f579b7e705da", "in_collections": true, "collections_agent": "2018-01-02T00:00:00.000Z", "account_currency": "CAD", "home_currency": "USD", "amount_due_account_currency": 15540, "amount_due_home_currency": 800.55, "last_open_invoice_date": "2018-06-12", "average_debt_age": "194.4 days", "statuses": {"In Collections": 0, "Pending": 0, "In Dispute": 0, "Paid In Full": 0}}], "pagination": {"page": 1, "page_length": 20, "next_page": "https://collections-window.apps.zuora.com/api/v1/accounts?page=2&page_length=20"}}; + } + + # Get active retry cycles for an account + # + # + account_id - ID of an account. + # + headers - Headers to be sent with the request + remote isolated function getActiveAccountCycle(string account_id, map headers = {}) returns GETActiveAccountCycleResponse|error { + return {"cycles": [{"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "debit_memo_id": "2c92c0fb78532b0001785a38f6427976", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-04-01T19:27:34.473Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a5199a6192d", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:27.521-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "invoice_id": "2c92c0fa7853052701785a38c6622473", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 1, "next_attempt": "2021-04-01T19:27:34.436Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a519d85193b", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:28.161-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get active retry cycles for a debit memo + # + # + debit_memo_id - ID of a debit memo. + # + headers - Headers to be sent with the request + remote isolated function getActiveDebitMemoCycle(string debit_memo_id, map headers = {}) returns GETActiveDebitMemoCycleResponse|error { + return {"cycles": [{"account_id": "2c92c0f868e161e70168e25eb51d755f", "debit_memo_id": "2c92c0fa7853052701785a38f3bb267f", "payment_method_id": "2c92c0f8774f1afe01775f6e533c4c06", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 2, "next_attempt": "2021-04-01T10:22:57.464Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51af791979", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.380-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c0857881cf3e01788ee263b0331c", "time_of_execution": "2021-04-01T19:21:55.207Z", "source": "PR-00000380", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-04-01T10:22:57.464-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get active retry cycles for an invoice + # + # + invoice_id - ID of an invoice. + # + headers - Headers to be sent with the request + remote isolated function getActiveInvoiceCycle(string invoice_id, map headers = {}) returns GETActiveInvoiceCycleResponse|error { + return {"cycles": [{"account_id": "2c92c0f96bd69165016bdccdd6ce2f29", "invoice_id": "2c92c0f8778bf8cd017798168cb50e0b", "payment_method_id": "2c92c0f9774f2b3e01775f6f06d87b61", "currency": "USD", "status": "Cycle Incomplete", "current_attempt_number": 2, "next_attempt": "2021-04-01T19:27:34.648Z", "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ca076c21", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-04-01T19:27:34.648-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bce0d0a06d5", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:53:39.845-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get the Amount Recovered metrics + # + # + headers - Headers to be sent with the request + remote isolated function getAmountRecovered(map headers = {}) returns GETAmountRecoveredResponse|error { + return {"currency": {"USD": {"total_amount": "77515.21", "last_30_days": "1100.01"}, "EUR": {"total_amount": "337.19", "last_30_days": "17.17"}, "CAD": {"total_amount": "123954.10", "last_30_days": "5132.87"}}, "success": true}; + } + + # Get baseline metrics + # + # + headers - Headers to be sent with the request + remote isolated function getBaselineMetrics(map headers = {}) returns GETBaselineMetricsResponse|error { + return {"retry_success_rate": "11.90", "retry_success_rate_trend": "down", "document_success_rate": "13.54", "document_success_rate_trend": "neutral", "average_days_outstanding": "4.76", "average_days_outstanding_trend": "up", "success": true}; + } + + # Get a collections agent by email + # + # + email - The email of the collections agent. + # + headers - Headers to be sent with the request + remote isolated function getCollectionsAgent(string email, map headers = {}) returns CollectionAgent|error { + return {"name": "Testing User", "email": "test@zuora.com", "zuora_identity_id": "2c92c0f96178a7a901619b10f5d12345", "amount_in_collections": 800.55, "accounts": 2, "account_statuses": {"In Collections": 1, "Pending": 0, "In Dispute": 1, "Paid In Full": 0}}; + } + + # Get all collections agents + # + # + headers - Headers to be sent with the request + remote isolated function getCollectionsAgents(map headers = {}) returns GETCollectionAgentsResponse|error { + return {"accounts": [{"name": "Testing User", "email": "test@zuora.com", "zuora_identity_id": "2c92c0f96178a7a901619b10f5d12345", "amount_in_collections": 800.55, "accounts": 2, "account_statuses": {"In Collections": 1, "Pending": 0, "In Dispute": 1, "Paid In Full": 0}}], "pagination": {"page": 1, "page_length": 20, "next_page": "https://collections-window.apps.zuora.com/api/v1/users?page=2&page_length=20"}}; + } + + # Get an overview of collections + # + # + headers - Headers to be sent with the request + remote isolated function getCollectionsInfo(map headers = {}) returns CollectionsInfo|error { + return {"accounts_in_collections": 24, "home_currency": "USD", "total_debt": "8379.78", "largest_debts": ["Test Account - 12438.00 USD", "Jimmy John - 8000.00 USD", "James Smith - 2450.55 USD", "Bob Roberts - 1000.00 USD", "Jim Reynolds - 829.00 USD"], "oldest_debts": ["Test Account - 2662 days", "Jimbo - 1494 days", "Steve Smith - 942 days", "Jason Williams - 678 days", "Will Jasons - 365 days"], "statuses": {"In Collections": 24, "Pending": 2, "In Dispute": 5, "Paid in Full": 0}}; + } + + # Get the Customer Group metrics + # + # + headers - Headers to be sent with the request + remote isolated function getCustomerGroupMetrics(map headers = {}) returns GETCustomerGroupMetricsResponse|error { + return {"customer_groups": [{"id": 1, "name": "batch22", "smart_retry": false, "document_success_rate": "17.17", "document_success_rate_trend": "up", "retry_success_rate": "21.76", "average_attempts": "4.11"}, {"id": 2, "name": "Smart Retry", "smart_retry": true, "document_success_rate": "74.17", "document_success_rate_trend": "down", "retry_success_rate": "81.21", "average_attempts": "1.32"}, {"id": 4, "name": "All Remaining Customers", "smart_retry": false, "document_success_rate": "16.35", "document_success_rate_trend": "up", "retry_success_rate": "15.21", "average_attempts": "3.32"}], "success": true}; + } + + # Get retry cycle history for a debit memo + # + # + debit_memo_id - ID of a debit memo. + # + headers - Headers to be sent with the request + remote isolated function getDebitMemoCycleHistory(string debit_memo_id, map headers = {}) returns GETDebitMemoCycleHistoryResponse|error { + return {"cycles": [{"account_id": "2c92c0f868e161e70168e25eb51d755f", "debit_memo_id": "2c92c0fa7853052701785a38f3bb267f", "payment_method_id": "2c92c0f8774f1afe01775f6e533c4c06", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 2, "next_attempt": null, "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c085785305e201785a51af791979", "time_of_execution": "2021-03-23T16:50:18.878Z", "source": "PR-00000376", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-23T07:51:30.380-09:00", "criteria": "incremental_time", "api_updated_retry_time": "2021-03-26T14:27:21.107-04:00"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c0857881cf3e01788ee263b0331c", "time_of_execution": "2021-04-01T19:21:55.207Z", "source": "PR-00000378", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get the Document Success Rate metrics by customer group + # + # + headers - Headers to be sent with the request + remote isolated function getDocumentSuccessRateByCustomerGroup(map headers = {}) returns GETDocumentSuccessRateByCustomerGroupResponse|error { + return {"customer_groups": {"1": {"05_21": "17.53", "04_21": "13.21", "03_21": "14.92", "02_21": "8.99", "01_21": "34.25", "12_20": "12.30"}, "2": {"05_21": "11.11", "04_21": "7.87", "03_21": "26.00", "02_21": "11.06", "01_21": "13.43", "12_20": "17.92"}, "4": {"05_21": "11.13", "04_21": "9.17", "03_21": "17.20", "02_21": "19.06", "01_21": "12.43", "12_20": "15.92"}}, "success": true}; + } + + # Get retry cycle history for an invoice + # + # + invoice_id - ID of an invoice. + # + headers - Headers to be sent with the request + remote isolated function getInvoiceCycleHistory(string invoice_id, map headers = {}) returns GETInvoiceCycleHistoryResponse|error { + return {"cycles": [{"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 1, "next_attempt": null, "customer_group": "Testing Group", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-04-01T19:11:21.639Z", "source": "PR-00000370", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 5}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}, {"account_id": "2c92c0f96bd69165016bdcbf55ad5e62", "invoice_id": "2c92c0fa7849b3ff01784bc5e8ee18b5", "payment_method_id": "2c92c0f9774f2b3e01775f6cf2fb726a", "currency": "USD", "status": "Cycle Complete", "current_attempt_number": 2, "next_attempt": null, "customer_group": "All Remaining Customers", "attempts": [{"attempt_number": 1, "zuora_payment_id": "2c92c0867849d42301784bc9ce806c31", "time_of_execution": "2021-03-19T18:42:20.103Z", "source": "PR-00000371", "cpr_generated": false, "success": false, "amount_collected": "0.0", "action_info": {"action": "Retry"}, "retry_info": {"next": "2021-03-19T09:43:28.670-09:00", "criteria": "incremental_time"}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}, {"attempt_number": 2, "zuora_payment_id": "2c92c09c7849d3c101784bcdfc010671", "time_of_execution": "2021-03-19T18:52:24.137Z", "source": "PR-00000372", "cpr_generated": true, "success": false, "amount_collected": "0.0", "action_info": {"action": "Stop"}, "retry_info": {}, "mapping_info": {"label": "Hard Decline", "level": "code", "customer_group_id": 1}, "gateway_info": {"id": "2c92c0f85e2d19af015e3a61d8947e5d", "code": "insufficient_funds", "response": "Your card has insufficient funds."}}]}]}; + } + + # Get a payment run + # + # + payment_run_id - The payment run ID. A payment run id in Advanced Payment Manager is different from a payment run id in Zuora. + # + headers - Headers to be sent with the request + remote isolated function getPaymentRun(int payment_run_id, map headers = {}) returns GETPaymentRunResponse|error { + return {"success": true, "id": 6, "status": "Complete", "target_date": "2018-01-02T00:00:00.000Z", "filter": "Account.Currency = 'CAD'", "payment_run_schedule_id": "Adhoc", "invoices_held": {}, "metrics": {"documents": 0, "payments": 0, "failed": 0, "skipped": 0, "amount": 0, "credit": 0}}; + } + + # Get a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + remote isolated function getPaymentRunSchedule(int schedule_id, map headers = {}) returns GETPaymentRunScheduleResponse|error { + return {"success": true, "id": 6, "status": "Active", "filter": "Account.BillCycleDay = 8", "schedule": "At 6:00 AM, only on Monday and Tuesday"}; + } + + # Get all payment run schedules + # + # + headers - Headers to be sent with the request + remote isolated function getPaymentRunSchedules(map headers = {}) returns GETPaymentRunSchedulesResponse|error { + return {"success": true, "size": 3, "schedules": [{"id": 6, "status": "Active", "filter": "Account.BillCycleDay = 8", "schedule": "At 6:00 AM, only on Monday and Tuesday"}]}; + } + + # Get Statement Generator settings + # + # + headers - Headers to be sent with the request + remote isolated function getSettings(map headers = {}) returns GETStatementSettingsResponse|error { + return {"success": true, "templates": [{"name": "Default Template"}, {"name": "Template for end consumers"}], "default_template": "Default Template", "default_cycle": "Month"}; + } + + # Remove an account from retry cycle + # + # + account_id - ID of an account. + # + headers - Headers to be sent with the request + remote isolated function removeAccountFromCycle(string account_id, map headers = {}) returns inline_response_200_7|error { + return {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [310, 311, 312]"}; + } + + # Remove a debit memo from retry cycle + # + # + debit_memo_id - ID of a debit memo. + # + headers - Headers to be sent with the request + remote isolated function removeDebitMemoFromCycle(string debit_memo_id, map headers = {}) returns inline_response_200_6|error { + return {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [301]"}; + } + + # Remove an invoice from retry cycle + # + # + invoice_id - ID of an invoice. + # + headers - Headers to be sent with the request + remote isolated function removeRemoveInoviceFromCycle(string invoice_id, map headers = {}) returns inline_response_200_5|error { + return {"success": true, "message": "Payments with the following IDs have been removed from the retry cycle: [290, 291]"}; + } + + # Update a payment run schedule + # + # + schedule_id - The schedule ID + # + headers - Headers to be sent with the request + remote isolated function updatePaymentRunSchedule(int schedule_id, PUTPaymentRunSchedule payload, map headers = {}) returns POSTPaymentRunScheduleResponse|error { + return {"id": 6, "success": true}; + } +} diff --git a/openapi-cli/src/test/resources/generators/client/mock/ref_example.json b/openapi-cli/src/test/resources/generators/client/mock/ref_example.json new file mode 100644 index 000000000..ee48f78c4 --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/ref_example.json @@ -0,0 +1,2437 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Opendatasoft's Explore API Reference Documentation", + "version": "v2.1", + "contact": { + "email": "support@opendatasoft.com" + }, + "license": { + "name": "Copyright Opendatasoft" + } + }, + "servers": [ + { + "url": "https://documentation-resources.opendatasoft.com/api/explore/v2.1", + "description": "Portal that hosts example datasets" + } + ], + "security": [ + { + "apikey": [] + } + ], + "tags": [ + { + "name": "Catalog", + "description": "API to enumerate datasets" + }, + { + "name": "Dataset", + "description": "API to work on records" + } + ], + "paths": { + "/catalog/datasets": { + "get": { + "summary": "Query catalog datasets", + "operationId": "getDatasets", + "tags": [ + "Catalog" + ], + "description": "Retrieve available datasets.", + "parameters": [ + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + }, + { + "$ref": "#/components/parameters/group_by" + }, + { + "$ref": "#/components/parameters/include_links" + }, + { + "$ref": "#/components/parameters/include_app_metas" + } + ], + "responses": { + "200": { + "description": "A list of available datasets", + "content": { + "application/json; charset=utf-8": { + "schema": { + "$ref": "#/components/schemas/results_dataset-v2.1" + }, + "examples": { + "datasets": { + "$ref": "#/components/examples/datasets-v2.1" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/exports": { + "get": { + "summary": "List export formats", + "operationId": "listExportFormats", + "tags": [ + "Catalog" + ], + "description": "List available export formats", + "responses": { + "200": { + "description": "A list of available export formats", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/exports/{format}": { + "get": { + "summary": "Export a catalog", + "operationId": "exportDatasets", + "tags": [ + "Catalog" + ], + "description": "Export a catalog in the desired format.", + "parameters": [ + { + "$ref": "#/components/parameters/format-catalog-v2.1" + }, + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/group_by" + }, + { + "$ref": "#/components/parameters/limit_export" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/exports/csv": { + "get": { + "summary": "Export a catalog in CSV", + "operationId": "exportCatalogCSV", + "tags": [ + "Catalog" + ], + "description": "Export a catalog in CSV (Comma Separated Values). Specific parameters are described here", + "parameters": [ + { + "name": "delimiter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + ";", + ",", + "\t", + "|" + ], + "default": ";" + }, + "description": "Sets the field delimiter of the CSV export" + }, + { + "name": "list_separator", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "," + }, + "description": "Sets the separator character used for multivalued strings" + }, + { + "name": "quote_all", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "description": "Set it to true to force quoting all strings, i.e. surrounding all strings with quote characters" + }, + { + "name": "with_bom", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true + }, + "description": "Set it to true to force the first characters of the CSV file to be a Unicode Byte Order Mask (0xFEFF). It usually makes Excel correctly open the output CSV file without warning.\n**Warning:** the default value of this parameter is `false` in v2.0 and `true` starting with v2.1" + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/exports/dcat{dcat_ap_format}": { + "get": { + "summary": "Export a catalog in RDF/XML (DCAT)", + "operationId": "exportCatalogDCAT", + "tags": [ + "Catalog" + ], + "description": "Export a catalog in RDF/XML described with DCAT (Data Catalog Vocabulary). Specific parameters are described here", + "parameters": [ + { + "$ref": "#/components/parameters/dcat_format" + }, + { + "name": "include_exports", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/enum-format-datasets-v2.1" + }, + "description": "Sets the datasets exports exposed in the DCAT export. By default, all exports are exposed.", + "examples": { + "legacy": { + "summary": "Only expose csv, json and geojson datasets exports", + "value": "csv,json,geojson" + } + } + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/facets": { + "get": { + "summary": "List facet values", + "operationId": "getDatasetsFacets", + "tags": [ + "Catalog" + ], + "description": "Enumerate facet values for datasets and returns a list of values for each facet.\nCan be used to implement guided navigation in large result sets.", + "parameters": [ + { + "$ref": "#/components/parameters/facet" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/timezone" + } + ], + "responses": { + "200": { + "description": "An enumeration of facets", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "facets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/facet_enumeration" + } + } + } + }, + "examples": { + "catalog_facets": { + "$ref": "#/components/examples/catalog_facets" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/records": { + "get": { + "summary": "Query dataset records", + "operationId": "getRecords", + "tags": [ + "Dataset" + ], + "description": "Perform a query on dataset records.", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/group_by" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/limit" + }, + { + "$ref": "#/components/parameters/offset" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + }, + { + "$ref": "#/components/parameters/include_links" + }, + { + "$ref": "#/components/parameters/include_app_metas" + } + ], + "responses": { + "200": { + "description": "Records", + "content": { + "application/json; charset=utf-8": { + "schema": { + "$ref": "#/components/schemas/results-v2.1" + }, + "examples": { + "records": { + "$ref": "#/components/examples/records-v2.1" + }, + "group_by_country": { + "$ref": "#/components/examples/group_by_country-v2.1" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/exports": { + "get": { + "summary": "List export formats", + "operationId": "listDatasetExportFormats", + "tags": [ + "Dataset" + ], + "description": "List available export formats", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + } + ], + "responses": { + "200": { + "description": "A list of available export formats", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + } + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/exports/{format}": { + "get": { + "summary": "Export a dataset", + "operationId": "exportRecords", + "tags": [ + "Dataset" + ], + "description": "Export a dataset in the desired format.\n**Note:** The `group_by` parameter is only available on exports starting with the v2.1", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "$ref": "#/components/parameters/format-datasets-v2.1" + }, + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/order_by" + }, + { + "$ref": "#/components/parameters/group_by" + }, + { + "$ref": "#/components/parameters/limit_export" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + }, + { + "$ref": "#/components/parameters/use_labels" + }, + { + "$ref": "#/components/parameters/epsg" + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/exports/csv": { + "get": { + "summary": "Export a dataset in CSV", + "operationId": "exportRecordsCSV", + "tags": [ + "Dataset" + ], + "description": "Export a dataset in CSV (Comma Separated Values). Specific parameters are described here", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "name": "delimiter", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + ";", + ",", + "\t", + "|" + ], + "default": ";" + }, + "description": "Sets the field delimiter of the CSV export" + }, + { + "name": "list_separator", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "," + }, + "description": "Sets the separator character used for multivalued strings" + }, + { + "name": "quote_all", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false + }, + "description": "Set it to true to force quoting all strings, i.e. surrounding all strings with quote characters" + }, + { + "name": "with_bom", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true + }, + "description": "Set it to true to force the first characters of the CSV file to be a Unicode Byte Order Mask (0xFEFF). It usually makes Excel correctly open the output CSV file without warning.\n**Warning:** the default value of this parameter is `false` in v2.0 and `true` starting with v2.1" + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/exports/gpx": { + "get": { + "summary": "Export a dataset in GPX", + "operationId": "exportRecordsGPX", + "tags": [ + "Dataset" + ], + "description": "Export a dataset in GPX. Specific parameters are described here", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "name": "name_field", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Sets the field that is used as the 'name' attribute in the GPX output" + }, + { + "name": "description_field_list", + "in": "query", + "required": false, + "schema": { + "type": "string" + }, + "description": "Sets the fields to use in the 'description' attribute of the GPX output" + }, + { + "name": "use_extension", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true + }, + "description": "Set it to true to use the `` tag for attributes (as GDAL does). Set it to false to use the `` tag for attributes.\n**Warning:** the default value of this parameter is `false` in v2.0 and `true` starting with v2.1" + } + ], + "responses": { + "200": { + "description": "Return a file" + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}": { + "get": { + "summary": "Show dataset information", + "operationId": "getDataset", + "tags": [ + "Catalog" + ], + "description": "Returns a list of available endpoints for the specified dataset, with metadata and endpoints.\n\nThe response includes the following links:\n* the attachments endpoint\n* the files endpoint\n* the records endpoint\n* the catalog endpoint.", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + }, + { + "$ref": "#/components/parameters/include_links" + }, + { + "$ref": "#/components/parameters/include_app_metas" + } + ], + "responses": { + "200": { + "description": "The dataset", + "content": { + "application/json; charset=utf-8json": { + "schema": { + "$ref": "#/components/schemas/dataset-v2.1" + }, + "examples": { + "dataset": { + "$ref": "#/components/examples/dataset-v2.1" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/facets": { + "get": { + "summary": "List dataset facets", + "operationId": "getRecordsFacets", + "tags": [ + "Dataset" + ], + "description": "Enumerates facet values for records and returns a list of values for each facet.\nCan be used to implement guided navigation in large result sets.\n", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "$ref": "#/components/parameters/where" + }, + { + "$ref": "#/components/parameters/refine" + }, + { + "$ref": "#/components/parameters/exclude" + }, + { + "$ref": "#/components/parameters/facet" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + } + ], + "responses": { + "200": { + "description": "Facets enumeration", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "facets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/facet_enumeration" + } + } + } + }, + "examples": { + "facets": { + "$ref": "#/components/examples/facets" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/attachments": { + "get": { + "summary": "List dataset attachments", + "operationId": "getDatasetAttachments", + "tags": [ + "Dataset" + ], + "description": "Returns a list of all available attachments for a dataset.\n", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + } + ], + "responses": { + "200": { + "description": "List of all available attachments", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/attachment" + } + } + } + }, + "examples": { + "attachments": { + "$ref": "#/components/examples/attachments" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, + "/catalog/datasets/{dataset_id}/records/{record_id}": { + "get": { + "summary": "Read a dataset record", + "operationId": "getRecord", + "tags": [ + "Dataset" + ], + "description": "Reads a single dataset record based on its identifier.\n", + "parameters": [ + { + "$ref": "#/components/parameters/dataset_id" + }, + { + "$ref": "#/components/parameters/record_id" + }, + { + "$ref": "#/components/parameters/select" + }, + { + "$ref": "#/components/parameters/lang" + }, + { + "$ref": "#/components/parameters/timezone" + } + ], + "responses": { + "200": { + "description": "A single record", + "content": { + "application/json; charset=utf-8": { + "schema": { + "$ref": "#/components/schemas/record-v2.1" + }, + "examples": { + "record": { + "$ref": "#/components/examples/record-v2.1" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/bad_request" + }, + "401": { + "description": "Unauthorized" + }, + "429": { + "$ref": "#/components/responses/quota" + }, + "500": { + "description": "Internal Server Error" + } + } + } + } + }, + "components": { + "securitySchemes": { + "apikey": { + "type": "apiKey", + "description": "API key to make authenticated requests.", + "name": "apikey", + "in": "query" + } + }, + "parameters": { + "select": { + "name": "select", + "in": "query", + "description": "Examples:\n- `select=size` - Example of select, which only return the \"size\" field.\n- `select=size * 2 as bigger_size` - Example of a complex expression with a label, which returns a new field named \"bigger_size\" and containing the double of size field value.\n- `select=dataset_id, fields` - Example of a select in catalog ODSQL query to only retrieve dataset_id and schema of datasets.\n\nA select expression can be used to add, remove or change the fields to return.\nAn expression can be:\n - a wildcard ('*'): all fields are returned.\n - A field name: only the specified field is returned.\n - An include/exclude function: All fields matching the include or exclude expression are included or excluded. This expression can contain wildcard.\n - A complex expression. The result of the expression is returned. A label can be set for this expression, and in that case, the field will be named after this label.", + "schema": { + "type": "string" + } + }, + "where": { + "name": "where", + "in": "query", + "description": "A `where` filter is a text expression performing a simple full-text search that can also include logical operations\n(NOT, AND, OR...) and lots of other functions to perform complex and precise search operations.\n\nFor more information, see [Opendatasoft Query Language (ODSQL)]() reference documentation.", + "schema": { + "type": "string" + } + }, + "order_by": { + "name": "order_by", + "in": "query", + "description": "Example: `order_by=sum(age) desc, name asc`\n\nA comma-separated list of field names or aggregations to sort on, followed by an order (`asc` or `desc`).\n\nResults are sorted in ascending order by default. To sort results in descending order, use the `desc` keyword.", + "style": "form", + "explode": false, + "schema": { + "type": "string" + } + }, + "limit": { + "name": "limit", + "in": "query", + "description": "Number of items to return.\n\nTo use with the `offset` parameter to implement pagination.\n\nThe maximum possible value depends on whether the query contains a `group_by` clause or not.\n\nFor a query **without** a `group_by`:\n - the maximum value for `limit` is 100,\n - `offset+limit` should be less than 10000\n\nFor a query **with** a `group_by`:\n - the maximum value for `limit` is 20000,\n - `offset+limit` should be less than 20000\n\n**Note:** If you need more results, please use the /exports endpoint.\n", + "schema": { + "maximum": 100, + "minimum": -1, + "type": "integer", + "default": 10 + } + }, + "offset": { + "name": "offset", + "in": "query", + "description": "Index of the first item to return (starting at 0).\n\nTo use with the `limit` parameter to implement pagination.\n\n**Note:** the maximum value depends on the type of query, see the note on `limit` for the details\n", + "schema": { + "minimum": 0, + "type": "integer", + "default": 0 + } + }, + "refine": { + "name": "refine", + "in": "query", + "description": "Example: `refine=modified:2020` - Return only the value `2020` from the `modified` facet.\n\nA facet filter used to limit the result set.\nUsing this parameter, you can refine your query to display only the selected facet value in the response.\n\nRefinement uses the following syntax: `refine=:`\n\nFor date, and other hierarchical facets, when refining on one value, all second-level values related to that entry will appear in facets enumeration. For example, after refining on the year 2019, the related second-level month will appear. And when refining on August 2019, the third-level day will appear.\n\n**`refine` must not be confused with a `where` filter. Refining with a facet is equivalent to selecting an entry in the left navigation panel.**", + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "exclude": { + "name": "exclude", + "in": "query", + "description": "Examples:\n- `exclude=city:Paris` - Exclude the value `Paris` from the `city` facet. Facets enumeration will display `Paris` as `excluded` without any count information.\n- `exclude=modified:2019/12` - Exclude the value `2019/12` from the `modified` facet. Facets enumeration will display `2020` as `excluded` without any count information.\n\nA facet filter used to exclude a facet value from the result set.\nUsing this parameter, you can filter your query to exclude the selected facet value in the response.\n\n`exclude` uses the following syntax: `exclude=:`\n\n**`exclude` must not be confused with a `where` filter. Excluding a facet value is equivalent to removing an entry in the left navigation panel.**", + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "lang": { + "name": "lang", + "in": "query", + "description": "A language value.\n\nIf specified, the `lang` value override the default language, which is \"fr\".\nThe language is used to format string, for example in the `date_format` function.", + "schema": { + "type": "string", + "enum": [ + "en", + "fr", + "nl", + "pt", + "it", + "ar", + "de", + "es", + "ca", + "eu", + "sv" + ] + }, + "style": "form" + }, + "timezone": { + "name": "timezone", + "in": "query", + "description": "Set the timezone for datetime fields.\n\nTimezone IDs are defined by the [Unicode CLDR project](https://github.com/unicode-org/cldr). The list of timezone IDs is available in [timezone.xml](https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml).", + "schema": { + "type": "string", + "default": "UTC" + }, + "examples": { + "UTC": { + "summary": "UTC timezone", + "value": "UTC" + }, + "Europe/Paris": { + "summary": "Paris timezone", + "value": "Europe/Paris" + }, + "US/Eastern": { + "summary": "Eastern timezone", + "value": "US/Eastern" + }, + "Europe/London": { + "summary": "London timezone", + "value": "Europe/London" + }, + "Europe/Berlin": { + "summary": "Berlin timezone", + "value": "Europe/Berlin" + } + } + }, + "group_by": { + "name": "group_by", + "in": "query", + "description": "Example: `group_by=city_field as city`\n\nA group by expression defines a grouping function for an aggregation.\nIt can be:\n - a field name: group result by each value of this field\n - a range function: group result by range\n - a date function: group result by date\n\nIt is possible to specify a custom name with the 'as name' notation.", + "style": "form", + "explode": false, + "schema": { + "type": "string" + } + }, + "include_links": { + "name": "include_links", + "in": "query", + "description": "If set to `true`, this parameter will add HATEOAS links in the response.\n", + "schema": { + "type": "boolean", + "default": false + } + }, + "include_app_metas": { + "name": "include_app_metas", + "in": "query", + "description": "If set to `true`, this parameter will add application metadata to the response.\n", + "schema": { + "type": "boolean", + "default": false + } + }, + "format-catalog-v2.1": { + "name": "format", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "csv", + "data.json", + "dcat", + "dcat_ap_ch", + "dcat_ap_de", + "dcat_ap_se", + "dcat_ap_sp", + "dcat_ap_it", + "dcat_ap_vl", + "dcat_ap_benap", + "json", + "rdf", + "rss", + "ttl", + "xlsx" + ], + "description": "Format specifier for the catalog export.\n`dcat_ap_*` formats are only available upon activation.\nSee [here](#tag/Catalog/operation/listExportFormats) to get the list of available export formats" + }, + "style": "simple" + }, + "limit_export": { + "name": "limit", + "in": "query", + "description": "Number of items to return in export.\n\nUse -1 (default) to retrieve all records\n", + "schema": { + "minimum": -1, + "type": "integer", + "default": -1 + } + }, + "dcat_format": { + "name": "dcat_ap_format", + "in": "path", + "required": true, + "schema": { + "type": "string", + "enum": [ + "_ap_ch", + "_ap_de", + "_ap_se", + "_ap_sp", + "_ap_it", + "_ap_vl", + "_ap_benap" + ], + "description": "DCAT format specifier for the catalog export.\n`dcat_ap_*` formats are only available upon activation." + }, + "style": "simple" + }, + "facet": { + "name": "facet", + "in": "query", + "description": "A facet is a field used for simple filtering (through the `refine` and `exclude` parameters) or exploration (with the `/facets` endpoint).\n\nFacets can be configured in the back-office or with this parameter.\n", + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "dataset_id": { + "name": "dataset_id", + "in": "path", + "description": "The identifier of the dataset to be queried.\n\nYou can find it in the \"Information\" tab of the dataset page or in the dataset URL, right after `/datasets/`.", + "required": true, + "schema": { + "type": "string" + } + }, + "format-datasets-v2.1": { + "name": "format", + "in": "path", + "required": true, + "schema": { + "$ref": "#/components/schemas/enum-format-datasets-v2.1" + }, + "style": "simple" + }, + "use_labels": { + "name": "use_labels", + "in": "query", + "description": "If set to `true`, this parameter will make exports output the label of each field rather than its name.\n\nThis parameter only makes sense for formats that contain a list of the fields in their output.\n", + "schema": { + "type": "boolean", + "default": false + } + }, + "epsg": { + "name": "epsg", + "in": "query", + "description": "This parameter sets the EPSG code to project shapes into for formats that support geometric features.\n", + "schema": { + "type": "integer", + "default": 4326 + } + }, + "record_id": { + "name": "record_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Record identifier" + } + }, + "schemas": { + "links": { + "type": "object", + "properties": { + "href": { + "type": "string", + "format": "uri" + }, + "rel": { + "type": "string", + "enum": [ + "self", + "first", + "last", + "next", + "dataset", + "catalog" + ] + } + } + }, + "dataset-v2.1": { + "type": "object", + "properties": { + "_links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "dataset_id": { + "type": "string" + }, + "dataset_uid": { + "type": "string", + "readOnly": true + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "mimetype": { + "type": "string" + }, + "url": { + "type": "string" + }, + "id": { + "type": "string" + }, + "title": { + "type": "string" + } + } + } + }, + "has_records": { + "type": "boolean" + }, + "data_visible": { + "type": "boolean" + }, + "features": { + "type": "array", + "description": "A map of available features for a dataset, with the fields they apply to.\n", + "items": { + "type": "string" + } + }, + "metas": { + "type": "object" + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "label": { + "type": "string" + }, + "type": { + "type": "string" + }, + "annotations": { + "type": "object" + }, + "description": { + "type": "string", + "nullable": true + } + } + } + }, + "additionalProperties": {} + } + }, + "results_dataset-v2.1": { + "type": "object", + "properties": { + "total_count": { + "type": "integer" + }, + "_links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/dataset-v2.1" + } + } + } + }, + "enum-format-datasets-v2.1": { + "type": "string", + "enum": [ + "csv", + "fgb", + "geojson", + "gpx", + "json", + "jsonl", + "jsonld", + "kml", + "n3", + "ov2", + "parquet", + "rdfxml", + "shp", + "turtle", + "xlsx" + ] + }, + "facet_value_enumeration": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "count": { + "type": "integer" + }, + "value": { + "type": "string" + }, + "state": { + "type": "string" + } + } + }, + "facet_enumeration": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "facets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/facet_value_enumeration" + } + } + } + }, + "record-v2.1": { + "type": "object", + "properties": { + "_id": { + "type": "string" + }, + "_timestamp": { + "type": "string", + "format": "dateTime" + }, + "_size": { + "type": "integer", + "format": "int64" + }, + "_links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "field1": { + "type": "string" + }, + "field2": { + "type": "integer" + } + } + }, + "results-v2.1": { + "type": "object", + "properties": { + "total_count": { + "type": "integer" + }, + "_links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/links" + } + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/record-v2.1" + } + } + } + }, + "attachment": { + "type": "object", + "properties": { + "href": { + "type": "string" + }, + "metas": { + "type": "object", + "properties": { + "mime-type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "id": { + "type": "string" + } + } + } + } + } + }, + "examples": { + "datasets-v2.1": { + "value": { + "total_count": 19, + "results": [ + { + "dataset_id": "world-administrative-boundaries-countries-and-territories", + "dataset_uid": "da_6kvv9v", + "attachments": [], + "has_records": true, + "data_visible": true, + "fields": [ + { + "annotations": {}, + "description": null, + "type": "geo_point_2d", + "name": "geo_point_2d", + "label": "Geo Point" + }, + { + "annotations": {}, + "description": null, + "type": "geo_shape", + "name": "geo_shape", + "label": "Geo Shape" + }, + { + "description": null, + "label": "Status", + "type": "text", + "name": "status", + "annotations": { + "facet": [] + } + }, + { + "description": "ISO 3 code of the country to which the territory belongs", + "label": "ISO 3 country code", + "type": "text", + "name": "color_code", + "annotations": { + "facet": [] + } + }, + { + "description": null, + "label": "Region of the territory", + "type": "text", + "name": "region", + "annotations": { + "facet": [] + } + }, + { + "description": null, + "label": "ISO 3 territory code", + "type": "text", + "name": "iso3", + "annotations": { + "sortable": [] + } + }, + { + "description": null, + "label": "Continent of the territory", + "type": "text", + "name": "continent", + "annotations": { + "facet": [] + } + }, + { + "description": "Name of the territory", + "label": "English Name", + "type": "text", + "name": "name", + "annotations": { + "sortable": [] + } + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "iso_3166_1_alpha_2_codes", + "label": "ISO 3166-1 Alpha 2-Codes" + }, + { + "annotations": {}, + "label": "French Name", + "type": "text", + "name": "french_short", + "description": "French term, when it is available in https://data.opendatasoft.com/explore/dataset/countries-territories-taxonomy-mvp-ct-taxonomy-with-hxl-tags1@public/table/, English name otherwise" + } + ], + "metas": { + "default": { + "records_count": 256, + "modified": "2021-06-23T14:59:57+00:00", + "source_domain_address": null, + "references": "https://geonode.wfp.org/layers/geonode:wld_bnd_adm0_wfp", + "keyword": [ + "United Nation", + "ISO-3 code", + "Countries", + "Territories", + "Shape", + "Boundaries" + ], + "source_domain_title": null, + "geographic_reference": [ + "world" + ], + "timezone": null, + "title": "World Administrative Boundaries - Countries and Territories", + "parent_domain": null, + "theme": [ + "Administration, Government, Public finances, Citizenship" + ], + "modified_updates_on_data_change": false, + "metadata_processed": "2021-06-23T15:00:02.656000+00:00", + "data_processed": "2019-05-15T07:49:01+00:00", + "territory": [ + "World" + ], + "description": "

This dataset displays level 0 world administrative boundaries. It contains countries as well as non-sovereign territories (like, for instance, French overseas). 

", + "modified_updates_on_metadata_change": false, + "shared_catalog": null, + "source_domain": null, + "attributions": null, + "geographic_area_mode": null, + "geographic_reference_auto": true, + "geographic_area": null, + "publisher": "World Food Programme (UN agency)", + "language": "en", + "license": "Open Government Licence v3.0", + "source_dataset": null, + "metadata_languages": [ + "en" + ], + "oauth_scope": null, + "federated": true, + "license_url": "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" + } + }, + "features": [ + "analyze", + "geo" + ] + }, + { + "dataset_id": "geonames-all-cities-with-a-population-1000", + "dataset_uid": "da_5m8ykr", + "attachments": [ + { + "mimetype": "application/zip", + "url": "odsfile://cities1000.zip", + "id": "cities1000_zip", + "title": "cities1000.zip" + } + ], + "has_records": true, + "data_visible": true, + "fields": [ + { + "description": null, + "label": "Geoname ID", + "type": "text", + "name": "geoname_id", + "annotations": { + "facetsort": [ + "-count" + ], + "id": [] + } + }, + { + "description": null, + "label": "Name", + "type": "text", + "name": "name", + "annotations": { + "sortable": [] + } + }, + { + "description": null, + "label": "ASCII Name", + "type": "text", + "name": "ascii_name", + "annotations": {} + }, + { + "description": null, + "label": "Alternate Names", + "type": "text", + "name": "alternate_names", + "annotations": { + "multivalued": [ + "," + ] + } + }, + { + "description": "see http://www.geonames.org/export/codes.html", + "label": "Feature Class", + "type": "text", + "name": "feature_class", + "annotations": {} + }, + { + "description": "see http://www.geonames.org/export/codes.html", + "label": "Feature Code", + "type": "text", + "name": "feature_code", + "annotations": {} + }, + { + "description": null, + "label": "Country Code", + "type": "text", + "name": "country_code", + "annotations": {} + }, + { + "description": null, + "label": "Country name EN", + "type": "text", + "name": "cou_name_en", + "annotations": { + "facet": [], + "facetsort": [ + "alphanum" + ], + "disjunctive": [] + } + }, + { + "description": null, + "label": "Country Code 2", + "type": "text", + "name": "country_code_2", + "annotations": {} + }, + { + "description": null, + "label": "Admin1 Code", + "type": "text", + "name": "admin1_code", + "annotations": {} + }, + { + "description": null, + "label": "Admin2 Code", + "type": "text", + "name": "admin2_code", + "annotations": { + "facetsort": [ + "-count" + ] + } + }, + { + "description": null, + "label": "Admin3 Code", + "type": "text", + "name": "admin3_code", + "annotations": {} + }, + { + "description": null, + "label": "Admin4 Code", + "type": "text", + "name": "admin4_code", + "annotations": {} + }, + { + "description": null, + "label": "Population", + "type": "int", + "name": "population", + "annotations": {} + }, + { + "description": null, + "label": "Elevation", + "type": "text", + "name": "elevation", + "annotations": {} + }, + { + "description": null, + "label": "DIgital Elevation Model", + "type": "int", + "name": "dem", + "annotations": {} + }, + { + "description": null, + "label": "Timezone", + "type": "text", + "name": "timezone", + "annotations": { + "facet": [], + "hierarchical": [ + "/" + ] + } + }, + { + "description": null, + "label": "Modification date", + "type": "date", + "name": "modification_date", + "annotations": {} + }, + { + "description": null, + "label": "LABEL EN", + "type": "text", + "name": "label_en", + "annotations": {} + }, + { + "description": null, + "label": "Coordinates", + "type": "geo_point_2d", + "name": "coordinates", + "annotations": { + "facetsort": [ + "-count" + ] + } + } + ], + "metas": { + "default": { + "records_count": 137609, + "modified": "2021-06-23T14:37:45+00:00", + "source_domain_address": null, + "references": null, + "keyword": null, + "source_domain_title": null, + "geographic_reference": [ + "world" + ], + "timezone": null, + "title": "Geonames - All Cities with a population > 1000", + "parent_domain": null, + "theme": null, + "modified_updates_on_data_change": false, + "metadata_processed": "2021-06-23T14:49:23.198000+00:00", + "data_processed": "2021-06-23T14:49:23+00:00", + "territory": [ + "World" + ], + "description": null, + "modified_updates_on_metadata_change": false, + "shared_catalog": null, + "source_domain": null, + "attributions": null, + "geographic_area_mode": null, + "geographic_reference_auto": true, + "geographic_area": null, + "publisher": null, + "language": "en", + "license": null, + "source_dataset": null, + "metadata_languages": [ + "en" + ], + "oauth_scope": null, + "federated": false, + "license_url": null + } + }, + "features": [ + "geo", + "analyze", + "timeserie" + ] + } + ] + } + }, + "catalog_facets": { + "value": { + "links": [], + "facets": [ + { + "name": "publisher", + "facets": [ + { + "count": 2, + "state": "displayed", + "name": "Opendatasoft", + "value": "Opendatasoft" + }, + { + "count": 2, + "state": "displayed", + "name": "Opendatasoft - Data Team", + "value": "Opendatasoft - Data Team" + } + ] + }, + { + "name": "features", + "facets": [ + { + "count": 19, + "state": "displayed", + "name": "analyze", + "value": "analyze" + }, + { + "count": 13, + "state": "displayed", + "name": "timeserie", + "value": "timeserie" + } + ] + }, + { + "name": "language", + "facets": [ + { + "count": 17, + "state": "displayed", + "name": "en", + "value": "en" + }, + { + "count": 4, + "state": "displayed", + "name": "fr", + "value": "fr" + } + ] + } + ] + } + }, + "records-v2.1": { + "value": { + "total_count": 137611, + "results": [ + { + "admin1_code": "27", + "elevation": null, + "name": "Saint-Leu", + "modification_date": "2019-03-26", + "alternate_names": [ + "Saint-Leu" + ], + "feature_class": "P", + "admin3_code": "711", + "cou_name_en": "France", + "coordinates": { + "lat": 46.7306, + "lon": 4.50083 + }, + "country_code_2": null, + "geoname_id": "2978771", + "feature_code": "PPL", + "label_en": "France", + "dem": 366, + "country_code": "FR", + "ascii_name": "Saint-Leu", + "timezone": "Europe/Paris", + "admin2_code": "71", + "admin4_code": "71436", + "population": 29278 + }, + { + "admin1_code": "32", + "elevation": null, + "name": "Saint-Léger-lès-Domart", + "modification_date": "2016-02-18", + "alternate_names": [ + "Saint-Leger", + "Saint-Leger-les-Domart", + "Saint-Léger", + "Saint-Léger-lès-Domart" + ], + "feature_class": "P", + "admin3_code": "802", + "cou_name_en": "France", + "coordinates": { + "lat": 50.05208, + "lon": 2.14067 + }, + "country_code_2": null, + "geoname_id": "2978817", + "feature_code": "PPL", + "label_en": "France", + "dem": 31, + "country_code": "FR", + "ascii_name": "Saint-Leger-les-Domart", + "timezone": "Europe/Paris", + "admin2_code": "80", + "admin4_code": "80706", + "population": 1781 + } + ] + } + }, + "group_by_country-v2.1": { + "value": { + "results": [ + { + "count": 16729, + "cou_name_en": "United States" + }, + { + "count": 9945, + "cou_name_en": "Italy" + }, + { + "count": 8981, + "cou_name_en": "Mexico" + } + ] + } + }, + "dataset-v2.1": { + "value": { + "dataset_id": "geonames-all-cities-with-a-population-1000", + "dataset_uid": "da_s2n5ed", + "attachments": [], + "has_records": true, + "data_visible": true, + "fields": [ + { + "description": null, + "label": "Geoname ID", + "type": "text", + "name": "geoname_id", + "annotations": { + "facetsort": [ + "-count" + ], + "id": [] + } + }, + { + "description": null, + "label": "Name", + "type": "text", + "name": "name", + "annotations": { + "sortable": [] + } + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "ascii_name", + "label": "ASCII Name" + }, + { + "description": null, + "label": "Alternate Names", + "type": "text", + "name": "alternate_names", + "annotations": { + "multivalued": [ + "," + ] + } + }, + { + "annotations": {}, + "label": "Feature Class", + "type": "text", + "name": "feature_class", + "description": "see http://www.geonames.org/export/codes.html" + }, + { + "annotations": {}, + "label": "Feature Code", + "type": "text", + "name": "feature_code", + "description": "see http://www.geonames.org/export/codes.html" + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "country_code", + "label": "Country Code" + }, + { + "description": null, + "label": "Country name EN", + "type": "text", + "name": "cou_name_en", + "annotations": { + "facet": [], + "facetsort": [ + "alphanum" + ], + "disjunctive": [] + } + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "country_code_2", + "label": "Country Code 2" + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "admin1_code", + "label": "Admin1 Code" + }, + { + "description": null, + "label": "Admin2 Code", + "type": "text", + "name": "admin2_code", + "annotations": { + "facetsort": [ + "-count" + ] + } + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "admin3_code", + "label": "Admin3 Code" + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "admin4_code", + "label": "Admin4 Code" + }, + { + "annotations": {}, + "description": null, + "type": "int", + "name": "population", + "label": "Population" + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "elevation", + "label": "Elevation" + }, + { + "annotations": {}, + "description": null, + "type": "int", + "name": "dem", + "label": "DIgital Elevation Model" + }, + { + "description": null, + "label": "Timezone", + "type": "text", + "name": "timezone", + "annotations": { + "facet": [], + "hierarchical": [ + "/" + ] + } + }, + { + "annotations": {}, + "description": null, + "type": "date", + "name": "modification_date", + "label": "Modification date" + }, + { + "annotations": {}, + "description": null, + "type": "text", + "name": "label_en", + "label": "LABEL EN" + }, + { + "description": null, + "label": "Coordinates", + "type": "geo_point_2d", + "name": "coordinates", + "annotations": { + "facetsort": [ + "-count" + ] + } + } + ], + "metas": { + "default": { + "records_count": 137611, + "modified": "2021-06-23T07:50:20+00:00", + "source_domain_address": null, + "references": "https://download.geonames.org/export/dump/", + "keyword": [ + "Geonames", + "city", + "world" + ], + "source_domain_title": null, + "geographic_reference": [ + "world" + ], + "timezone": null, + "title": "Geonames - All Cities with a population > 1000", + "parent_domain": null, + "theme": [ + "Administration, Government, Public finances, Citizenship" + ], + "modified_updates_on_data_change": true, + "metadata_processed": "2021-06-23T07:50:26.162000+00:00", + "data_processed": "2021-06-22T08:47:08+00:00", + "territory": [ + "World" + ], + "description": "

All cities with a population > 1000 or seats of adm div (ca 80.000)

Sources and Contributions

  • Sources : GeoNames is aggregating over hundred different data sources. \t
  • Ambassadors : GeoNames Ambassadors help in many countries. \t
  • Wiki : A wiki allows to view the data and quickly fix error and add missing places. \t
  • Donations and Sponsoring : Costs for running GeoNames are covered by donations and sponsoring.

Enrichment:

  • add country name
", + "modified_updates_on_metadata_change": false, + "shared_catalog": null, + "source_domain": null, + "attributions": [ + "https://www.geonames.org/about.html" + ], + "geographic_area_mode": null, + "geographic_reference_auto": true, + "geographic_area": null, + "publisher": "GeoNames", + "language": "en", + "license": "CC BY 4.0", + "source_dataset": null, + "metadata_languages": [ + "en" + ], + "oauth_scope": null, + "federated": true, + "license_url": "https://creativecommons.org/licenses/by/4.0/" + } + }, + "features": [ + "geo", + "analyze", + "timeserie" + ] + } + }, + "facets": { + "value": { + "links": [], + "facets": [ + { + "facets": [ + { + "count": 68888, + "state": "displayed", + "name": "Europe", + "value": "Europe" + }, + { + "count": 36276, + "state": "displayed", + "name": "America", + "value": "America" + } + ], + "name": "timezone" + }, + { + "facets": [ + { + "count": 313, + "state": "displayed", + "name": "Afghanistan", + "value": "Afghanistan" + }, + { + "count": 356, + "state": "displayed", + "name": "Albania", + "value": "Albania" + } + ], + "name": "cou_name_en" + } + ] + } + }, + "attachments": { + "value": { + "links": [], + "attachments": [ + { + "href": "https://documentation-resources.opendatasoft.com/api/v2/catalog/datasets/geonames-all-cities-with-a-population-1000/attachments/cities1000_zip", + "metas": { + "mime-type": "application/zip", + "title": "cities1000.zip" + } + } + ] + } + }, + "record-v2.1": { + "value": { + "timezone": "Europe/Brussels", + "elevation": null, + "name": "Fraire", + "modification_date": "2020-04-05", + "dem": 238, + "cou_name_en": "Belgium", + "feature_class": "P", + "admin3_code": "93", + "alternate_names": [ + "Fraire" + ], + "coordinates": { + "lat": 50.26127, + "lon": 4.5076 + }, + "country_code_2": null, + "geoname_id": "2798031", + "feature_code": "PPL", + "label_en": "Belgium", + "admin4_code": "93088", + "country_code": "BE", + "ascii_name": "Fraire", + "admin1_code": "WAL", + "admin2_code": "WNA", + "population": 1492 + } + } + }, + "responses": { + "bad_request": { + "description": "Bad Request", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "minLength": 1 + }, + "error_code": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "message", + "error_code" + ] + }, + "examples": { + "invalid_odsql": { + "value": { + "message": "ODSQL query is malformed: invalid_function() Clause(s) containing the error(s): select.", + "error_code": "ODSQLError" + } + } + } + } + } + }, + "quota": { + "description": "Too many requests", + "content": { + "application/json; charset=utf-8": { + "schema": { + "type": "object", + "properties": { + "errorcode": { + "type": "number" + }, + "reset_time": { + "type": "string", + "minLength": 1 + }, + "limit_time_unit": { + "type": "string", + "minLength": 1 + }, + "call_limit": { + "type": "number" + }, + "error": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "errorcode", + "reset_time", + "limit_time_unit", + "call_limit", + "error" + ] + }, + "examples": { + "quota_exceeded": { + "value": { + "errorcode": 10002, + "reset_time": "2021-01-26T00:00:00Z", + "limit_time_unit": "day", + "call_limit": 10000, + "error": "Too many requests on the domain. Please contact the domain administrator." + } + } + } + } + } + } + } + } +} diff --git a/openapi-cli/src/test/resources/generators/client/mock/reference_example.bal b/openapi-cli/src/test/resources/generators/client/mock/reference_example.bal new file mode 100644 index 000000000..2ef4fd7d0 --- /dev/null +++ b/openapi-cli/src/test/resources/generators/client/mock/reference_example.bal @@ -0,0 +1,85 @@ +public isolated client class Client { + final readonly & ApiKeysConfig apiKeyConfig; + # Gets invoked to initialize the `connector`. + # + # + apiKeyConfig - API keys for authorization + # + config - The configurations to be used when initializing the `connector` + # + serviceUrl - URL of the target service + # + return - An error if connector initialization failed + public isolated function init(ApiKeysConfig apiKeyConfig, ConnectionConfig config = {}, string serviceUrl = "https://documentation-resources.opendatasoft.com/api/explore/v2.1") returns error? { + return; + } + + # Query catalog datasets + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - A list of available datasets + resource isolated function get catalog/datasets(map headers = {}, *GetDatasetsQueries queries) returns json|error { + return {"total_count": 19, "results": [{"dataset_id": "world-administrative-boundaries-countries-and-territories", "dataset_uid": "da_6kvv9v", "attachments": [], "has_records": true, "data_visible": true, "fields": [{"annotations": {}, "description": null, "type": "geo_point_2d", "name": "geo_point_2d", "label": "Geo Point"}, {"annotations": {}, "description": null, "type": "geo_shape", "name": "geo_shape", "label": "Geo Shape"}, {"description": null, "label": "Status", "type": "text", "name": "status", "annotations": {"facet": []}}, {"description": "ISO 3 code of the country to which the territory belongs", "label": "ISO 3 country code", "type": "text", "name": "color_code", "annotations": {"facet": []}}, {"description": null, "label": "Region of the territory", "type": "text", "name": "region", "annotations": {"facet": []}}, {"description": null, "label": "ISO 3 territory code", "type": "text", "name": "iso3", "annotations": {"sortable": []}}, {"description": null, "label": "Continent of the territory", "type": "text", "name": "continent", "annotations": {"facet": []}}, {"description": "Name of the territory", "label": "English Name", "type": "text", "name": "name", "annotations": {"sortable": []}}, {"annotations": {}, "description": null, "type": "text", "name": "iso_3166_1_alpha_2_codes", "label": "ISO 3166-1 Alpha 2-Codes"}, {"annotations": {}, "label": "French Name", "type": "text", "name": "french_short", "description": "French term, when it is available in https://data.opendatasoft.com/explore/dataset/countries-territories-taxonomy-mvp-ct-taxonomy-with-hxl-tags1@public/table/, English name otherwise"}], "metas": {"default": {"records_count": 256, "modified": "2021-06-23T14:59:57+00:00", "source_domain_address": null, "references": "https://geonode.wfp.org/layers/geonode:wld_bnd_adm0_wfp", "keyword": ["United Nation", "ISO-3 code", "Countries", "Territories", "Shape", "Boundaries"], "source_domain_title": null, "geographic_reference": ["world"], "timezone": null, "title": "World Administrative Boundaries - Countries and Territories", "parent_domain": null, "theme": ["Administration, Government, Public finances, Citizenship"], "modified_updates_on_data_change": false, "metadata_processed": "2021-06-23T15:00:02.656000+00:00", "data_processed": "2019-05-15T07:49:01+00:00", "territory": ["World"], "description": "

This dataset displays level 0 world administrative boundaries. It contains countries as well as non-sovereign territories (like, for instance, French overseas). 

", "modified_updates_on_metadata_change": false, "shared_catalog": null, "source_domain": null, "attributions": null, "geographic_area_mode": null, "geographic_reference_auto": true, "geographic_area": null, "publisher": "World Food Programme (UN agency)", "language": "en", "license": "Open Government Licence v3.0", "source_dataset": null, "metadata_languages": ["en"], "oauth_scope": null, "federated": true, "license_url": "http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/"}}, "features": ["analyze", "geo"]}, {"dataset_id": "geonames-all-cities-with-a-population-1000", "dataset_uid": "da_5m8ykr", "attachments": [{"mimetype": "application/zip", "url": "odsfile://cities1000.zip", "id": "cities1000_zip", "title": "cities1000.zip"}], "has_records": true, "data_visible": true, "fields": [{"description": null, "label": "Geoname ID", "type": "text", "name": "geoname_id", "annotations": {"facetsort": ["-count"], "id": []}}, {"description": null, "label": "Name", "type": "text", "name": "name", "annotations": {"sortable": []}}, {"description": null, "label": "ASCII Name", "type": "text", "name": "ascii_name", "annotations": {}}, {"description": null, "label": "Alternate Names", "type": "text", "name": "alternate_names", "annotations": {"multivalued": [","]}}, {"description": "see http://www.geonames.org/export/codes.html", "label": "Feature Class", "type": "text", "name": "feature_class", "annotations": {}}, {"description": "see http://www.geonames.org/export/codes.html", "label": "Feature Code", "type": "text", "name": "feature_code", "annotations": {}}, {"description": null, "label": "Country Code", "type": "text", "name": "country_code", "annotations": {}}, {"description": null, "label": "Country name EN", "type": "text", "name": "cou_name_en", "annotations": {"facet": [], "facetsort": ["alphanum"], "disjunctive": []}}, {"description": null, "label": "Country Code 2", "type": "text", "name": "country_code_2", "annotations": {}}, {"description": null, "label": "Admin1 Code", "type": "text", "name": "admin1_code", "annotations": {}}, {"description": null, "label": "Admin2 Code", "type": "text", "name": "admin2_code", "annotations": {"facetsort": ["-count"]}}, {"description": null, "label": "Admin3 Code", "type": "text", "name": "admin3_code", "annotations": {}}, {"description": null, "label": "Admin4 Code", "type": "text", "name": "admin4_code", "annotations": {}}, {"description": null, "label": "Population", "type": "int", "name": "population", "annotations": {}}, {"description": null, "label": "Elevation", "type": "text", "name": "elevation", "annotations": {}}, {"description": null, "label": "DIgital Elevation Model", "type": "int", "name": "dem", "annotations": {}}, {"description": null, "label": "Timezone", "type": "text", "name": "timezone", "annotations": {"facet": [], "hierarchical": ["/"]}}, {"description": null, "label": "Modification date", "type": "date", "name": "modification_date", "annotations": {}}, {"description": null, "label": "LABEL EN", "type": "text", "name": "label_en", "annotations": {}}, {"description": null, "label": "Coordinates", "type": "geo_point_2d", "name": "coordinates", "annotations": {"facetsort": ["-count"]}}], "metas": {"default": {"records_count": 137609, "modified": "2021-06-23T14:37:45+00:00", "source_domain_address": null, "references": null, "keyword": null, "source_domain_title": null, "geographic_reference": ["world"], "timezone": null, "title": "Geonames - All Cities with a population > 1000", "parent_domain": null, "theme": null, "modified_updates_on_data_change": false, "metadata_processed": "2021-06-23T14:49:23.198000+00:00", "data_processed": "2021-06-23T14:49:23+00:00", "territory": ["World"], "description": null, "modified_updates_on_metadata_change": false, "shared_catalog": null, "source_domain": null, "attributions": null, "geographic_area_mode": null, "geographic_reference_auto": true, "geographic_area": null, "publisher": null, "language": "en", "license": null, "source_dataset": null, "metadata_languages": ["en"], "oauth_scope": null, "federated": false, "license_url": null}}, "features": ["geo", "analyze", "timeserie"]}]}; + } + + # Show dataset information + # + # + dataset_id - The identifier of the dataset to be queried. + # You can find it in the "Information" tab of the dataset page or in the dataset URL, right after `/datasets/`. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - The dataset + resource isolated function get catalog/datasets/[string dataset_id](map headers = {}, *GetDatasetQueries queries) returns json|error { + return {"dataset_id": "geonames-all-cities-with-a-population-1000", "dataset_uid": "da_s2n5ed", "attachments": [], "has_records": true, "data_visible": true, "fields": [{"description": null, "label": "Geoname ID", "type": "text", "name": "geoname_id", "annotations": {"facetsort": ["-count"], "id": []}}, {"description": null, "label": "Name", "type": "text", "name": "name", "annotations": {"sortable": []}}, {"annotations": {}, "description": null, "type": "text", "name": "ascii_name", "label": "ASCII Name"}, {"description": null, "label": "Alternate Names", "type": "text", "name": "alternate_names", "annotations": {"multivalued": [","]}}, {"annotations": {}, "label": "Feature Class", "type": "text", "name": "feature_class", "description": "see http://www.geonames.org/export/codes.html"}, {"annotations": {}, "label": "Feature Code", "type": "text", "name": "feature_code", "description": "see http://www.geonames.org/export/codes.html"}, {"annotations": {}, "description": null, "type": "text", "name": "country_code", "label": "Country Code"}, {"description": null, "label": "Country name EN", "type": "text", "name": "cou_name_en", "annotations": {"facet": [], "facetsort": ["alphanum"], "disjunctive": []}}, {"annotations": {}, "description": null, "type": "text", "name": "country_code_2", "label": "Country Code 2"}, {"annotations": {}, "description": null, "type": "text", "name": "admin1_code", "label": "Admin1 Code"}, {"description": null, "label": "Admin2 Code", "type": "text", "name": "admin2_code", "annotations": {"facetsort": ["-count"]}}, {"annotations": {}, "description": null, "type": "text", "name": "admin3_code", "label": "Admin3 Code"}, {"annotations": {}, "description": null, "type": "text", "name": "admin4_code", "label": "Admin4 Code"}, {"annotations": {}, "description": null, "type": "int", "name": "population", "label": "Population"}, {"annotations": {}, "description": null, "type": "text", "name": "elevation", "label": "Elevation"}, {"annotations": {}, "description": null, "type": "int", "name": "dem", "label": "DIgital Elevation Model"}, {"description": null, "label": "Timezone", "type": "text", "name": "timezone", "annotations": {"facet": [], "hierarchical": ["/"]}}, {"annotations": {}, "description": null, "type": "date", "name": "modification_date", "label": "Modification date"}, {"annotations": {}, "description": null, "type": "text", "name": "label_en", "label": "LABEL EN"}, {"description": null, "label": "Coordinates", "type": "geo_point_2d", "name": "coordinates", "annotations": {"facetsort": ["-count"]}}], "metas": {"default": {"records_count": 137611, "modified": "2021-06-23T07:50:20+00:00", "source_domain_address": null, "references": "https://download.geonames.org/export/dump/", "keyword": ["Geonames", "city", "world"], "source_domain_title": null, "geographic_reference": ["world"], "timezone": null, "title": "Geonames - All Cities with a population > 1000", "parent_domain": null, "theme": ["Administration, Government, Public finances, Citizenship"], "modified_updates_on_data_change": true, "metadata_processed": "2021-06-23T07:50:26.162000+00:00", "data_processed": "2021-06-22T08:47:08+00:00", "territory": ["World"], "description": "

All cities with a population > 1000 or seats of adm div (ca 80.000)

Sources and Contributions

  • Sources : GeoNames is aggregating over hundred different data sources. \t
  • Ambassadors : GeoNames Ambassadors help in many countries. \t
  • Wiki : A wiki allows to view the data and quickly fix error and add missing places. \t
  • Donations and Sponsoring : Costs for running GeoNames are covered by donations and sponsoring.

Enrichment:

  • add country name
", "modified_updates_on_metadata_change": false, "shared_catalog": null, "source_domain": null, "attributions": ["https://www.geonames.org/about.html"], "geographic_area_mode": null, "geographic_reference_auto": true, "geographic_area": null, "publisher": "GeoNames", "language": "en", "license": "CC BY 4.0", "source_dataset": null, "metadata_languages": ["en"], "oauth_scope": null, "federated": true, "license_url": "https://creativecommons.org/licenses/by/4.0/"}}, "features": ["geo", "analyze", "timeserie"]}; + } + + # List dataset attachments + # + # + dataset_id - The identifier of the dataset to be queried. + # You can find it in the "Information" tab of the dataset page or in the dataset URL, right after `/datasets/`. + # + headers - Headers to be sent with the request + # + return - List of all available attachments + resource isolated function get catalog/datasets/[string dataset_id]/attachments(map headers = {}) returns inline_response_200_2|error { + return {"links": [], "attachments": [{"href": "https://documentation-resources.opendatasoft.com/api/v2/catalog/datasets/geonames-all-cities-with-a-population-1000/attachments/cities1000_zip", "metas": {"mime-type": "application/zip", "title": "cities1000.zip"}}]}; + } + + # List dataset facets + # + # + dataset_id - The identifier of the dataset to be queried. + # You can find it in the "Information" tab of the dataset page or in the dataset URL, right after `/datasets/`. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - Facets enumeration + resource isolated function get catalog/datasets/[string dataset_id]/facets(map headers = {}, *GetRecordsFacetsQueries queries) returns inline_response_200_1|error { + return {"links": [], "facets": [{"facets": [{"count": 68888, "state": "displayed", "name": "Europe", "value": "Europe"}, {"count": 36276, "state": "displayed", "name": "America", "value": "America"}], "name": "timezone"}, {"facets": [{"count": 313, "state": "displayed", "name": "Afghanistan", "value": "Afghanistan"}, {"count": 356, "state": "displayed", "name": "Albania", "value": "Albania"}], "name": "cou_name_en"}]}; + } + + # Query dataset records + # + # + dataset_id - The identifier of the dataset to be queried. + # You can find it in the "Information" tab of the dataset page or in the dataset URL, right after `/datasets/`. + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - Records + resource isolated function get catalog/datasets/[string dataset_id]/records(map headers = {}, *GetRecordsQueries queries) returns results\-v2\.1|error { + return {"total_count": 137611, "results": [{"admin1_code": "27", "elevation": null, "name": "Saint-Leu", "modification_date": "2019-03-26", "alternate_names": ["Saint-Leu"], "feature_class": "P", "admin3_code": "711", "cou_name_en": "France", "coordinates": {"lat": 46.7306, "lon": 4.50083}, "country_code_2": null, "geoname_id": "2978771", "feature_code": "PPL", "label_en": "France", "dem": 366, "country_code": "FR", "ascii_name": "Saint-Leu", "timezone": "Europe/Paris", "admin2_code": "71", "admin4_code": "71436", "population": 29278}, {"admin1_code": "32", "elevation": null, "name": "Saint-Léger-lès-Domart", "modification_date": "2016-02-18", "alternate_names": ["Saint-Leger", "Saint-Leger-les-Domart", "Saint-Léger", "Saint-Léger-lès-Domart"], "feature_class": "P", "admin3_code": "802", "cou_name_en": "France", "coordinates": {"lat": 50.05208, "lon": 2.14067}, "country_code_2": null, "geoname_id": "2978817", "feature_code": "PPL", "label_en": "France", "dem": 31, "country_code": "FR", "ascii_name": "Saint-Leger-les-Domart", "timezone": "Europe/Paris", "admin2_code": "80", "admin4_code": "80706", "population": 1781}]}; + } + + # Read a dataset record + # + # + dataset_id - The identifier of the dataset to be queried. + # You can find it in the "Information" tab of the dataset page or in the dataset URL, right after `/datasets/`. + # + record_id - Record identifier + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - A single record + resource isolated function get catalog/datasets/[string dataset_id]/records/[string record_id](map headers = {}, *GetRecordQueries queries) returns record\-v2\.1|error { + return {"timezone": "Europe/Brussels", "elevation": null, "name": "Fraire", "modification_date": "2020-04-05", "dem": 238, "cou_name_en": "Belgium", "feature_class": "P", "admin3_code": "93", "alternate_names": ["Fraire"], "coordinates": {"lat": 50.26127, "lon": 4.5076}, "country_code_2": null, "geoname_id": "2798031", "feature_code": "PPL", "label_en": "Belgium", "admin4_code": "93088", "country_code": "BE", "ascii_name": "Fraire", "admin1_code": "WAL", "admin2_code": "WNA", "population": 1492}; + } + + # List facet values + # + # + headers - Headers to be sent with the request + # + queries - Queries to be sent with the request + # + return - An enumeration of facets + resource isolated function get catalog/facets(map headers = {}, *GetDatasetsFacetsQueries queries) returns inline_response_200_1|error { + return {"links": [], "facets": [{"name": "publisher", "facets": [{"count": 2, "state": "displayed", "name": "Opendatasoft", "value": "Opendatasoft"}, {"count": 2, "state": "displayed", "name": "Opendatasoft - Data Team", "value": "Opendatasoft - Data Team"}]}, {"name": "features", "facets": [{"count": 19, "state": "displayed", "name": "analyze", "value": "analyze"}, {"count": 13, "state": "displayed", "name": "timeserie", "value": "timeserie"}]}, {"name": "language", "facets": [{"count": 17, "state": "displayed", "name": "en", "value": "en"}, {"count": 4, "state": "displayed", "name": "fr", "value": "fr"}]}]}; + } +} diff --git a/openapi-cli/src/test/resources/testng.xml b/openapi-cli/src/test/resources/testng.xml index a88d59373..5dc0aa029 100644 --- a/openapi-cli/src/test/resources/testng.xml +++ b/openapi-cli/src/test/resources/testng.xml @@ -113,6 +113,7 @@ under the License. + diff --git a/openapi-client-native/ballerina-tests/Dependencies.toml b/openapi-client-native/ballerina-tests/Dependencies.toml index f25a62a50..e9cd5368b 100644 --- a/openapi-client-native/ballerina-tests/Dependencies.toml +++ b/openapi-client-native/ballerina-tests/Dependencies.toml @@ -55,7 +55,7 @@ dependencies = [ [[package]] org = "ballerina" name = "crypto" -version = "2.7.0" +version = "2.7.2" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "time"} @@ -75,7 +75,7 @@ dependencies = [ [[package]] org = "ballerina" name = "http" -version = "2.11.0" +version = "2.11.1" dependencies = [ {org = "ballerina", name = "auth"}, {org = "ballerina", name = "cache"}, diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java index 9436edcac..0e0fd1065 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGenerator.java @@ -124,6 +124,7 @@ */ public class BallerinaClientGenerator { + protected OASClientConfig oasClientConfig; private final Filter filter; protected List imports = new ArrayList<>(); private List apiKeyNameList = new ArrayList<>(); @@ -173,6 +174,7 @@ public BallerinaClientGenerator(OASClientConfig oasClientConfig) { this.remoteFunctionNameList = new ArrayList<>(); this.authConfigGeneratorImp = new AuthConfigGeneratorImp(false, false); this.resourceMode = oasClientConfig.isResourceMode(); + this.oasClientConfig = oasClientConfig; } /** @@ -182,14 +184,13 @@ public BallerinaClientGenerator(OASClientConfig oasClientConfig) { * @throws BallerinaOpenApiException When function fail in process. */ public SyntaxTree generateSyntaxTree() throws BallerinaOpenApiException, ClientException { + generateHttpImport(); + return getSyntaxTree(); + } - // Create `ballerina/http` import declaration node - List importForHttp = getImportDeclarationNodes(); - imports.addAll(importForHttp); - + protected SyntaxTree getSyntaxTree() throws ClientException, BallerinaOpenApiException { // Add authentication related records authConfigGeneratorImp.addAuthRelatedRecords(openAPI); - List nodes = getModuleMemberDeclarationNodes(); NodeList importsList = createNodeList(imports); ModulePartNode modulePartNode = @@ -203,6 +204,12 @@ public SyntaxTree generateSyntaxTree() throws BallerinaOpenApiException, ClientE return clientDocCommentGenerator.updateSyntaxTreeWithDocComments(); } + private void generateHttpImport() { + // Create `ballerina/http` import declaration node + List importForHttp = getImportDeclarationNodes(); + imports.addAll(importForHttp); + } + protected List getModuleMemberDeclarationNodes() throws BallerinaOpenApiException { List nodes = new ArrayList<>(); // Add class definition node to module member nodes @@ -239,7 +246,7 @@ public BallerinaUtilGenerator getBallerinaUtilGenerator() { * } * */ - private ClassDefinitionNode getClassDefinitionNode() { + protected ClassDefinitionNode getClassDefinitionNode() { // Collect members for class definition node List memberNodeList = new ArrayList<>(); // Add instance variable to class definition node @@ -268,47 +275,61 @@ private ClassDefinitionNode getClassDefinitionNode() { /** * This function is to filter the operations based on the user given tags and operations. */ - private Map> filterOperations() { - //todo refactor code - Map> filteredOperation = new HashMap<>(); + protected Map> filterOperations() { + Map> filteredOperations = new HashMap<>(); List filterTags = filter.getTags(); List filterOperations = filter.getOperations(); - Set> pathsItems = openAPI.getPaths().entrySet(); - for (Map.Entry path : pathsItems) { + for (Map.Entry pathEntry : openAPI.getPaths().entrySet()) { Map operations = new HashMap<>(); - if (!path.getValue().readOperationsMap().isEmpty()) { - Map operationMap = path.getValue().readOperationsMap(); - for (Map.Entry operation : operationMap.entrySet()) { - List operationTags = operation.getValue().getTags(); - String operationId = operation.getValue().getOperationId(); - if (!filterTags.isEmpty() || !filterOperations.isEmpty()) { - // Generate remote function only if it is available in tag filter or operation filter or both - if (operationTags != null || ((!filterOperations.isEmpty()) && (operationId != null))) { - if (isaFilteredOperation(filterTags, filterOperations, operationTags, operationId)) { - operations.put(operation.getKey(), operation.getValue()); - } - } - } else { - operations.put(operation.getKey(), operation.getValue()); - } - } - if (!operations.isEmpty()) { - filteredOperation.put(path.getKey(), operations); + Map operationMap = pathEntry.getValue().readOperationsMap(); + if (operationMap.isEmpty()) { + continue; + } + for (Map.Entry operationEntry : operationMap.entrySet()) { + Operation operation = operationEntry.getValue(); + List operationTags = operation.getTags(); + String operationId = operation.getOperationId(); + + if (shouldFilterOperation(filterTags, filterOperations, operationTags, operationId)) { + operations.put(operationEntry.getKey(), operation); } } + if (!operations.isEmpty()) { + filteredOperations.put(pathEntry.getKey(), operations); + } + } + + return filteredOperations; + } + + /** + * This includes the filtering logic for OAS operations. + */ + private boolean shouldFilterOperation(List filterTags, List filterOperations, + List operationTags, String operationId) { + boolean hasFilterTags = !filterTags.isEmpty(); + boolean hasFilterOperations = !filterOperations.isEmpty(); + boolean hasOperationTags = operationTags != null; + boolean hasOperationId = operationId != null; + + if (!hasFilterTags && !hasFilterOperations) { + return true; } - return filteredOperation; + + return (hasFilterTags && hasOperationTags && !Collections.disjoint(filterTags, operationTags)) || + (hasFilterOperations && hasOperationId && filterOperations.contains(operationId)); } + /** * Generate metadata node of the class including documentation and display annotation. Content of the documentation * will be taken from the `description` section inside the `info` section in OpenAPI definition. * * @return {@link MetadataNode} Metadata node of the client class */ - private MetadataNode getClassMetadataNode() { + protected MetadataNode getClassMetadataNode() { List classLevelAnnotationNodes = new ArrayList<>(); if (openAPI.getInfo().getExtensions() != null) { @@ -355,7 +376,7 @@ private MetadataNode getClassMetadataNode() { * @return {@link FunctionDefinitionNode} Class init function * @throws BallerinaOpenApiException When invalid server URL is provided */ - private FunctionDefinitionNode createInitFunction() { + FunctionDefinitionNode createInitFunction() { FunctionSignatureNode functionSignatureNode = getInitFunctionSignatureNode(); FunctionBodyNode functionBodyNode = getInitFunctionBodyNode(); @@ -371,7 +392,7 @@ private FunctionDefinitionNode createInitFunction() { * * @return {@link FunctionBodyNode} */ - private FunctionBodyNode getInitFunctionBodyNode() { + protected FunctionBodyNode getInitFunctionBodyNode() { List assignmentNodes = new ArrayList<>(); @@ -419,7 +440,7 @@ private FunctionBodyNode getInitFunctionBodyNode() { * @return {@link FunctionSignatureNode} * @throws BallerinaOpenApiException When invalid server URL is provided */ - private FunctionSignatureNode getInitFunctionSignatureNode() { + protected FunctionSignatureNode getInitFunctionSignatureNode() { List parameterNodes = new ArrayList<>(); ServerURLGeneratorImp serverURLGeneratorImp = new ServerURLGeneratorImp(openAPI.getServers()); ParameterNode serverURLNode = serverURLGeneratorImp.generateServerURL(); @@ -505,7 +526,7 @@ public static List ascendingOrder(List inputList) { * * @return {@link MetadataNode} Metadata node containing entire function documentation comment. */ - private MetadataNode getInitDocComment() { + protected MetadataNode getInitDocComment() { List docs = new ArrayList<>(); String clientInitDocComment = "Gets invoked to initialize the `connector`.\n"; @@ -544,7 +565,7 @@ private MetadataNode getInitDocComment() { * * @return {@link List} List of instance variables */ - private List createClassInstanceVariables() { + protected List createClassInstanceVariables() { List fieldNodeList = new ArrayList<>(); Token finalKeywordToken = createToken(FINAL_KEYWORD); @@ -568,14 +589,8 @@ protected QualifiedNameReferenceNode getHttpClientTypeName() { createIdentifierToken(GeneratorConstants.CLIENT)); } - private static boolean isaFilteredOperation(List filterTags, List filterOperations, - List operationTags, String operationId) { - return (operationTags != null && GeneratorUtils.hasTags(operationTags, filterTags)) || - ((operationId != null) && filterOperations.contains(operationId.trim())); - } - - private List createRemoteFunctions(Map> - filteredOperations) { + List createRemoteFunctions(Map> + filteredOperations) { List remoteFunctionNodes = new ArrayList<>(); for (Map.Entry> operation : filteredOperations.entrySet()) { for (Map.Entry operationEntry : operation.getValue().entrySet()) { @@ -620,7 +635,7 @@ protected RemoteFunctionGenerator getRemoteFunctionGenerator(Map.Entry createResourceFunctions(Map createResourceFunctions(Map> filteredOperations) { List resourceFunctionNodes = new ArrayList<>(); for (Map.Entry> operation : filteredOperations.entrySet()) { diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java index 4aed5f83c..b8d9da4b7 100644 --- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java +++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/BallerinaClientGeneratorWithStatusCodeBinding.java @@ -114,6 +114,7 @@ * @since 1.9.0 */ public class BallerinaClientGeneratorWithStatusCodeBinding extends BallerinaClientGenerator { + public BallerinaClientGeneratorWithStatusCodeBinding(OASClientConfig oasClientConfig) { super(oasClientConfig); authConfigGeneratorImp = new AuthConfigGeneratorWithStatusCodeBinding(false, false); @@ -206,7 +207,7 @@ protected ResourceFunctionGenerator getResourceFunctionGenerator(Map.Entry createImplFunction(String path, + protected Optional createImplFunction(String path, Map.Entry operation, OpenAPI openAPI, AuthConfigGeneratorImp authConfigGeneratorImp, @@ -219,26 +220,47 @@ private Optional createImplFunction(String path, Token functionKeyWord = createToken(FUNCTION_KEYWORD); IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId() + "Impl"); // Create function signature - ImplFunctionSignatureGenerator signatureGenerator = new ImplFunctionSignatureGenerator(operation.getValue(), - openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path, clientExternFunction); + ImplFunctionSignatureGenerator signatureGenerator = getImplFunctionSignatureGenerator(path, operation, + openAPI, clientExternFunction); //Create function body - FunctionBodyGeneratorImp functionBodyGenerator = new ImplFunctionBodyGenerator(path, operation, openAPI, - authConfigGeneratorImp, ballerinaUtilGenerator, imports, signatureGenerator.hasHeaders(), - signatureGenerator.hasDefaultHeaders(), signatureGenerator.hasQueries(), hasDefaultResponse, - nonDefaultStatusCodes); - FunctionBodyNode functionBodyNode; + FunctionBodyGenerator functionBodyGenerator = getFunctionBodyGeneratorImp(path, operation, openAPI, + authConfigGeneratorImp, ballerinaUtilGenerator, hasDefaultResponse, nonDefaultStatusCodes, + signatureGenerator); Optional functionBodyNodeResult = functionBodyGenerator.getFunctionBodyNode(); + diagnostics.addAll(functionBodyGenerator.getDiagnostics()); if (functionBodyNodeResult.isEmpty()) { return Optional.empty(); } - functionBodyNode = functionBodyNodeResult.get(); - + FunctionBodyNode functionBodyNode = functionBodyNodeResult.get(); Optional functionSignatureNode = signatureGenerator.generateFunctionSignature(); return functionSignatureNode.map(signatureNode -> NodeFactory.createFunctionDefinitionNode(OBJECT_METHOD_DEFINITION, null, qualifierList, functionKeyWord, functionName, createEmptyNodeList(), signatureNode, functionBodyNode)); } + protected ImplFunctionSignatureGenerator getImplFunctionSignatureGenerator(String path, + Map.Entry + operation, OpenAPI openAPI, + FunctionDefinitionNode + clientExternFunction) { + return new ImplFunctionSignatureGenerator(operation.getValue(), + openAPI, operation.getKey().toString().toLowerCase(Locale.ROOT), path, clientExternFunction); + } + + protected FunctionBodyGenerator getFunctionBodyGeneratorImp(String path, + Map.Entry operation, + OpenAPI openAPI, + AuthConfigGeneratorImp authConfigGeneratorImp, + BallerinaUtilGenerator ballerinaUtilGenerator, + boolean hasDefaultResponse, + List nonDefaultStatusCodes, + ImplFunctionSignatureGenerator signatureGenerator) { + return new ImplFunctionBodyGenerator(path, operation, openAPI, + authConfigGeneratorImp, ballerinaUtilGenerator, imports, signatureGenerator.hasHeaders(), + signatureGenerator.hasDefaultHeaders(), signatureGenerator.hasQueries(), hasDefaultResponse, + nonDefaultStatusCodes); + } + /** * Get the setModule function definition node. *
@@ -345,7 +367,7 @@ private AnnotationDeclarationNode getMethodImplAnnotation() {
      *
      * @return {@link TypeDefinitionNode} TypeDefinitionNode
      */
-    private TypeDefinitionNode getClientErrorType() {
+    protected TypeDefinitionNode getClientErrorType() {
         return createTypeDefinitionNode(null, null, createToken(TYPE_KEYWORD),
                 createIdentifierToken("ClientMethodInvocationError"),
                 createQualifiedNameReferenceNode(createIdentifierToken("http"), createToken(COLON_TOKEN),
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java
index 0dd050f7d..3d82b0e0e 100644
--- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/FunctionSignatureGenerator.java
@@ -21,13 +21,12 @@
 import io.ballerina.compiler.syntax.tree.FunctionSignatureNode;
 import io.ballerina.compiler.syntax.tree.Node;
 import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic;
-import io.ballerina.openapi.core.generators.client.exception.FunctionSignatureGeneratorException;
 
 import java.util.List;
 import java.util.Optional;
 
 public interface FunctionSignatureGenerator {
-    Optional generateFunctionSignature() throws FunctionSignatureGeneratorException;
+    Optional generateFunctionSignature();
     List getDiagnostics();
     boolean hasDefaultStatusCodeBinding();
     List getNonDefaultStatusCodes();
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java
index ad5e29d0c..3816d1c5e 100644
--- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/ImplFunctionSignatureGenerator.java
@@ -59,10 +59,8 @@ public class ImplFunctionSignatureGenerator {
 
     List parameterNodes = new ArrayList<>();
     ReturnTypeDescriptorNode returnTypeDescriptorNode;
-
     ResourceFunctionSignatureGenerator resourceFunctionSignatureGenerator;
 
-
     public ImplFunctionSignatureGenerator(Operation operation, OpenAPI openAPI, String httpMethod, String path,
                                           FunctionDefinitionNode clientExternFunction) {
         FunctionSignatureNode functionSignatureNode = clientExternFunction.functionSignature();
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/diagnostic/DiagnosticMessages.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/diagnostic/DiagnosticMessages.java
index 47550196f..6b8844dd7 100644
--- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/diagnostic/DiagnosticMessages.java
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/diagnostic/DiagnosticMessages.java
@@ -55,7 +55,14 @@ public enum DiagnosticMessages {
             DiagnosticSeverity.WARNING),
     OAS_CLIENT_114("OAS_CLIENT_114", "the operation: '%s' is skipped in the client generation since only a default" +
             " response found for the operation which is not supported with status code binding option",
-            DiagnosticSeverity.WARNING);
+            DiagnosticSeverity.WARNING),
+    OAS_CLIENT_115("OAS_CLIENT_115", "the operation for given path `%s` , " +
+            "method `%s` is skipped in the mock client function generation since it is not provided success response",
+            DiagnosticSeverity.WARNING),
+    OAS_CLIENT_116("OAS_CLIENT_116", "the operation for given path `%s` , method `%s` is skipped in " +
+            "the mock client function generation since it is not provided with examples", DiagnosticSeverity.WARNING),
+    OAS_CLIENT_117("OAS_CLIENT_117", "the operation for given path `%s` , method `%s` is skipped in " +
+            "the mock client function generation since it has invalid reference", DiagnosticSeverity.WARNING);
     private final String code;
     private final String description;
     private final DiagnosticSeverity severity;
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/AdvanceMockClientGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/AdvanceMockClientGenerator.java
new file mode 100644
index 000000000..8ea661804
--- /dev/null
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/AdvanceMockClientGenerator.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.ballerina.openapi.core.generators.client.mock;
+
+import io.ballerina.compiler.syntax.tree.FunctionBodyNode;
+import io.ballerina.compiler.syntax.tree.NodeList;
+import io.ballerina.compiler.syntax.tree.ObjectFieldNode;
+import io.ballerina.compiler.syntax.tree.ReturnStatementNode;
+import io.ballerina.compiler.syntax.tree.StatementNode;
+import io.ballerina.openapi.core.generators.client.AuthConfigGeneratorImp;
+import io.ballerina.openapi.core.generators.client.BallerinaClientGeneratorWithStatusCodeBinding;
+import io.ballerina.openapi.core.generators.client.BallerinaUtilGenerator;
+import io.ballerina.openapi.core.generators.client.FunctionBodyGenerator;
+import io.ballerina.openapi.core.generators.client.ImplFunctionSignatureGenerator;
+import io.ballerina.openapi.core.generators.client.model.OASClientConfig;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnStatementNode;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.RETURN_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.SEMICOLON_TOKEN;
+
+/**
+ * This class contains the advance client generation when the client generation enables with status code bindings.
+ *
+ * @since 2.1.0
+ */
+public class AdvanceMockClientGenerator extends BallerinaClientGeneratorWithStatusCodeBinding {
+    public AdvanceMockClientGenerator(OASClientConfig oasClientConfig) {
+        super(oasClientConfig);
+    }
+
+    @Override
+    public FunctionBodyNode getInitFunctionBodyNode() {
+        List assignmentNodes = new ArrayList<>();
+        ReturnStatementNode returnStatementNode = createReturnStatementNode(createToken(
+                RETURN_KEYWORD), null, createToken(SEMICOLON_TOKEN));
+        assignmentNodes.add(returnStatementNode);
+        NodeList statementList = createNodeList(assignmentNodes);
+        return createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN),
+                null, statementList, createToken(CLOSE_BRACE_TOKEN), null);
+    }
+
+    @Override
+    public FunctionBodyGenerator getFunctionBodyGeneratorImp(String path,
+                                                             Map.Entry operation,
+                                                             OpenAPI openAPI,
+                                                             AuthConfigGeneratorImp authConfigGeneratorImp,
+                                                             BallerinaUtilGenerator ballerinaUtilGenerator,
+                                                             boolean hasDefaultResponse,
+                                                             List nonDefaultStatusCodes,
+                                                             ImplFunctionSignatureGenerator signatureGenerator) {
+        return new MockFunctionBodyGenerator(path, operation, openAPI, true);
+    }
+
+    @Override
+    public List createClassInstanceVariables() {
+        List fieldNodeList = new ArrayList<>();
+        // add apiKey instance variable when API key security schema is given
+        ObjectFieldNode apiKeyFieldNode = authConfigGeneratorImp.getApiKeyMapClassVariable();
+        if (apiKeyFieldNode != null) {
+            fieldNodeList.add(apiKeyFieldNode);
+        }
+        return fieldNodeList;
+    }
+
+}
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/BallerinaMockClientGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/BallerinaMockClientGenerator.java
new file mode 100644
index 000000000..1bae074a5
--- /dev/null
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/BallerinaMockClientGenerator.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package io.ballerina.openapi.core.generators.client.mock;
+
+import io.ballerina.compiler.syntax.tree.ClassDefinitionNode;
+import io.ballerina.compiler.syntax.tree.FunctionBodyBlockNode;
+import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
+import io.ballerina.compiler.syntax.tree.FunctionSignatureNode;
+import io.ballerina.compiler.syntax.tree.IdentifierToken;
+import io.ballerina.compiler.syntax.tree.MetadataNode;
+import io.ballerina.compiler.syntax.tree.Node;
+import io.ballerina.compiler.syntax.tree.NodeList;
+import io.ballerina.compiler.syntax.tree.ObjectFieldNode;
+import io.ballerina.compiler.syntax.tree.ReturnStatementNode;
+import io.ballerina.compiler.syntax.tree.StatementNode;
+import io.ballerina.compiler.syntax.tree.SyntaxTree;
+import io.ballerina.compiler.syntax.tree.Token;
+import io.ballerina.openapi.core.generators.client.BallerinaClientGenerator;
+import io.ballerina.openapi.core.generators.client.exception.ClientException;
+import io.ballerina.openapi.core.generators.client.model.OASClientConfig;
+import io.ballerina.openapi.core.generators.common.GeneratorConstants;
+import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createClassDefinitionNode;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionDefinitionNode;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createReturnStatementNode;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLASS_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLIENT_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.FUNCTION_DEFINITION;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.FUNCTION_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.ISOLATED_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.PUBLIC_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.RETURN_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.SEMICOLON_TOKEN;
+
+/**
+ * This generator class is for generate the mock clients.
+ *
+ * @since 2.1.0
+ */
+public class BallerinaMockClientGenerator extends BallerinaClientGenerator {
+    public BallerinaMockClientGenerator(OASClientConfig oasClientConfig) {
+        super(oasClientConfig);
+    }
+
+    public FunctionDefinitionNode getInitFunction() {
+        FunctionSignatureNode functionSignatureNode = super.getInitFunctionSignatureNode();
+        NodeList qualifierList = createNodeList(createToken(PUBLIC_KEYWORD), createToken(ISOLATED_KEYWORD));
+        IdentifierToken functionName = createIdentifierToken("init");
+        List assignmentNodes = new ArrayList<>();
+        ReturnStatementNode returnStatementNode = createReturnStatementNode(createToken(
+                RETURN_KEYWORD), null, createToken(SEMICOLON_TOKEN));
+        assignmentNodes.add(returnStatementNode);
+        NodeList statementList = createNodeList(assignmentNodes);
+        FunctionBodyBlockNode functionBodyNode = createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN),
+                null, statementList, createToken(CLOSE_BRACE_TOKEN), null);
+        return createFunctionDefinitionNode(FUNCTION_DEFINITION, super.getInitDocComment(), qualifierList,
+                createToken(FUNCTION_KEYWORD), functionName, createEmptyNodeList(), functionSignatureNode,
+                functionBodyNode);
+    }
+
+    @Override
+    public ClassDefinitionNode getClassDefinitionNode() {
+        List memberNodeList = new ArrayList<>();
+        memberNodeList.addAll(createClassInstanceVariables());
+        memberNodeList.add(getInitFunction());
+        Map> filteredOperations = filterOperations();
+        List functionDefinitionNodeList = new ArrayList<>();
+        //Mock client function generation
+        for (Map.Entry> operation : filteredOperations.entrySet()) {
+            for (Map.Entry operationEntry : operation.getValue().entrySet()) {
+                MockClientFunctionGenerator mockClientFunctionGenerator = new MockClientFunctionGenerator(
+                        operation.getKey(), operationEntry, oasClientConfig);
+                Optional funDefOptionalNode = mockClientFunctionGenerator.generateFunction();
+                funDefOptionalNode.ifPresent(functionDefinitionNodeList::add);
+                diagnostics.addAll(mockClientFunctionGenerator.getDiagnostics());
+            }
+        }
+        memberNodeList.addAll(functionDefinitionNodeList);
+        MetadataNode metadataNode = getClassMetadataNode();
+        IdentifierToken className = createIdentifierToken(GeneratorConstants.CLIENT);
+        NodeList classTypeQualifiers = createNodeList(
+                createToken(ISOLATED_KEYWORD), createToken(CLIENT_KEYWORD));
+        return createClassDefinitionNode(metadataNode, createToken(PUBLIC_KEYWORD), classTypeQualifiers,
+                createToken(CLASS_KEYWORD), className, createToken(OPEN_BRACE_TOKEN),
+                createNodeList(memberNodeList), createToken(CLOSE_BRACE_TOKEN), null);
+
+    }
+
+    @Override
+    public SyntaxTree generateSyntaxTree() throws BallerinaOpenApiException, ClientException {
+        return getSyntaxTree();
+    }
+
+    @Override
+    public List createClassInstanceVariables() {
+        List fieldNodeList = new ArrayList<>();
+        // add apiKey instance variable when API key security schema is given
+        ObjectFieldNode apiKeyFieldNode = authConfigGeneratorImp.getApiKeyMapClassVariable();
+        if (apiKeyFieldNode != null) {
+            fieldNodeList.add(apiKeyFieldNode);
+        }
+        return fieldNodeList;
+    }
+}
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockClientFunctionGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockClientFunctionGenerator.java
new file mode 100644
index 000000000..c1f2c7028
--- /dev/null
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockClientFunctionGenerator.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.ballerina.openapi.core.generators.client.mock;
+
+import io.ballerina.compiler.syntax.tree.FunctionBodyNode;
+import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode;
+import io.ballerina.compiler.syntax.tree.FunctionSignatureNode;
+import io.ballerina.compiler.syntax.tree.IdentifierToken;
+import io.ballerina.compiler.syntax.tree.Node;
+import io.ballerina.compiler.syntax.tree.NodeFactory;
+import io.ballerina.compiler.syntax.tree.NodeList;
+import io.ballerina.compiler.syntax.tree.Token;
+import io.ballerina.openapi.core.generators.client.FunctionGenerator;
+import io.ballerina.openapi.core.generators.client.FunctionSignatureGenerator;
+import io.ballerina.openapi.core.generators.client.RemoteFunctionSignatureGenerator;
+import io.ballerina.openapi.core.generators.client.ResourceFunctionSignatureGenerator;
+import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic;
+import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp;
+import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages;
+import io.ballerina.openapi.core.generators.client.model.OASClientConfig;
+import io.ballerina.openapi.core.generators.common.GeneratorUtils;
+import io.ballerina.openapi.core.generators.common.exception.BallerinaOpenApiException;
+import io.ballerina.tools.diagnostics.Diagnostic;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createEmptyNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createIdentifierToken;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.FUNCTION_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.ISOLATED_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.OBJECT_METHOD_DEFINITION;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.REMOTE_KEYWORD;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.RESOURCE_ACCESSOR_DEFINITION;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.RESOURCE_KEYWORD;
+import static io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages.OAS_CLIENT_107;
+
+/**
+ * Mock client function generator.
+ *
+ * @since 2.1.0
+ */
+public class MockClientFunctionGenerator implements FunctionGenerator {
+    String path;
+    Map.Entry operation;
+    OpenAPI openAPI;
+    List diagnostics = new ArrayList<>();
+    boolean isResourceFunction;
+    OASClientConfig oasClientConfig;
+    FunctionSignatureGenerator signatureGenerator;
+    public MockClientFunctionGenerator(String path, Map.Entry operation,
+                                       OASClientConfig oasClientConfig) {
+        this.path = path;
+        this.operation = operation;
+        this.openAPI = oasClientConfig.getOpenAPI();
+        this.isResourceFunction = oasClientConfig.isResourceMode();
+        this.oasClientConfig = oasClientConfig;
+        this.signatureGenerator = oasClientConfig.isResourceMode() ? new ResourceFunctionSignatureGenerator(
+                operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ENGLISH), path) :
+                new RemoteFunctionSignatureGenerator(
+                        operation.getValue(), openAPI, operation.getKey().toString().toLowerCase(Locale.ENGLISH), path);
+    }
+
+    @Override
+    public Optional generateFunction() {
+        if (isResourceFunction) {
+            NodeList qualifierList = createNodeList(createToken(RESOURCE_KEYWORD),
+                    createToken(ISOLATED_KEYWORD));
+            Token functionKeyWord = createToken(FUNCTION_KEYWORD);
+            String method = operation.getKey().toString().toLowerCase(Locale.ROOT);
+            IdentifierToken functionName = createIdentifierToken(method);
+
+            List pathDiagnostics = new ArrayList<>();
+            NodeList relativeResourcePath;
+            try {
+                relativeResourcePath = GeneratorUtils.getRelativeResourcePath(path, operation.getValue(),
+                        openAPI.getComponents(), false, pathDiagnostics);
+            } catch (BallerinaOpenApiException e) {
+                DiagnosticMessages diagnosticMessages = OAS_CLIENT_107;
+                ClientDiagnosticImp clientDiagnosticImp = new ClientDiagnosticImp(diagnosticMessages);
+                diagnostics.add(clientDiagnosticImp);
+                return Optional.empty();
+            }
+            if (!pathDiagnostics.isEmpty()) {
+                pathDiagnostics.forEach(diagnostic -> {
+                    if (diagnostic.diagnosticInfo().code().equals("OAS_COMMON_204")) {
+                        DiagnosticMessages message = DiagnosticMessages.OAS_CLIENT_110;
+                        ClientDiagnosticImp clientDiagnostic = new ClientDiagnosticImp(message, path, method);
+                        diagnostics.add(clientDiagnostic);
+                    }
+                });
+                return Optional.empty();
+            }
+            //create function signature
+            Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature();
+            diagnostics.addAll(signatureGenerator.getDiagnostics());
+            if (signatureNodeOptional.isEmpty()) {
+                return Optional.empty();
+            }
+            FunctionSignatureNode signatureNode = signatureNodeOptional.get();
+            // function body generator
+            MockFunctionBodyGenerator bodyGenerator = new MockFunctionBodyGenerator(path, operation, openAPI,
+                    oasClientConfig.isStatusCodeBinding());
+            Optional functionBodyOptionalNode = bodyGenerator.getFunctionBodyNode();
+            diagnostics.addAll(bodyGenerator.getDiagnostics());
+            if (functionBodyOptionalNode.isEmpty()) {
+                return Optional.empty();
+            }
+            FunctionBodyNode functionBodyNode = functionBodyOptionalNode.get();
+            return Optional.of(NodeFactory.createFunctionDefinitionNode(RESOURCE_ACCESSOR_DEFINITION, null,
+                    qualifierList,
+                    functionKeyWord, functionName, relativeResourcePath, signatureNode, functionBodyNode));
+        } else {
+            //Create qualifier list
+            NodeList qualifierList = createNodeList(createToken(REMOTE_KEYWORD), createToken(ISOLATED_KEYWORD));
+            Token functionKeyWord = createToken(FUNCTION_KEYWORD);
+            IdentifierToken functionName = createIdentifierToken(operation.getValue().getOperationId());
+            Optional signatureNodeOptional = signatureGenerator.generateFunctionSignature();
+            diagnostics.addAll(signatureGenerator.getDiagnostics());
+            if (signatureNodeOptional.isEmpty()) {
+                return Optional.empty();
+            }
+            FunctionSignatureNode functionSignatureNode = signatureNodeOptional.get();
+            MockFunctionBodyGenerator bodyGenerator = new MockFunctionBodyGenerator(path, operation, openAPI,
+                    oasClientConfig.isStatusCodeBinding());
+            Optional functionBodyOptionalNode = bodyGenerator.getFunctionBodyNode();
+            diagnostics.addAll(bodyGenerator.getDiagnostics());
+            if (functionBodyOptionalNode.isEmpty()) {
+                return Optional.empty();
+            }
+            FunctionBodyNode functionBodyNode = functionBodyOptionalNode.get();
+            return Optional.of(NodeFactory.createFunctionDefinitionNode(OBJECT_METHOD_DEFINITION, null,
+                    qualifierList, functionKeyWord, functionName, createEmptyNodeList(), functionSignatureNode,
+                    functionBodyNode));
+        }
+    }
+
+    @Override
+    public List getDiagnostics() {
+        return diagnostics;
+    }
+
+    @Override
+    public boolean hasDefaultStatusCodeBinding() {
+        return signatureGenerator.hasDefaultStatusCodeBinding();
+    }
+
+    @Override
+    public List getNonDefaultStatusCodes() {
+        return signatureGenerator.getNonDefaultStatusCodes();
+    }
+}
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockFunctionBodyGenerator.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockFunctionBodyGenerator.java
new file mode 100644
index 000000000..d1b7e0f89
--- /dev/null
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/mock/MockFunctionBodyGenerator.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com).
+ *
+ * WSO2 LLC. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.ballerina.openapi.core.generators.client.mock;
+
+import io.ballerina.compiler.syntax.tree.FunctionBodyBlockNode;
+import io.ballerina.compiler.syntax.tree.FunctionBodyNode;
+import io.ballerina.compiler.syntax.tree.NodeList;
+import io.ballerina.compiler.syntax.tree.NodeParser;
+import io.ballerina.compiler.syntax.tree.StatementNode;
+import io.ballerina.compiler.syntax.tree.TypeDescriptorNode;
+import io.ballerina.openapi.core.generators.client.FunctionBodyGenerator;
+import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnostic;
+import io.ballerina.openapi.core.generators.client.diagnostic.ClientDiagnosticImp;
+import io.ballerina.openapi.core.generators.client.diagnostic.DiagnosticMessages;
+import io.ballerina.openapi.core.generators.common.GeneratorConstants;
+import io.ballerina.openapi.core.generators.common.GeneratorUtils;
+import io.ballerina.openapi.core.generators.common.exception.InvalidReferenceException;
+import io.ballerina.tools.diagnostics.Diagnostic;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.Operation;
+import io.swagger.v3.oas.models.PathItem;
+import io.swagger.v3.oas.models.examples.Example;
+import io.swagger.v3.oas.models.media.MediaType;
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.oas.models.responses.ApiResponse;
+import io.swagger.v3.oas.models.responses.ApiResponses;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Optional;
+
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createNodeList;
+import static io.ballerina.compiler.syntax.tree.AbstractNodeFactory.createToken;
+import static io.ballerina.compiler.syntax.tree.NodeFactory.createFunctionBodyBlockNode;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.CLOSE_BRACE_TOKEN;
+import static io.ballerina.compiler.syntax.tree.SyntaxKind.OPEN_BRACE_TOKEN;
+import static io.ballerina.openapi.core.generators.common.GeneratorConstants.HTTP_201;
+import static io.ballerina.openapi.core.generators.common.GeneratorConstants.POST;
+import static io.ballerina.openapi.core.generators.common.GeneratorConstants.RESPONSE;
+import static io.ballerina.openapi.core.generators.common.GeneratorUtils.generateStatusCodeTypeInclusionRecord;
+
+/**
+ * Mock function body generator.
+ *
+ * @since 2.1.0
+ */
+public class MockFunctionBodyGenerator implements FunctionBodyGenerator {
+    String path;
+    Map.Entry operation;
+    OpenAPI openAPI;
+    List diagnostics = new ArrayList<>();
+    boolean isAdvanceClient;
+
+    public MockFunctionBodyGenerator(String path, Map.Entry operation,
+                                     OpenAPI openAPI, boolean isAdvanceClient) {
+        this.path = path;
+        this.operation = operation;
+        this.openAPI = openAPI;
+        this.isAdvanceClient = isAdvanceClient;
+    }
+
+    @Override
+    public Optional getFunctionBodyNode() {
+
+        ApiResponses responses = operation.getValue().getResponses();
+        String method = operation.getKey().toString().toLowerCase(Locale.ENGLISH);
+        //Get the successful response
+        ApiResponse successResponse;
+        String code;
+        //Collect all success responses
+        Map successResponses = new HashMap<>();
+        for (Map.Entry response : responses.entrySet()) {
+            if (response.getKey().startsWith("2")) {
+                successResponses.put(response.getKey(), response.getValue());
+            }
+        }
+        if (successResponses.isEmpty()) {
+            ClientDiagnosticImp diagnosticImp = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_115,
+                    path, operation.getKey().toString());
+            diagnostics.add(diagnosticImp);
+            return Optional.empty();
+        }
+        if (method.equals(POST) && successResponses.containsKey(HTTP_201)) {
+            successResponse = successResponses.get(HTTP_201);
+            code = HTTP_201;
+        } else {
+            Optional> firstRes = successResponses.entrySet().stream().findFirst();
+            successResponse = firstRes.get().getValue();
+            code = firstRes.get().getKey().toLowerCase(Locale.ENGLISH);
+        }
+
+        // Here only consider 2xx response, since this range consider for success status code
+        if (successResponse.getContent() == null) {
+            ClientDiagnosticImp diagnosticImp = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_115,
+                    path, operation.getKey().toString());
+            diagnostics.add(diagnosticImp);
+            return Optional.empty();
+        }
+        Map examples = new HashMap<>();
+        for (Map.Entry mediaType : successResponse.getContent().entrySet()) {
+            MediaType value = mediaType.getValue();
+            Schema schema = value.getSchema();
+
+            if (value.getExamples() != null) {
+                examples = value.getExamples();
+            } else if (value.getExample() != null) {
+                Object exampleObject = value.getExample();
+                Example example = new Example();
+                example.setValue(exampleObject);
+                examples.put(RESPONSE, example);
+            } else {
+                try {
+                    examples = getExamplesFromSchema(schema);
+                } catch (InvalidReferenceException e) {
+                    ClientDiagnosticImp diagnosticImp = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_117,
+                            path, operation.getKey().toString());
+                    diagnostics.add(diagnosticImp);
+                    return Optional.empty();
+                }
+            }
+        }
+
+        if (examples.isEmpty()) {
+            ClientDiagnosticImp diagnosticImp = new ClientDiagnosticImp(DiagnosticMessages.OAS_CLIENT_116,
+                    path, operation.getKey().toString());
+            diagnostics.add(diagnosticImp);
+            return Optional.empty();
+        }
+
+        Example example = examples.get(RESPONSE);
+        if (example == null) {
+            example = examples.values().iterator().next();
+        }
+        String statement;
+        try {
+            if (example.get$ref() != null) {
+                String exampleName = GeneratorUtils.extractReferenceType(example.get$ref());
+                Map exampleMap = openAPI.getComponents().getExamples();
+                example = exampleMap.get(exampleName);
+            }
+            String exampleValue = example.getValue().toString();
+            //TODO implement if the response has header example
+            if (isAdvanceClient) {
+                List newDiagnostics = new ArrayList<>();
+                statement = getReturnForAdvanceClient(successResponse, code, exampleValue, newDiagnostics);
+                diagnostics.addAll(newDiagnostics.stream().map(ClientDiagnosticImp::new).toList());
+            } else {
+                statement = "return " + exampleValue + ";";
+            }
+        } catch (InvalidReferenceException e) {
+            diagnostics.add(new ClientDiagnosticImp(e.getDiagnostic()));
+            return Optional.empty();
+        }
+        StatementNode returnNode = NodeParser.parseStatement(statement);
+        NodeList statementList = createNodeList(returnNode);
+
+        FunctionBodyBlockNode fBodyBlock = createFunctionBodyBlockNode(createToken(OPEN_BRACE_TOKEN),
+                null, statementList, createToken(CLOSE_BRACE_TOKEN), null);
+        return Optional.of(fBodyBlock);
+    }
+
+    private String getReturnForAdvanceClient(ApiResponse successResponse, String code, String exampleValue,
+                                             List newDiagnostics) throws InvalidReferenceException {
+        String statement;
+        code = GeneratorConstants.HTTP_CODES_DES.get(code);
+        String method = operation.getKey().toString().toLowerCase(Locale.ENGLISH);
+        TypeDescriptorNode typeDescriptorNode = generateStatusCodeTypeInclusionRecord(code,
+                successResponse, method, openAPI, path, newDiagnostics);
+        statement = "return  <" + typeDescriptorNode.toSourceCode() + " > {\n" +
+                "            body : " + exampleValue + "\n" +
+                "        };";
+        return statement;
+    }
+
+    private Map getExamplesFromSchema(Schema schema) throws InvalidReferenceException {
+        Map examples = new HashMap<>();
+        if (schema.getExample() != null) {
+            Object exampleObject = schema.getExample();
+            Example example = new Example();
+            example.setValue(exampleObject);
+            examples.put(RESPONSE, example);
+        } else if (schema.getExamples() != null) {
+            List schemaExamples = schema.getExamples();
+            Object exampleObject = schemaExamples.get(0);
+            Example example = new Example();
+            example.setValue(exampleObject);
+            examples.put(RESPONSE, example);
+        } else if (schema.get$ref() != null) {
+            String ref = schema.get$ref();
+            String refName = GeneratorUtils.extractReferenceType(ref);
+            schema = openAPI.getComponents().getSchemas().get(refName);
+            if (schema != null) {
+                return getExamplesFromSchema(schema);
+            }
+        }
+        return examples;
+    }
+
+    @Override
+    public List getDiagnostics() {
+        return diagnostics;
+    }
+}
diff --git a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
index cc6baed5b..5c0c9e8ea 100644
--- a/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
+++ b/openapi-core/src/main/java/io/ballerina/openapi/core/generators/client/model/OASClientConfig.java
@@ -35,6 +35,7 @@ public class OASClientConfig {
     private final boolean isPlugin;
     private final String license;
     private final boolean statusCodeBinding;
+    private final boolean isMock;
 
 
     private OASClientConfig(Builder clientConfigBuilder) {
@@ -45,6 +46,7 @@ private OASClientConfig(Builder clientConfigBuilder) {
         this.resourceMode = clientConfigBuilder.resourceMode;
         this.license = clientConfigBuilder.license;
         this.statusCodeBinding = clientConfigBuilder.statusCodeBinding;
+        this.isMock = clientConfigBuilder.isMock;
     }
 
     public OpenAPI getOpenAPI() {
@@ -73,17 +75,22 @@ public boolean isStatusCodeBinding() {
         return statusCodeBinding;
     }
 
+    public boolean isMock() {
+        return isMock;
+    }
+
     /**
      * Client IDL plugin meta data builder class.
      */
     public static class Builder {
         private OpenAPI openAPI;
-        private Filter filter;
+        private Filter filter = new Filter();
         private boolean nullable = false;
         private boolean resourceMode = true;
         private boolean isPlugin = false;
         private String license = DO_NOT_MODIFY_FILE_HEADER;
         private boolean statusCodeBinding = false;
+        private boolean isMock = false;
 
         public Builder withOpenAPI(OpenAPI openAPI) {
             this.openAPI = openAPI;
@@ -120,6 +127,11 @@ public Builder withStatusCodeBinding(boolean statusCodeBinding) {
             return this;
         }
 
+        public Builder withMock(boolean isMock) {
+            this.isMock = isMock;
+            return this;
+        }
+
         public OASClientConfig build() {
             return new OASClientConfig(this);
         }
diff --git a/openapi-core/src/main/java/module-info.java b/openapi-core/src/main/java/module-info.java
index 8565e8664..8c2585f81 100644
--- a/openapi-core/src/main/java/module-info.java
+++ b/openapi-core/src/main/java/module-info.java
@@ -38,7 +38,6 @@
     requires swagger.parser.v3;
     requires org.apache.commons.lang3;
 
-//    exports io.ballerina.openapi.core.generators.service;
     exports io.ballerina.openapi.core.generators.common.model;
     exports io.ballerina.openapi.core.generators.common.exception;
     exports io.ballerina.openapi.core.generators.client.model;
@@ -60,4 +59,5 @@
     exports io.ballerina.openapi.core.generators.service.response;
     exports io.ballerina.openapi.core.generators.service.diagnostic;
     exports io.ballerina.openapi.core.generators.type.diagnostic;
+    exports io.ballerina.openapi.core.generators.client.mock;
 }