From e5dee54797d0583f7c9aaf1dfde28f8dd3ad497c Mon Sep 17 00:00:00 2001 From: Viacheslav Pivovarov Date: Sat, 5 Oct 2024 11:12:30 +0400 Subject: [PATCH] [R][client] Make enum parameters with `required: false` optional to request arguments (#19654) * ODM-11697: Adding null check in R-api * bin/generate-samples.sh executed --------- Co-authored-by: Kunitsyn Oleg <114428801+genestack-okunitsyn@users.noreply.github.com> --- .../src/main/resources/r/api.mustache | 10 ++--- .../codegen/r/RClientCodegenTest.java | 41 +++++++++++++++++++ .../src/test/resources/bugs/issue_18016.yaml | 27 ++++++++++++ samples/client/echo_api/r/R/query_api.R | 2 +- .../petstore/R-httr2-wrapper/R/pet_api.R | 2 +- samples/client/petstore/R-httr2/R/pet_api.R | 2 +- samples/client/petstore/R/R/pet_api.R | 2 +- 7 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/bugs/issue_18016.yaml diff --git a/modules/openapi-generator/src/main/resources/r/api.mustache b/modules/openapi-generator/src/main/resources/r/api.mustache index 14e3fe6b54aa..781391d120c6 100644 --- a/modules/openapi-generator/src/main/resources/r/api.mustache +++ b/modules/openapi-generator/src/main/resources/r/api.mustache @@ -324,7 +324,7 @@ {{#items}} {{#isEnum}} # validate enum values - if (!(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { + if (!is.null(query_item) && !(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { {{#useDefaultExceptionHandling}} stop("Invalid value for {{{paramName}}} when calling {{classname}}${{operationId}}. Must be {{_enum}}.") {{/useDefaultExceptionHandling}} @@ -346,7 +346,7 @@ {{#isEnum}} # validate enum values for (query_item in `{{{paramName}}}`) { - if (!(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { + if (!is.null(query_item) && !(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { {{#useDefaultExceptionHandling}} stop("Invalid value for {{{paramName}}} when calling {{classname}}${{operationId}}. Must be {{_enum}}.") {{/useDefaultExceptionHandling}} @@ -370,7 +370,7 @@ {{#items}} {{#isEnum}} # validate enum values - if (!(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { + if (!is.null(query_item) && !(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { {{#useDefaultExceptionHandling}} stop("Invalid value for {{{paramName}}} when calling {{classname}}${{operationId}}. Must be {{_enum}}.") {{/useDefaultExceptionHandling}} @@ -392,7 +392,7 @@ {{#isEnum}} # validate enum values for (query_item in `{{{paramName}}}`) { - if (!(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { + if (!is.null(query_item) && !(query_item %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { {{#useDefaultExceptionHandling}} stop("Invalid value for {{{paramName}}} when calling {{classname}}${{operationId}}. Must be {{_enum}}.") {{/useDefaultExceptionHandling}} @@ -412,7 +412,7 @@ {{/isArray}} {{^isArray}} {{#isEnum}} - if (!(`{{paramName}}` %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { + if (!is.null(`{{paramName}}`) && !(`{{paramName}}` %in% c({{#_enum}}"{{{.}}}"{{^-last}}, {{/-last}}{{/_enum}}))) { {{#useDefaultExceptionHandling}} stop("Invalid value for {{{paramName}}} when calling {{classname}}${{operationId}}. Must be {{_enum}}.") {{/useDefaultExceptionHandling}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/r/RClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/r/RClientCodegenTest.java index 9656eea98b28..69adc13f1cbf 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/r/RClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/r/RClientCodegenTest.java @@ -17,11 +17,21 @@ package org.openapitools.codegen.r; +import io.swagger.v3.oas.models.OpenAPI; +import org.openapitools.codegen.ClientOptInput; import org.openapitools.codegen.CodegenConstants; +import org.openapitools.codegen.DefaultGenerator; +import org.openapitools.codegen.TestUtils; import org.openapitools.codegen.languages.RClientCodegen; import org.testng.Assert; import org.testng.annotations.Test; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.regex.Pattern; + public class RClientCodegenTest { @Test @@ -53,4 +63,35 @@ public void testAdditionalPropertiesPutForConfigValues() throws Exception { Assert.assertEquals(codegen.isHideGenerationTimestamp(), false); } + @Test + public void testNullCheckOnEnumValues() throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + final DefaultGenerator defaultGenerator = new DefaultGenerator(); + + RClientCodegen rClientCodegen = new RClientCodegen(); + rClientCodegen.setOutputDir(output.getAbsolutePath()); + + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/bugs/issue_18016.yaml"); + final ClientOptInput clientOptInput = new ClientOptInput(); + clientOptInput.openAPI(openAPI); + clientOptInput.config(rClientCodegen); + defaultGenerator.opts(clientOptInput); + + var petsApi = defaultGenerator.generate().stream() + .filter(file -> "pets_api.R".equals(file.getName())).findFirst(); + if (petsApi.isEmpty()) { + Assert.fail("`pets_api.R` have not been generated"); + } + var isIfCondition = Pattern.compile("^\\s*(?!<#)\\s*if.*\\s%in%\\s.*").asPredicate(); + var containsNullCheck = Pattern.compile("![(\\s]*is\\.null").asPredicate(); + var hit = false; + for (var line: Files.readAllLines(Paths.get(petsApi.get().getAbsolutePath()))) { + if (isIfCondition.test(line)) { + hit = true; + Assert.assertTrue(containsNullCheck.test(line), "Null check is missing in line: " + line); + } + } + Assert.assertTrue(hit, "No if statement for enum found"); + } } diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_18016.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_18016.yaml new file mode 100644 index 000000000000..28453c7ea767 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_18016.yaml @@ -0,0 +1,27 @@ +openapi: "3.0.0" +info: + version: 1.0.0 + description: Specification to generate null check for enums in R client + title: RClientNullCheck +paths: + /exists: + get: + tags: + - pets + parameters: + - name: type + in: query + description: Type of pet + required: false + schema: + enum: + - cat + - dog + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: boolean diff --git a/samples/client/echo_api/r/R/query_api.R b/samples/client/echo_api/r/R/query_api.R index a4459a898099..e4f2697ae717 100644 --- a/samples/client/echo_api/r/R/query_api.R +++ b/samples/client/echo_api/r/R/query_api.R @@ -223,7 +223,7 @@ QueryApi <- R6::R6Class( - if (!(`enum_nonref_string_query` %in% c("success", "failure", "unclassified"))) { + if (!is.null(`enum_nonref_string_query`) && !(`enum_nonref_string_query` %in% c("success", "failure", "unclassified"))) { stop("Invalid value for enum_nonref_string_query when calling QueryApi$TestEnumRefString. Must be [success, failure, unclassified].") } query_params[["enum_nonref_string_query"]] <- `enum_nonref_string_query` diff --git a/samples/client/petstore/R-httr2-wrapper/R/pet_api.R b/samples/client/petstore/R-httr2-wrapper/R/pet_api.R index 8619642a8321..e5e359b0526f 100644 --- a/samples/client/petstore/R-httr2-wrapper/R/pet_api.R +++ b/samples/client/petstore/R-httr2-wrapper/R/pet_api.R @@ -664,7 +664,7 @@ PetApi <- R6::R6Class( # explore for (query_item in `status`) { # validate enum values - if (!(query_item %in% c("available", "pending", "sold"))) { + if (!is.null(query_item) && !(query_item %in% c("available", "pending", "sold"))) { rlang::abort(message = "Invalid value for `status` when calling PetApi$find_pets_by_status. Must be [available, pending, sold].", .subclass = "ApiException", ApiException = ApiException$new(status = 0, diff --git a/samples/client/petstore/R-httr2/R/pet_api.R b/samples/client/petstore/R-httr2/R/pet_api.R index 407235e30b5a..506a0a2e33c7 100644 --- a/samples/client/petstore/R-httr2/R/pet_api.R +++ b/samples/client/petstore/R-httr2/R/pet_api.R @@ -664,7 +664,7 @@ PetApi <- R6::R6Class( # explore for (query_item in `status`) { # validate enum values - if (!(query_item %in% c("available", "pending", "sold"))) { + if (!is.null(query_item) && !(query_item %in% c("available", "pending", "sold"))) { rlang::abort(message = "Invalid value for `status` when calling PetApi$find_pets_by_status. Must be [available, pending, sold].", .subclass = "ApiException", ApiException = ApiException$new(status = 0, diff --git a/samples/client/petstore/R/R/pet_api.R b/samples/client/petstore/R/R/pet_api.R index a1dae0ea497a..b1bae279acce 100644 --- a/samples/client/petstore/R/R/pet_api.R +++ b/samples/client/petstore/R/R/pet_api.R @@ -664,7 +664,7 @@ PetApi <- R6::R6Class( # explore for (query_item in `status`) { # validate enum values - if (!(query_item %in% c("available", "pending", "sold"))) { + if (!is.null(query_item) && !(query_item %in% c("available", "pending", "sold"))) { rlang::abort(message = "Invalid value for `status` when calling PetApi$FindPetsByStatus. Must be [available, pending, sold].", .subclass = "ApiException", ApiException = ApiException$new(status = 0,