Skip to content

Commit

Permalink
[R][Client] Fix api response, JSON for maps and let httr2 api client …
Browse files Browse the repository at this point in the history
…handle empty response bodies (OpenAPITools#18049)

* changed files from rebuilding project and updating samples

* change default value of from_encoding to empty string

- from_encoding = NULL is invalid and produces an error, as only strings are allowed, see https://www.rdocumentation.org/packages/base/versions/3.6.2/topics/iconv
- defaulting to an empty string fixes this issue

* fix map being surrounded by quotes in json

* allow httr2 client to deal with empty response body

* changed files from rebuilding project and updating samples

* added PetMap schema for test of correct map serialization in toJSONString and regenerated samples
  • Loading branch information
joXemMx authored Mar 13, 2024
1 parent 7e8de91 commit 6075b8a
Show file tree
Hide file tree
Showing 28 changed files with 663 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ ApiResponse <- R6::R6Class(
#' @param from_encoding The encoding of the raw response.
#' @param to_encoding The target encoding of the return value.
#' @export
response_as_text = function(from_encoding = NULL, to_encoding = "UTF-8") {
response_as_text = function(from_encoding = "", to_encoding = "UTF-8") {
text_response <- iconv(readBin(self$response, character()), from = from_encoding, to = to_encoding)
if (is.na(text_response)) {
warning("The response is binary and will not be converted to text.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,11 @@ ApiClient <- R6::R6Class(
api_response <- ApiResponse$new()
api_response$status_code <- resp %>% resp_status()
api_response$status_code_desc <- resp %>% resp_status_desc()
api_response$response <- resp %>% resp_body_raw()
if (length(resp$body) == 0) {
api_response$response <- NULL
} else {
api_response$response <- resp %>% resp_body_raw()
}
api_response$headers <- resp %>% resp_headers()
api_response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@
{{/isArray}}
{{#isMap}}
{{#isPrimitiveType}}
{{#isNumeric}}%d{{/isNumeric}}{{^isNumeric}}{{^isBoolean}}"{{/isBoolean}}%s{{^isBoolean}}"{{/isBoolean}}{{/isNumeric}}
{{#isNumeric}}%d{{/isNumeric}}{{^isNumeric}}{{^isBoolean}}{{/isBoolean}}%s{{^isBoolean}}{{/isBoolean}}{{/isNumeric}}
{{/isPrimitiveType}}
{{^isPrimitiveType}}%s
{{/isPrimitiveType}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,15 @@ components:
- sold
xml:
name: Pet
PetMap:
title: a PetMap
description: A mock map of a pet and some properties
type: object
properties:
pet:
type: object
additionalProperties:
type: string
ApiResponse:
title: An uploaded response
description: Describes the result of uploading an image resource
Expand Down
2 changes: 1 addition & 1 deletion samples/client/echo_api/r/R/api_response.R
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ApiResponse <- R6::R6Class(
#' @param from_encoding The encoding of the raw response.
#' @param to_encoding The target encoding of the return value.
#' @export
response_as_text = function(from_encoding = NULL, to_encoding = "UTF-8") {
response_as_text = function(from_encoding = "", to_encoding = "UTF-8") {
text_response <- iconv(readBin(self$response, character()), from = from_encoding, to = to_encoding)
if (is.na(text_response)) {
warning("The response is binary and will not be converted to text.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ R/one_of_primitive_type_test.R
R/order.R
R/pet.R
R/pet_api.R
R/pet_map.R
R/petstore_api.R
R/pig.R
R/special.R
Expand Down Expand Up @@ -59,6 +60,7 @@ docs/OneOfPrimitiveTypeTest.md
docs/Order.md
docs/Pet.md
docs/PetApi.md
docs/PetMap.md
docs/Pig.md
docs/Special.md
docs/StoreApi.md
Expand Down
1 change: 1 addition & 0 deletions samples/client/petstore/R-httr2-wrapper/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export(NestedOneOf)
export(OneOfPrimitiveTypeTest)
export(Order)
export(Pet)
export(PetMap)
export(Pig)
export(Special)
export(Tag)
Expand Down
6 changes: 5 additions & 1 deletion samples/client/petstore/R-httr2-wrapper/R/api_client.R
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ ApiClient <- R6::R6Class(
api_response <- ApiResponse$new()
api_response$status_code <- resp %>% resp_status()
api_response$status_code_desc <- resp %>% resp_status_desc()
api_response$response <- resp %>% resp_body_raw()
if (length(resp$body) == 0) {
api_response$response <- NULL
} else {
api_response$response <- resp %>% resp_body_raw()
}
api_response$headers <- resp %>% resp_headers()

api_response
Expand Down
2 changes: 1 addition & 1 deletion samples/client/petstore/R-httr2-wrapper/R/api_response.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ ApiResponse <- R6::R6Class(
#' @param from_encoding The encoding of the raw response.
#' @param to_encoding The target encoding of the return value.
#' @export
response_as_text = function(from_encoding = NULL, to_encoding = "UTF-8") {
response_as_text = function(from_encoding = "", to_encoding = "UTF-8") {
text_response <- iconv(readBin(self$response, character()), from = from_encoding, to = to_encoding)
if (is.na(text_response)) {
warning("The response is binary and will not be converted to text.")
Expand Down
195 changes: 195 additions & 0 deletions samples/client/petstore/R-httr2-wrapper/R/pet_map.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
#' Create a new PetMap
#'
#' @description
#' A mock map of a pet and some properties
#'
#' @docType class
#' @title PetMap
#' @description PetMap Class
#' @format An \code{R6Class} generator object
#' @field pet named list(character) [optional]
#' @field _field_list a list of fields list(character)
#' @field additional_properties additional properties list(character) [optional]
#' @importFrom R6 R6Class
#' @importFrom jsonlite fromJSON toJSON
#' @export
PetMap <- R6::R6Class(
"PetMap",
public = list(
`pet` = NULL,
`_field_list` = c("pet"),
`additional_properties` = list(),
#' Initialize a new PetMap class.
#'
#' @description
#' Initialize a new PetMap class.
#'
#' @param pet pet
#' @param additional_properties additional properties (optional)
#' @param ... Other optional arguments.
#' @export
initialize = function(`pet` = NULL, additional_properties = NULL, ...) {
if (!is.null(`pet`)) {
stopifnot(is.vector(`pet`), length(`pet`) != 0)
sapply(`pet`, function(x) stopifnot(is.character(x)))
self$`pet` <- `pet`
}
if (!is.null(additional_properties)) {
for (key in names(additional_properties)) {
self$additional_properties[[key]] <- additional_properties[[key]]
}
}
},
#' To JSON string
#'
#' @description
#' To JSON String
#'
#' @return PetMap in JSON format
#' @export
toJSON = function() {
PetMapObject <- list()
if (!is.null(self$`pet`)) {
PetMapObject[["pet"]] <-
self$`pet`
}
for (key in names(self$additional_properties)) {
PetMapObject[[key]] <- self$additional_properties[[key]]
}

PetMapObject
},
#' Deserialize JSON string into an instance of PetMap
#'
#' @description
#' Deserialize JSON string into an instance of PetMap
#'
#' @param input_json the JSON input
#' @return the instance of PetMap
#' @export
fromJSON = function(input_json) {
this_object <- jsonlite::fromJSON(input_json)
if (!is.null(this_object$`pet`)) {
self$`pet` <- ApiClient$new()$deserializeObj(this_object$`pet`, "map(character)", loadNamespace("petstore"))
}
# process additional properties/fields in the payload
for (key in names(this_object)) {
if (!(key %in% self$`_field_list`)) { # json key not in list of fields
self$additional_properties[[key]] <- this_object[[key]]
}
}

self
},
#' To JSON string
#'
#' @description
#' To JSON String
#'
#' @return PetMap in JSON format
#' @export
toJSONString = function() {
jsoncontent <- c(
if (!is.null(self$`pet`)) {
sprintf(
'"pet":
%s
',
jsonlite::toJSON(lapply(self$`pet`, function(x){ x }), auto_unbox = TRUE, digits = NA)
)
}
)
jsoncontent <- paste(jsoncontent, collapse = ",")
json_string <- as.character(jsonlite::minify(paste("{", jsoncontent, "}", sep = "")))
json_obj <- jsonlite::fromJSON(json_string)
for (key in names(self$additional_properties)) {
json_obj[[key]] <- self$additional_properties[[key]]
}
json_string <- as.character(jsonlite::minify(jsonlite::toJSON(json_obj, auto_unbox = TRUE, digits = NA)))
},
#' Deserialize JSON string into an instance of PetMap
#'
#' @description
#' Deserialize JSON string into an instance of PetMap
#'
#' @param input_json the JSON input
#' @return the instance of PetMap
#' @export
fromJSONString = function(input_json) {
this_object <- jsonlite::fromJSON(input_json)
self$`pet` <- ApiClient$new()$deserializeObj(this_object$`pet`, "map(character)", loadNamespace("petstore"))
# process additional properties/fields in the payload
for (key in names(this_object)) {
if (!(key %in% self$`_field_list`)) { # json key not in list of fields
self$additional_properties[[key]] <- this_object[[key]]
}
}

self
},
#' Validate JSON input with respect to PetMap
#'
#' @description
#' Validate JSON input with respect to PetMap and throw an exception if invalid
#'
#' @param input the JSON input
#' @export
validateJSON = function(input) {
input_json <- jsonlite::fromJSON(input)
},
#' To string (JSON format)
#'
#' @description
#' To string (JSON format)
#'
#' @return String representation of PetMap
#' @export
toString = function() {
self$toJSONString()
},
#' Return true if the values in all fields are valid.
#'
#' @description
#' Return true if the values in all fields are valid.
#'
#' @return true if the values in all fields are valid.
#' @export
isValid = function() {
TRUE
},
#' Return a list of invalid fields (if any).
#'
#' @description
#' Return a list of invalid fields (if any).
#'
#' @return A list of invalid fields (if any).
#' @export
getInvalidFields = function() {
invalid_fields <- list()
invalid_fields
},
#' Print the object
#'
#' @description
#' Print the object
#'
#' @export
print = function() {
print(jsonlite::prettify(self$toJSONString()))
invisible(self)
}
),
# Lock the class to prevent modifications to the method or field
lock_class = TRUE
)
## Uncomment below to unlock the class to allow modifications of the method or field
# PetMap$unlock()
#
## Below is an example to define the print function
# PetMap$set("public", "print", function(...) {
# print(jsonlite::prettify(self$toJSONString()))
# invisible(self)
# })
## Uncomment below to lock the class to prevent modifications to the method or field
# PetMap$lock()

1 change: 1 addition & 0 deletions samples/client/petstore/R-httr2-wrapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ Class | Method | HTTP request | Description
- [OneOfPrimitiveTypeTest](docs/OneOfPrimitiveTypeTest.md)
- [Order](docs/Order.md)
- [Pet](docs/Pet.md)
- [PetMap](docs/PetMap.md)
- [Pig](docs/Pig.md)
- [Special](docs/Special.md)
- [Tag](docs/Tag.md)
Expand Down
10 changes: 10 additions & 0 deletions samples/client/petstore/R-httr2-wrapper/docs/PetMap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# petstore::PetMap

A mock map of a pet and some properties

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**pet** | **map(character)** | | [optional]


Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Automatically generated by openapi-generator (https://openapi-generator.tech)
# Please update as you see appropriate

context("Test PetMap")

model_instance <- PetMap$new()

test_that("pet", {
# tests for the property `pet` (map(character))

# uncomment below to test the property
#expect_equal(model.instance$`pet`, "EXPECTED_RESULT")
})
2 changes: 2 additions & 0 deletions samples/client/petstore/R-httr2/.openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ R/one_of_primitive_type_test.R
R/order.R
R/pet.R
R/pet_api.R
R/pet_map.R
R/pig.R
R/special.R
R/store_api.R
Expand Down Expand Up @@ -58,6 +59,7 @@ docs/OneOfPrimitiveTypeTest.md
docs/Order.md
docs/Pet.md
docs/PetApi.md
docs/PetMap.md
docs/Pig.md
docs/Special.md
docs/StoreApi.md
Expand Down
1 change: 1 addition & 0 deletions samples/client/petstore/R-httr2/NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export(NestedOneOf)
export(OneOfPrimitiveTypeTest)
export(Order)
export(Pet)
export(PetMap)
export(Pig)
export(Special)
export(Tag)
Expand Down
6 changes: 5 additions & 1 deletion samples/client/petstore/R-httr2/R/api_client.R
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,11 @@ ApiClient <- R6::R6Class(
api_response <- ApiResponse$new()
api_response$status_code <- resp %>% resp_status()
api_response$status_code_desc <- resp %>% resp_status_desc()
api_response$response <- resp %>% resp_body_raw()
if (length(resp$body) == 0) {
api_response$response <- NULL
} else {
api_response$response <- resp %>% resp_body_raw()
}
api_response$headers <- resp %>% resp_headers()

api_response
Expand Down
2 changes: 1 addition & 1 deletion samples/client/petstore/R-httr2/R/api_response.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ ApiResponse <- R6::R6Class(
#' @param from_encoding The encoding of the raw response.
#' @param to_encoding The target encoding of the return value.
#' @export
response_as_text = function(from_encoding = NULL, to_encoding = "UTF-8") {
response_as_text = function(from_encoding = "", to_encoding = "UTF-8") {
text_response <- iconv(readBin(self$response, character()), from = from_encoding, to = to_encoding)
if (is.na(text_response)) {
warning("The response is binary and will not be converted to text.")
Expand Down
Loading

0 comments on commit 6075b8a

Please sign in to comment.