blob: 8d5a3662f059754439116cd43b7f3f1bcedb84c1 [file] [log] [blame]
Marc Kupietzfd9e7492019-11-08 15:45:18 +01001################################################################################
2# Use setClassUnion to define the unholy NULL-data union as a virtual class.
3################################################################################
4setClassUnion("characterOrNULL", c("character", "NULL"))
5
Marc Kupietze95108e2019-09-18 13:23:58 +02006#' Class KorAPConnection
Marc Kupietz25aebc32019-09-16 18:40:50 +02007#'
Akron5e135462019-09-27 16:31:38 +02008#' \code{KorAPConnection} objects represent the connection to a KorAP server.
Marc Kupietz7715e9d2019-11-08 15:59:58 +01009#' New \code{KorAPConnection} objects can be created by \code{new("KorAPConnection")}.
Marc Kupietze95108e2019-09-18 13:23:58 +020010#'
Marc Kupietz0a96b282019-10-01 11:05:31 +020011#' @import R.cache
Marc Kupietze95108e2019-09-18 13:23:58 +020012#' @import utils
13#' @import methods
Marc Kupietze95108e2019-09-18 13:23:58 +020014#' @export
Marc Kupietzb49afa02020-06-04 15:50:29 +020015KorAPConnection <- setClass("KorAPConnection", slots=c(KorAPUrl="character", apiVersion="character", indexRevision="characterOrNULL", apiUrl="character", accessToken="characterOrNULL", userAgent="character", timeout="numeric", verbose="logical", cache="logical"))
Marc Kupietze95108e2019-09-18 13:23:58 +020016
17#' @param .Object KorAPConnection object
18#' @param KorAPUrl the URL of the KorAP server instance you want to access.
19#' @param apiVersion which version of KorAP's API you want to connect to.
20#' @param apiUrl URL of the KorAP web service.
Marc Kupietzb956b812019-11-25 17:53:13 +010021#' @param accessToken OAuth2 access token. To use authorization based on an access token
Marc Kupietz43a6ade2020-02-18 17:01:44 +010022#' in subsequent queries, initialize your KorAP connection with
Marc Kupietzb956b812019-11-25 17:53:13 +010023#' \code{kco <- new("KorAPConnection", accessToken="<access token>")}.
Marc Kupietz4862b862019-11-07 10:13:53 +010024#' In order to make the API
25#' token persistent for the currently used \code{KorAPUrl} (you can have one
26#' token per KorAPUrl / KorAP server instance), use
Marc Kupietzb956b812019-11-25 17:53:13 +010027#' \code{persistAccessToken(kco)}. This will store it in your keyring using the
Marc Kupietz4862b862019-11-07 10:13:53 +010028#' \code{\link{keyring}} package. Subsequent new("KorAPConnection") calls will
29#' then automatically retrieve the token from your keying. To stop using a
Marc Kupietzb956b812019-11-25 17:53:13 +010030#' persisted token, call \code{clearAccessToken(kco)}. Please note that for
Marc Kupietz4862b862019-11-07 10:13:53 +010031#' DeReKo, authorized queries will behave differently inside and outside the
32#' IDS, because of the special license situation. This concerns also cached
33#' results which do not take into account from where a request was issued. If
34#' you experience problems or unexpected results, please try \code{kco <-
35#' new("KorAPConnection", cache=FALSE)} or use
36#' \code{\link{clearCache}} to clear the cache completely.
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +020037#' @param userAgent user agent string.
38#' @param timeout time out in seconds.
Marc Kupietz4862b862019-11-07 10:13:53 +010039#' @param verbose logical. Decides whether following operations will default to
40#' be verbose.
41#' @param cache logical. Decides if API calls are cached locally. You can clear
42#' the cache with \code{\link{clearCache}()}.
43#' @return \code{\link{KorAPConnection}} object that can be used e.g. with
44#' \code{\link{corpusQuery}}
Marc Kupietze95108e2019-09-18 13:23:58 +020045#'
46#' @examples
Marc Kupietz05b22772020-02-18 21:58:42 +010047#' \donttest{
Marc Kupietz5a519822019-09-20 21:43:52 +020048#' kcon <- new("KorAPConnection", verbose = TRUE)
Marc Kupietze95108e2019-09-18 13:23:58 +020049#' kq <- corpusQuery(kcon, "Ameisenplage")
Marc Kupietz5a519822019-09-20 21:43:52 +020050#' kq <- fetchAll(kq)
Marc Kupietz05b22772020-02-18 21:58:42 +010051#' }
Marc Kupietz7915dc42019-09-12 17:44:58 +020052#'
Marc Kupietz4862b862019-11-07 10:13:53 +010053#' \dontrun{
Marc Kupietzb956b812019-11-25 17:53:13 +010054#' kcon <- new("KorAPConnection", verbose = TRUE, accessToken="e739u6eOzkwADQPdVChxFg")
Marc Kupietz4862b862019-11-07 10:13:53 +010055#' kq <- corpusQuery(kcon, "Ameisenplage", metadataOnly=FALSE)
56#' kq <- fetchAll(kq)
57#' kq@collectedMatches$snippet
58#' }
Marc Kupietz7915dc42019-09-12 17:44:58 +020059#'
Marc Kupietze95108e2019-09-18 13:23:58 +020060#' @rdname KorAPConnection-class
Marc Kupietz632cbd42019-09-06 16:04:51 +020061#' @export
Marc Kupietze95108e2019-09-18 13:23:58 +020062setMethod("initialize", "KorAPConnection",
Marc Kupietz8a82af72019-12-12 12:58:22 +010063 function(.Object, KorAPUrl = "https://korap.ids-mannheim.de/", apiVersion = 'v1.0', apiUrl, accessToken = getAccessToken(KorAPUrl), userAgent = "R-KorAP-Client", timeout=110, verbose = FALSE, cache = TRUE) {
Marc Kupietze95108e2019-09-18 13:23:58 +020064 .Object <- callNextMethod()
65 m <- regexpr("https?://[^?]+", KorAPUrl, perl = TRUE)
66 .Object@KorAPUrl <- regmatches(KorAPUrl, m)
67 if (!endsWith(.Object@KorAPUrl, '/')) {
68 .Object@KorAPUrl <- paste0(.Object@KorAPUrl, "/")
69 }
70 if (missing(apiUrl)) {
71 .Object@apiUrl = paste0(.Object@KorAPUrl, 'api/', apiVersion, '/')
72 } else {
73 .Object@apiUrl = apiUrl
74 }
Marc Kupietzb956b812019-11-25 17:53:13 +010075 .Object@accessToken = accessToken
Marc Kupietze95108e2019-09-18 13:23:58 +020076 .Object@apiVersion = apiVersion
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +020077 .Object@userAgent = userAgent
78 .Object@timeout = timeout
Marc Kupietz5a519822019-09-20 21:43:52 +020079 .Object@verbose = verbose
Marc Kupietz0a96b282019-10-01 11:05:31 +020080 .Object@cache = cache
Marc Kupietzb49afa02020-06-04 15:50:29 +020081 welcome <- apiCall(.Object, .Object@apiUrl, json = FALSE, cache = FALSE, getHeaders = TRUE)
82 message(welcome[[2]])
83 .Object@indexRevision <- welcome[[1]][["x-index-revision"]]
Marc Kupietze95108e2019-09-18 13:23:58 +020084 .Object
85 })
86
Marc Kupietza96537f2019-11-09 23:07:44 +010087
Marc Kupietzb956b812019-11-25 17:53:13 +010088accessTokenServiceName <- "RKorAPClientAccessToken"
Marc Kupietz4862b862019-11-07 10:13:53 +010089
Marc Kupietzb956b812019-11-25 17:53:13 +010090setGeneric("persistAccessToken", function(kco, ...) standardGeneric("persistAccessToken") )
Marc Kupietz4862b862019-11-07 10:13:53 +010091
Marc Kupietzb956b812019-11-25 17:53:13 +010092#' @aliases persistAccessToken
Marc Kupietz4862b862019-11-07 10:13:53 +010093#' @rdname KorAPConnection-class
94#' @import keyring
95#' @export
96#' @examples
97#' \dontrun{
Marc Kupietzb956b812019-11-25 17:53:13 +010098#' kco <- new("KorAPConnection", accessToken="e739u6eOzkwADQPdVChxFg")
99#' persistAccessToken(kco)
Marc Kupietz4862b862019-11-07 10:13:53 +0100100#' }
101#'
Marc Kupietzb956b812019-11-25 17:53:13 +0100102setMethod("persistAccessToken", "KorAPConnection", function(kco, accessToken = kco@accessToken) {
103 if (is.null(accessToken))
104 stop("It seems that you have not supplied any access token that could be persisted.", call. = FALSE)
Marc Kupietz4862b862019-11-07 10:13:53 +0100105
Marc Kupietzb956b812019-11-25 17:53:13 +0100106 kco@accessToken <- accessToken
107 key_set_with_value(accessTokenServiceName, kco@KorAPUrl, accessToken)
Marc Kupietz4862b862019-11-07 10:13:53 +0100108})
109
Marc Kupietzb956b812019-11-25 17:53:13 +0100110setGeneric("clearAccessToken", function(kco) standardGeneric("clearAccessToken") )
Marc Kupietz4862b862019-11-07 10:13:53 +0100111
Marc Kupietzb956b812019-11-25 17:53:13 +0100112#' @aliases clearAccessToken
Marc Kupietz4862b862019-11-07 10:13:53 +0100113#' @rdname KorAPConnection-class
114#' @import keyring
115#' @export
116#' @examples
117#' \dontrun{
118#' kco <- new("KorAPConnection")
Marc Kupietzb956b812019-11-25 17:53:13 +0100119#' clearAccessToken(kco)
Marc Kupietz4862b862019-11-07 10:13:53 +0100120#' }
121#'
Marc Kupietzb956b812019-11-25 17:53:13 +0100122setMethod("clearAccessToken", "KorAPConnection", function(kco) {
123 key_delete(accessTokenServiceName, kco@KorAPUrl)
Marc Kupietz4862b862019-11-07 10:13:53 +0100124})
125
126#' @import keyring
Marc Kupietzb956b812019-11-25 17:53:13 +0100127getAccessToken <- function(KorAPUrl) {
Marc Kupietz59e449b2019-12-12 12:53:54 +0100128 keyList <- tryCatch(withCallingHandlers(key_list(service = accessTokenServiceName),
Marc Kupietzddce5562019-11-24 15:45:38 +0100129 warning = function(w) invokeRestart("muffleWarning"),
Marc Kupietz59e449b2019-12-12 12:53:54 +0100130 error = function(e) return(NULL)),
131 error = function(e) { })
Marc Kupietzb2870f22019-11-20 22:28:34 +0100132 if (KorAPUrl %in% keyList)
Marc Kupietzb956b812019-11-25 17:53:13 +0100133 key_get(accessTokenServiceName, KorAPUrl)
Marc Kupietzfd9e7492019-11-08 15:45:18 +0100134 else
135 NULL
Marc Kupietz4862b862019-11-07 10:13:53 +0100136}
Marc Kupietz0a96b282019-10-01 11:05:31 +0200137
138KorAPCacheSubDir <- function() {
Marc Kupietz70b2c722020-02-18 13:32:09 +0100139 paste0("RKorAPClient_",
140 gsub(
141 "^([0-9]+\\.[0-9]+).*",
142 "\\1",
143 packageVersion("RKorAPClient"),
144 perl = TRUE
145 ))
Marc Kupietz0a96b282019-10-01 11:05:31 +0200146}
147
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200148setGeneric("apiCall", function(kco, ...) standardGeneric("apiCall") )
149
Marc Kupietz4de53ec2019-10-04 09:12:00 +0200150## quiets concerns of R CMD check re: the .'s that appear in pipelines
151if(getRversion() >= "2.15.1") utils::globalVariables(c("."))
152
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200153#' @aliases apiCall
154#' @rdname KorAPConnection-class
155#' @param kco KorAPConnection object
156#' @param url request url
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100157#' @param json logical that determines if json result is expected
Marc Kupietzb49afa02020-06-04 15:50:29 +0200158#' @param getHeaders logical that determines if headers and content should be returned (as a list)
Marc Kupietz69cc54a2019-09-30 12:06:54 +0200159#' @importFrom jsonlite fromJSON
160#' @export
Marc Kupietzb49afa02020-06-04 15:50:29 +0200161setMethod("apiCall", "KorAPConnection", function(kco, url, json = TRUE, getHeaders = FALSE, cache = kco@cache) {
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100162 result <- ""
163 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200164 result <- R.cache::loadCache(dir=KorAPCacheSubDir(), key=list(url, kco@accessToken, kco@indexRevision))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100165 if (!is.null(result)) {
166 if (!is.null(result$meta))
167 result$meta$cached <- "local"
168 return(result)
Marc Kupietz0a96b282019-10-01 11:05:31 +0200169 }
170 }
Marc Kupietzb956b812019-11-25 17:53:13 +0100171 if (!is.null(kco@accessToken))
172 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout), add_headers(Authorization = paste("Bearer", kco@accessToken)))
Marc Kupietz4862b862019-11-07 10:13:53 +0100173 else
174 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100175 if (json || status_code(resp) != 200) {
176 if (json && !http_type(resp) %in% c("application/json", "application/ld+json")) {
177 stop("API did not return json", call. = FALSE)
178 }
179 result <- jsonlite::fromJSON(content(resp, "text", encoding = "UTF-8"))
180 if (!is.null(result$warnings)) {
181 message <- if (nrow(result$warnings) > 1)
182 sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
183 else
184 sprintf("%s: %s", result$warnings[1], result$warnings[2])
185 warning(message, call. = FALSE)
186 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200187 }
188 if (status_code(resp) != 200) {
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100189 if (kco@verbose) {
190 cat("\n")
191 }
192 message <- sprintf("%s KorAP API request failed", status_code(resp))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100193 if (!is.null(result$errors)) {
194 message <- sprintf("%s - %s %s", message, result$errors[1], result$errors[2])
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100195 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200196 stop(message, call. = FALSE)
197 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100198 if (!json) {
199 result <- content(resp, "text", encoding = "UTF-8")
Marc Kupietz0a96b282019-10-01 11:05:31 +0200200 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100201 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200202 R.cache::saveCache(result, key = list(url, kco@accessToken, kco@indexRevision), dir = KorAPCacheSubDir(), compress = TRUE)
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100203 }
Marc Kupietzb49afa02020-06-04 15:50:29 +0200204 if (getHeaders) {
205 list(httr::headers(resp), result)
206 } else {
207 result
208 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200209})
210
Marc Kupietz0a96b282019-10-01 11:05:31 +0200211setGeneric("clearCache", function(kco) standardGeneric("clearCache") )
212
213#' @aliases clearCache
214#' @rdname KorAPConnection-class
215#' @export
216setMethod("clearCache", "KorAPConnection", function(kco) {
217 R.cache::clearCache(dir=KorAPCacheSubDir())
218})
219
Marc Kupietze95108e2019-09-18 13:23:58 +0200220#' @rdname KorAPConnection-class
221#' @param object KorAPConnection object
222#' @export
223setMethod("show", "KorAPConnection", function(object) {
224 cat("<KorAPConnection>", "\n")
225 cat("apiUrl: ", object@apiUrl, "\n")
226})
227
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200228##' Funtion KorAPConnection()
229##'
230##' Wrappper function for new("KorAPConnection")
231##'
232##' @rdname KorAPConnection-constructor
233##' @name KorAPConnection-constructor
234##' @export
235## XKorAPConnection <- function(...) new("KorAPConnection", ...)