blob: 0919d4e259537ae07301917a83fa48e6d3b4b3f7 [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#'
Marc Kupietz67edcb52021-09-20 21:54:24 +02008#' `KorAPConnection` objects represent the connection to a KorAP server.
9#' New `KorAPConnection` objects can be created by `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 Kupietz67edcb52021-09-20 21:54:24 +020023#' `kco <- new("KorAPConnection", accessToken="<access token>")`.
Marc Kupietz4862b862019-11-07 10:13:53 +010024#' In order to make the API
Marc Kupietz67edcb52021-09-20 21:54:24 +020025#' token persistent for the currently used `KorAPUrl` (you can have one
Marc Kupietz4862b862019-11-07 10:13:53 +010026#' token per KorAPUrl / KorAP server instance), use
Marc Kupietz67edcb52021-09-20 21:54:24 +020027#' `persistAccessToken(kco)`. This will store it in your keyring using the
28#' [keyring()] package. Subsequent new("KorAPConnection") calls will
Marc Kupietz4862b862019-11-07 10:13:53 +010029#' then automatically retrieve the token from your keying. To stop using a
Marc Kupietz67edcb52021-09-20 21:54:24 +020030#' persisted token, call `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
Marc Kupietz67edcb52021-09-20 21:54:24 +020034#' you experience problems or unexpected results, please try `kco <-
35#' new("KorAPConnection", cache=FALSE)` or use
36#' [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
Marc Kupietz67edcb52021-09-20 21:54:24 +020042#' the cache with [clearCache()].
43#' @return [KorAPConnection()] object that can be used e.g. with
44#' [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 Kupietz01c24772021-07-14 18:27:36 +0200132 if (KorAPUrl %in% keyList$username)
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
Marc Kupietz581a29b2021-09-04 20:51:04 +0200138
139warnIfNoAccessToken <- function(kco) {
140 if (is.null(kco@accessToken)) {
141 warning(
142 paste0(
143 "In order to receive KWICSs also from corpora with restricted licenses, you need an access token.\n",
144 "To generate an access token, login to KorAP and navigite to KorAP's OAuth settings <",
145 kco@KorAPUrl,
146 "settings/oauth#page-top>"
147 )
148 )
149 }
150}
151
Marc Kupietz0a96b282019-10-01 11:05:31 +0200152KorAPCacheSubDir <- function() {
Marc Kupietz70b2c722020-02-18 13:32:09 +0100153 paste0("RKorAPClient_",
154 gsub(
155 "^([0-9]+\\.[0-9]+).*",
156 "\\1",
157 packageVersion("RKorAPClient"),
158 perl = TRUE
159 ))
Marc Kupietz0a96b282019-10-01 11:05:31 +0200160}
161
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200162setGeneric("apiCall", function(kco, ...) standardGeneric("apiCall") )
163
Marc Kupietz4de53ec2019-10-04 09:12:00 +0200164## quiets concerns of R CMD check re: the .'s that appear in pipelines
165if(getRversion() >= "2.15.1") utils::globalVariables(c("."))
166
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200167#' @aliases apiCall
168#' @rdname KorAPConnection-class
169#' @param kco KorAPConnection object
170#' @param url request url
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100171#' @param json logical that determines if json result is expected
Marc Kupietzb49afa02020-06-04 15:50:29 +0200172#' @param getHeaders logical that determines if headers and content should be returned (as a list)
Marc Kupietz69cc54a2019-09-30 12:06:54 +0200173#' @importFrom jsonlite fromJSON
174#' @export
Marc Kupietzb49afa02020-06-04 15:50:29 +0200175setMethod("apiCall", "KorAPConnection", function(kco, url, json = TRUE, getHeaders = FALSE, cache = kco@cache) {
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100176 result <- ""
177 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200178 result <- R.cache::loadCache(dir=KorAPCacheSubDir(), key=list(url, kco@accessToken, kco@indexRevision))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100179 if (!is.null(result)) {
180 if (!is.null(result$meta))
181 result$meta$cached <- "local"
182 return(result)
Marc Kupietz0a96b282019-10-01 11:05:31 +0200183 }
184 }
Marc Kupietzb956b812019-11-25 17:53:13 +0100185 if (!is.null(kco@accessToken))
186 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout), add_headers(Authorization = paste("Bearer", kco@accessToken)))
Marc Kupietz4862b862019-11-07 10:13:53 +0100187 else
188 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100189 if (json || status_code(resp) != 200) {
190 if (json && !http_type(resp) %in% c("application/json", "application/ld+json")) {
191 stop("API did not return json", call. = FALSE)
192 }
193 result <- jsonlite::fromJSON(content(resp, "text", encoding = "UTF-8"))
194 if (!is.null(result$warnings)) {
195 message <- if (nrow(result$warnings) > 1)
196 sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
197 else
198 sprintf("%s: %s", result$warnings[1], result$warnings[2])
199 warning(message, call. = FALSE)
200 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200201 }
202 if (status_code(resp) != 200) {
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100203 if (kco@verbose) {
204 cat("\n")
205 }
206 message <- sprintf("%s KorAP API request failed", status_code(resp))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100207 if (!is.null(result$errors)) {
208 message <- sprintf("%s - %s %s", message, result$errors[1], result$errors[2])
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100209 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200210 stop(message, call. = FALSE)
211 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100212 if (!json) {
213 result <- content(resp, "text", encoding = "UTF-8")
Marc Kupietz0a96b282019-10-01 11:05:31 +0200214 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100215 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200216 R.cache::saveCache(result, key = list(url, kco@accessToken, kco@indexRevision), dir = KorAPCacheSubDir(), compress = TRUE)
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100217 }
Marc Kupietzb49afa02020-06-04 15:50:29 +0200218 if (getHeaders) {
219 list(httr::headers(resp), result)
220 } else {
221 result
222 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200223})
224
Marc Kupietz0a96b282019-10-01 11:05:31 +0200225setGeneric("clearCache", function(kco) standardGeneric("clearCache") )
226
227#' @aliases clearCache
228#' @rdname KorAPConnection-class
229#' @export
230setMethod("clearCache", "KorAPConnection", function(kco) {
231 R.cache::clearCache(dir=KorAPCacheSubDir())
232})
233
Marc Kupietze95108e2019-09-18 13:23:58 +0200234#' @rdname KorAPConnection-class
235#' @param object KorAPConnection object
236#' @export
237setMethod("show", "KorAPConnection", function(object) {
238 cat("<KorAPConnection>", "\n")
239 cat("apiUrl: ", object@apiUrl, "\n")
240})
241
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200242##' Funtion KorAPConnection()
243##'
244##' Wrappper function for new("KorAPConnection")
245##'
246##' @rdname KorAPConnection-constructor
247##' @name KorAPConnection-constructor
248##' @export
249## XKorAPConnection <- function(...) new("KorAPConnection", ...)