Skip to content

Commit

Permalink
#67 #68 enhance REST service interface
Browse files Browse the repository at this point in the history
  • Loading branch information
eblondel committed Oct 6, 2015
1 parent 3a5f365 commit f742004
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 119 deletions.
8 changes: 4 additions & 4 deletions R/Class-SDMXRequestBuilder.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
setClass("SDMXRequestBuilder",
representation(
baseUrl = "character",
suffix = "logical",
handler = "function"
handler = "function",
compliant = "logical"
),
prototype = list(
baseUrl = "http://www.myorg.org/sdmx",
suffix = TRUE,
handler = function(baseUrl, agencyId){
paste(baseUrl, agencyId,sep="/")
}
},
compliant = TRUE
),
validity = function(object){
return(TRUE);
Expand Down
61 changes: 42 additions & 19 deletions R/SDMXRESTRequestBuilder-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,57 @@
#========================

#constructor
SDMXRESTRequestBuilder <- function(baseUrl, suffix){
SDMXRESTRequestBuilder <- function(baseUrl, compliant){

serviceRequestHandler <- function(baseUrl, agencyId, suffix, operation, key,
filter = NULL, start = NULL, end = NULL){
#function to handle request
serviceRequestHandler <- function(baseUrl, agencyId, resource, flowRef,
key = NULL, start = NULL, end = NULL,
compliant){

if(is.null(operation))
stop("Missing SDMX service operation")
if(is.null(resource))
stop("Missing SDMX service resource")

#base request
req <- paste(baseUrl, operation, key, sep = "/")

#filter (if any)
if(!is.null(filter)) req <- paste(req, filter, sep="/")
restResource <- switch(resource,
"data" = ifelse(compliant, resource, "GetData")
)

#suffix
reqSuffix <- "?"
if(suffix) reqSuffix <- paste0(agencyId, "?")
req <- paste(req, reqSuffix, sep="/")
#wrap argument values
obj <- list(baseUrl = baseUrl, agencyId = agencyId, resource = restResource,
flowRef = flowRef, key = key, start = start, end = end)

#temporal extent (if any)
if(!is.null(start) && !is.null(end)){
req <- paste0(req, "startPeriod=", start, "&endPeriod=", end)
}
#REST resource handler
resourceHandler <- switch(resource,

#'data' resource
"data" = function(obj){

if(is.null(obj$flowRef)){
stop("Missing flowRef value")
}
if(is.null(obj$key)) obj$key = "all"

#base data request
req <- paste(obj$baseUrl, obj$resource, obj$flowRef, obj$key, sep = "/")

#DataQuery
#-> temporal extent (if any)
if(!is.null(obj$start) && !is.null(obj$end)){
req <- paste0(req, "/?", "startPeriod=", start, "&endPeriod=", end)
}

}

)

#handle rest resource path
req <- resourceHandler(obj)
return(req)
}

new("SDMXRESTRequestBuilder", baseUrl = baseUrl, suffix = suffix, handler = serviceRequestHandler)
new("SDMXRESTRequestBuilder",
baseUrl = baseUrl,
handler = serviceRequestHandler,
compliant = compliant)
}


Expand Down
4 changes: 2 additions & 2 deletions R/SDMXRequestBuilder-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
#========================

#constructor
SDMXRequestBuilder <- function(baseUrl, suffix, handler){
new("SDMXRequestBuilder", baseUrl = baseUrl, suffix = suffix, handler = handler)
SDMXRequestBuilder <- function(baseUrl, handler, compliant){
new("SDMXRequestBuilder", baseUrl = baseUrl, handler = handler, compliant = compliant)
}
4 changes: 2 additions & 2 deletions R/SDMXServiceProvider-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ setSDMXServiceProviders <- function(){
#OECD
SDMXServiceProvider(
"OECD", "Organisation for Economic Cooperation and Development ",
SDMXRESTRequestBuilder("http://stats.oecd.org/restsdmx/sdmx.ashx", TRUE)
SDMXRESTRequestBuilder("http://stats.oecd.org/restsdmx/sdmx.ashx", FALSE)
),

#UN-FAO
Expand All @@ -41,7 +41,7 @@ setSDMXServiceProviders <- function(){
#UN-ILO
SDMXServiceProvider(
"ILO", "International Labour Organization of the United Nations",
SDMXRESTRequestBuilder("http://www.ilo.org/ilostat/sdmx/ws/rest", FALSE)
SDMXRESTRequestBuilder("http://www.ilo.org/ilostat/sdmx/ws/rest", TRUE)
),

#UIS (UNESCO)
Expand Down
24 changes: 14 additions & 10 deletions R/readSDMX.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
# function to read SDMX as character string or from helper arguments
# (required in order to encapsulate a S3 old class object in a S4 representation)
readSDMX <- function(file = NULL, isURL = TRUE,
provider = NULL, agencyId = NULL,operation = NULL,
key = NULL, filter = NULL, filter.native = TRUE, start = NULL, end = NULL) {
provider = NULL, agencyId = NULL, resource = NULL,
flowRef = NULL, key = NULL, key.mode = "R", start = NULL, end = NULL) {

if(!(key.mode %in% c("R", "SDMX"))){
stop("Invalid value for key.mode argument. Accepted values are 'R', 'SDMX' ")
}

#check from arguments if request has to be performed
buildRequest <- FALSE
Expand All @@ -27,21 +31,21 @@ readSDMX <- function(file = NULL, isURL = TRUE,
#proceed with the request build
if(buildRequest){

if(filter.native && !missing(filter) && !is.null(missing)){
filter <- paste(sapply(filter, paste, collapse = "+"), collapse=".")
if(key.mode == "R" && !missing(key) && !is.null(key)){
key <- paste(sapply(key, paste, collapse = "+"), collapse=".")
}

if(is.null(operation)) stop("SDMX service operation cannot be null")
if(is.null(key)) stop("SDMX service key cannot be null")
if(is.null(resource)) stop("SDMX service resource cannot be null")
if(is.null(flowRef)) stop("SDMX service flowRef cannot be null")
file <- provider@builder@handler(
baseUrl = provider@builder@baseUrl,
agencyId = provider@agencyId,
suffix = provider@builder@suffix,
operation = operation,
resource = resource,
flowRef = flowRef,
key = key,
filter = filter,
start = start,
end = end)
end = end,
compliant = provider@builder@compliant)
message(file)
}

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,13 @@ First create a request builder for our provider:
myBuilder <- SDMXRequestBuilder(
baseUrl = "http://www.myorg.org/sdmx",
suffix = TRUE,
handler = function(baseUrl, agencyId, suffix, operation, key, filter, suffix, start, end){
paste(baseUrl, operation, key, filter, paste0(agencyId, "?startPeriod=", start, "&endPeriod=", end), sep="/")
handler = function(baseUrl, agencyId, resource, flowRef, key, start, end, compliant){
paste(baseUrl, agencyId, resource, flowRef, key, start, end, sep="/")
}
)
```

As you can see, we built a handler that will be in charge of creating a web-request such as [http://www.myorg.org/sdmx/operation/key/filter/agencyId?startPeriod=start&endPeriod=end](http://www.myorg.org/sdmx/operation/key/filter/agencyId?startPeriod=start&endPeriod=end)
As you can see, we built a handler that will be in charge of creating a web-request such as [http://www.myorg.org/sdmx/agencyId/resource/flowRef/key/start/end](http://www.myorg.org/sdmx/agencyId/resource/flowRef/key/start/end)

We can create a provider with the above request builder, and add it to the list of known SDMX service providers:

Expand Down Expand Up @@ -174,8 +174,8 @@ oecd <- findSDMXServiceProvider("OECD")
Now you know how to add a SDMX provider, you can consider using ``readSDMX`` without having to specifying a entire URL, but just by specifying the ``agencyId`` of the provider, and the different query parameters to reach your SDMX document:

```{r, echo = FALSE}
sdmx <- readSDMX(agencyId = "MYORG", operation = "data", key="MYSERIE",
filter="ALL", filter.native = FALSE, start = 2000, end = 2015)
sdmx <- readSDMX(agencyId = "MYORG", resource = "data", flowRef="MYSERIE",
key = "all", key.mode = "SDMX", start = 2000, end = 2015)
```

The following sections will show you how to query SDMX documents, by using ``readSDMX`` in different ways: either for _local_ or _remote_ files, using ``readSDMX`` as low-level or with the helpers.
Expand Down Expand Up @@ -205,8 +205,8 @@ The online rsdmx documentation also provides a list of data providers, either fr
Now, the service providers above mentioned are known by ``rsdmx`` which let users using ``readSDMX`` with the helper parameters. Let's see how it would look like for querying an OECD datasource:

```{r, echo = FALSE}
sdmx <- readSDMX(agencyId = "OECD", operation = "GetData", key = "MIG",
filter = list("TOT", NULL, NULL), start = 2010, end = 2011)
sdmx <- readSDMX(agencyId = "OECD", resource = "data", flowRef = "MIG",
key = list("TOT", NULL, NULL), start = 2010, end = 2011)
df <- as.data.frame(sdmx)
head(df)
```
Expand Down
6 changes: 3 additions & 3 deletions man/SDMXRESTRequestBuilder-class.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
\section{Slots}{
\describe{
\item{\code{baseUrl}}{an object of class "character" giving the base Url of the SDMX service endpoint}
\item{\code{suffix}}{an object of class "logical" indicating if the provider \code{agencyId} has to be used as suffix in the data web-request, before start/end url parameters. For some reasons, we noticed that some providers, e.g. UN-ILO were not authorizing to specify the \code{agencyId} as suffix.}
\item{\code{compliant}}{an object of class "logical" indicating if the web-service is compliant with the SDMX REST web-service specifications}
}
}

\usage{
SDMXRESTRequestBuilder(baseUrl, suffix)
SDMXRESTRequestBuilder(baseUrl, compliant)
}

\arguments{
\item{baseUrl}{an object of class "character" giving the base Url of the SDMX service endpoint}
\item{suffix}{an object of class "logical" indicating if the provider \code{agencyId} has to be used as suffix in the data web-request, before start/end url parameters}
\item{compliant}{an object of class "logical" indicating if the web-service is compliant with the SDMX REST web-service specifications}
}

\author{ Emmanuel Blondel, \email{emmanuel.blondel1@gmail.com}}
Expand Down
21 changes: 9 additions & 12 deletions man/SDMXRequestBuilder-class.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,42 @@
\section{Slots}{
\describe{
\item{\code{baseUrl}}{an object of class "character" giving the base Url of the SDMX service endpoint}
\item{\code{suffix}}{an object of class "logical" indicating if the provider \code{agencyId} has to be used as suffix in the data web-request, before start/end url parameters}
\item{\code{handler}}{an object of class "function" that will be in charge of build a web request.}
\item{\code{handler}}{an object of class "function" that will be in charge of build a web request.}
\item{\code{compliant}}{an object of class "logical" indicating if the request builder is somehow compliant with a service specification}
}
}

\usage{
SDMXRequestBuilder(baseUrl, suffix, handler)
SDMXRequestBuilder(baseUrl, handler, compliant)
}

\arguments{
\item{baseUrl}{an object of class "character" giving the base Url of the SDMX service endpoint}
\item{suffix}{an object of class "logical" indicating if the provider \code{agencyId} has to be used as suffix in the data web-request, before start/end url parameters}
\item{handler}{an object of class "function" that will be in charge of build a web request.}
\item{compliant}{an object of class "logical" indicating if the request builder is somehow compliant with a service specification}
}

\details{

The \code{handler} function must have the following structure in term of arguments (baseUrl, operation, key, filter, suffix,
start, end) and output (a string representing the web request to build).
The \code{handler} function must have the following structure in term of arguments (baseUrl, agencyId, resource, flowRef, key = NULL, start = NULL, end = NULL, compliant) and output (a string representing the web request to build).

The rsdmx package will as much as possible try to handler generic handlers, e.g. an handler for SDMX REST web-services.
For the latter example, a specific builder is provided and made part of the specific and still experimental \link{SDMXRESTRequestBuilder}.
The rsdmx package will as much as possible try to handler generic handlers, e.g. an handler for SDMX REST web-services. For the latter example, a specific builder is provided and made part of the specific and still experimental \link{SDMXRESTRequestBuilder}.

}

\author{ Emmanuel Blondel, \email{emmanuel.blondel1@gmail.com}}
\examples{

#an handler
myHandler <- function(baseUrl, agencyId, suffix, operation, key, filter, start, end){
req <- paste(baseUrl, agencyId, operation, key, filter, start, end, sep="/")
myHandler <- function(baseUrl, agencyId, resource, flowRef, key, start, end, compliant){
req <- paste(baseUrl, agencyId, resource, flowRef, key, start, end, sep="/")
return(req)
}

#how to create a SDMXRequestBuilder
requestBuilder <- SDMXRequestBuilder(
baseUrl = "http://www.myorg.",
suffix = TRUE,
handler = myHandler)
handler = myHandler, compliant = FALSE)
}
\keyword{classes}
\keyword{builder}
Expand Down
2 changes: 1 addition & 1 deletion man/SDMXServiceProvider-class.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ SDMXServiceProvider(agencyId, name, builder)

#let's create a SDMXRESTRequestBuilder
#(assuming that "My Organization" implements SDMX REST web-services)
myBuilder <- SDMXRESTRequestBuilder(baseUrl = "http://www.myorg.", suffix = TRUE)
myBuilder <- SDMXRESTRequestBuilder(baseUrl = "http://www.myorg.org", compliant = TRUE)

#create a SDMXServiceProvider
provider <- SDMXServiceProvider(agencyId = "MYORG", name = "My Organization", builder = myBuilder)
Expand Down
2 changes: 1 addition & 1 deletion man/addSDMXServiceProvider.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ addSDMXServiceProvider(provider)

#create a provider
myBuilder <- SDMXRESTRequestBuilder(baseUrl = "http://www.myorg.",
suffix = TRUE)
compliant = TRUE)
myProvider <- SDMXServiceProvider(
agencyId = "MYORG", name = "My Organization",
builder = myBuilder
Expand Down
17 changes: 8 additions & 9 deletions man/readSDMX.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

\usage{
readSDMX(file, isURL,
provider, agencyId, operation, key,
filter, filter.native, start, end)
provider, agencyId, resource, flowRef,
key, key.mode, start, end)
}

\arguments{
Expand All @@ -18,11 +18,10 @@ readSDMX(file, isURL,
specific provider given as argument)}
\item{provider}{an object of class "SDMXServiceProvider". If specified, \code{file} and \code{isURL} arguments will be ignored.}
\item{agencyId}{an object of class "character" representing a provider id. It has to be match a default provider as listed in \code{getSDMXServiceProviders()}}
\item{operation}{An object of class "character" giving the SDMX service request operation to perform. e.g. "GetData". Recognized if
a valid provider or provide id has been specified as argument.}
\item{key}{An object of class "character" giving the SDMX key family (data structure) id. Recognized if valid provider or provide id has been specified as argument.}
\item{filter}{An object of class "character" or "list" giving the SDMX data filter to apply. Recognized if a valid provider or provide id has been specified as argument. If \code{filter.native} is \code{TRUE} (default value), filter has to be an object of class "list" representing the filters to apply to the dataset, otherwise the filter will be a string.}
\item{filter.native}{An object of class "boolean" indicating if the filter has to be provided in the native way, ie a object of class "list" representing the filter(s) to apply. Default value is \code{TRUE}}
\item{resource}{An object of class "character" giving the SDMX service request resource to query e.g. "data". Recognized if a valid provider or provide id has been specified as argument.}
\item{flowRef}{An object of class "character" giving the SDMX flow ref (data structure) id. Recognized if valid provider or provide id has been specified as argument.}
\item{key}{An object of class "character" or "list" giving the SDMX data key/filter to apply. Recognized if a valid provider or provide id has been specified as argument. If \code{key.mode} is equal to \code{"R"} (default value), filter has to be an object of class "list" representing the filters to apply to the dataset, otherwise the filter will be a string.}
\item{key.mode}{An object of class "character" indicating if the \code{key} has to be provided as an R object, ie a object of class "list" representing the filter(s) to apply. Default value is \code{"R"}. Alternative value is \code{"SDMX"}}
\item{start}{An object of class "integer" or "character" giving the SDMX start time to apply. Recognized if a valid provider or provide id has been specified as argument.}
\item{end}{An object of class "integer" or "character" giving the SDMX end time to apply. Recognized if a valid provider or provide id has been specified as argument.}
}
Expand Down Expand Up @@ -80,8 +79,8 @@ stats3 <- as.data.frame(sdmx3)
head(stats3)

#examples using helpers!
sdmx4 <- readSDMX(agencyId = "OECD", operation = "GetData", key = "MIG",
filter = list("TOT", NULL, NULL), start = 2011, end = 2011)
sdmx4 <- readSDMX(agencyId = "OECD", resource = "data", flowRef = "MIG",
key = list("TOT", NULL, NULL), start = 2011, end = 2011)
stats4 <- as.data.frame(sdmx4)
head(stats4)

Expand Down
Loading

0 comments on commit f742004

Please sign in to comment.