Ian
Ian

Reputation: 185

calling an API in local Rstudio with multiple values as input for one variable

I can call the api from local Rstudio by this script successfully:

The api is published in Domino datalab

library(httr)
library(rjson)
url <- "XXX"
response <- POST(
url,
authenticate("XXX", "XXX", type = "basic"),
body=toJSON(list(data=list( 
"product_list_Zone"= "ZONE 1", 
"product_list_brand"= "A1", 
"product_list_FY22SRP"=390,
"commodityPrice"= 415.59))),
content_type("application/json"))

However, I want to have multiple values for each of "product_list_brand" and "product_list_FY22SRP". Say, "product_list_brand" can be A1 and A2, and "product_list_FY22SRP" will be 390 (corresponding to A1) and 400 (corresponding to A2).

I tried something like replacing "product_list_brand"= "A1" with "product_list_brand"= c("A1, A2") but this won't work. I also tried "product_list_brand"= toJSON(c("A1, A2")) but it won't work still.

So how should I have a correct output with multiple values for each of "product_list_brand" and "product_list_FY22SRP" inputs? I think it's a format issue and I can call my api successfully function using "product_list_brand"= c("A1, A2") (but not calling the api, it did not work).

(and one interesting thing is I have to use the format body=toJSON(list(data=list())) here, as per the example from Domino datalab, so far using other formats will occur errors. )

Thanks in advance!

Upvotes: 0

Views: 288

Answers (1)

r2evans
r2evans

Reputation: 160607

Whether or not you can pass multiple values in on argument is controlled wholly on the API side, it has nothing to do with the client. If the remote end does not support it, the only thing you can do is repeat the query with individual values.

Perhaps:

func <- function(url, bodies, ..., expand = FALSE) {
  bodies <- if (expand) do.call(expand.grid, bodies) else as.data.frame(bodies)
  bodies <- do.call(mapply, c(list(FUN = function(...) jsonlite::toJSON(list(data = list(...)))), bodies))
  lapply(bodies, function(body) do.call(httr::POST, c(list(url = url, body = body), ...)))
}

func("XXX",
     bodies = list("product_list_Zone" = "ZONE 1", "product_list_brand" = c("A1", "A2"),
                   "product_list_FY22SRP" = 390, "commodityPrice" = 415.59),
     httr::authenticate("XXX", "XXX", type = "basic"),
     httr::content_type("application/json"))

This should always return a list, each element is the result from one call to the API.

The only difference that expand= makes is if/when you provide more than one multi-value argument. For an example, I'll use two zones and two brands, and interrupt the steps to show the body passed to each call to func:

cat(sep = "\n",
  func("XXX", bodies = list("product_list_Zone" = c("ZONE 1", "ZONE 2"), "product_list_brand" = c("A1","A2"), "product_list_FY22SRP" = 390, "commodityPrice" = 415.59), httr::authenticate("XXX", "XXX", type = "basic"), httr::content_type("application/json"))
)
# {"data":{"product_list_Zone":["ZONE 1"],"product_list_brand":["A1"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}
# {"data":{"product_list_Zone":["ZONE 2"],"product_list_brand":["A2"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}

cat(sep = "\n",
  func("XXX", bodies = list("product_list_Zone" = c("ZONE 1", "ZONE 2"), "product_list_brand" = c("A1","A2"), "product_list_FY22SRP" = 390, "commodityPrice" = 415.59), httr::authenticate("XXX", "XXX", type = "basic"), httr::content_type("application/json"), expand = TRUE)
)
# {"data":{"product_list_Zone":["ZONE 1"],"product_list_brand":["A1"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}
# {"data":{"product_list_Zone":["ZONE 2"],"product_list_brand":["A1"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}
# {"data":{"product_list_Zone":["ZONE 1"],"product_list_brand":["A2"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}
# {"data":{"product_list_Zone":["ZONE 2"],"product_list_brand":["A2"],"product_list_FY22SRP":[390],"commodityPrice":[415.59]}}

With expand=FALSE (the default), it is assumed that the arguments will "recycle" (in R's sense) naturally: all vectors are either:

  • length 1, therefore repeated the length of the longest argument
  • length n, the longest argument
  • length of some integral divisor of n (side point: this is the feature of R's recycling that I do not think is a sane default ...)

For example, data.frame(a=1, b=1:2, d=1:6) will not err, and shows all three of the rules above.

With expand=TRUE, though, it uses expand.grid to find all permutations of all arguments.

Caveat emptor: I did not test this with an actual API, so the POST functionality might need some tweaking.

Upvotes: 1

Related Questions