blob: 243e1793df149cfdaa69378d4eb890aa26c2983e [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 Kupietz6ae76052021-09-21 10:34:00 +020047#' \dontrun{
48#'
Marc Kupietz5a519822019-09-20 21:43:52 +020049#' kcon <- new("KorAPConnection", verbose = TRUE)
Marc Kupietze95108e2019-09-18 13:23:58 +020050#' kq <- corpusQuery(kcon, "Ameisenplage")
Marc Kupietz5a519822019-09-20 21:43:52 +020051#' kq <- fetchAll(kq)
Marc Kupietz05b22772020-02-18 21:58:42 +010052#' }
Marc Kupietz7915dc42019-09-12 17:44:58 +020053#'
Marc Kupietz4862b862019-11-07 10:13:53 +010054#' \dontrun{
Marc Kupietz6ae76052021-09-21 10:34:00 +020055#'
Marc Kupietzb956b812019-11-25 17:53:13 +010056#' kcon <- new("KorAPConnection", verbose = TRUE, accessToken="e739u6eOzkwADQPdVChxFg")
Marc Kupietz4862b862019-11-07 10:13:53 +010057#' kq <- corpusQuery(kcon, "Ameisenplage", metadataOnly=FALSE)
58#' kq <- fetchAll(kq)
59#' kq@collectedMatches$snippet
60#' }
Marc Kupietz7915dc42019-09-12 17:44:58 +020061#'
Marc Kupietze95108e2019-09-18 13:23:58 +020062#' @rdname KorAPConnection-class
Marc Kupietz632cbd42019-09-06 16:04:51 +020063#' @export
Marc Kupietze95108e2019-09-18 13:23:58 +020064setMethod("initialize", "KorAPConnection",
Marc Kupietz8a82af72019-12-12 12:58:22 +010065 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 +020066 .Object <- callNextMethod()
67 m <- regexpr("https?://[^?]+", KorAPUrl, perl = TRUE)
68 .Object@KorAPUrl <- regmatches(KorAPUrl, m)
69 if (!endsWith(.Object@KorAPUrl, '/')) {
70 .Object@KorAPUrl <- paste0(.Object@KorAPUrl, "/")
71 }
72 if (missing(apiUrl)) {
73 .Object@apiUrl = paste0(.Object@KorAPUrl, 'api/', apiVersion, '/')
74 } else {
75 .Object@apiUrl = apiUrl
76 }
Marc Kupietzb956b812019-11-25 17:53:13 +010077 .Object@accessToken = accessToken
Marc Kupietze95108e2019-09-18 13:23:58 +020078 .Object@apiVersion = apiVersion
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +020079 .Object@userAgent = userAgent
80 .Object@timeout = timeout
Marc Kupietz5a519822019-09-20 21:43:52 +020081 .Object@verbose = verbose
Marc Kupietz0a96b282019-10-01 11:05:31 +020082 .Object@cache = cache
Marc Kupietzb49afa02020-06-04 15:50:29 +020083 welcome <- apiCall(.Object, .Object@apiUrl, json = FALSE, cache = FALSE, getHeaders = TRUE)
84 message(welcome[[2]])
85 .Object@indexRevision <- welcome[[1]][["x-index-revision"]]
Marc Kupietze95108e2019-09-18 13:23:58 +020086 .Object
87 })
88
Marc Kupietza96537f2019-11-09 23:07:44 +010089
Marc Kupietzb956b812019-11-25 17:53:13 +010090accessTokenServiceName <- "RKorAPClientAccessToken"
Marc Kupietz4862b862019-11-07 10:13:53 +010091
Marc Kupietzb956b812019-11-25 17:53:13 +010092setGeneric("persistAccessToken", function(kco, ...) standardGeneric("persistAccessToken") )
Marc Kupietz4862b862019-11-07 10:13:53 +010093
Marc Kupietzb956b812019-11-25 17:53:13 +010094#' @aliases persistAccessToken
Marc Kupietz4862b862019-11-07 10:13:53 +010095#' @rdname KorAPConnection-class
96#' @import keyring
97#' @export
98#' @examples
99#' \dontrun{
Marc Kupietz6ae76052021-09-21 10:34:00 +0200100#'
Marc Kupietzb956b812019-11-25 17:53:13 +0100101#' kco <- new("KorAPConnection", accessToken="e739u6eOzkwADQPdVChxFg")
102#' persistAccessToken(kco)
Marc Kupietz4862b862019-11-07 10:13:53 +0100103#' }
104#'
Marc Kupietzb956b812019-11-25 17:53:13 +0100105setMethod("persistAccessToken", "KorAPConnection", function(kco, accessToken = kco@accessToken) {
106 if (is.null(accessToken))
107 stop("It seems that you have not supplied any access token that could be persisted.", call. = FALSE)
Marc Kupietz4862b862019-11-07 10:13:53 +0100108
Marc Kupietzb956b812019-11-25 17:53:13 +0100109 kco@accessToken <- accessToken
110 key_set_with_value(accessTokenServiceName, kco@KorAPUrl, accessToken)
Marc Kupietz4862b862019-11-07 10:13:53 +0100111})
112
Marc Kupietzb956b812019-11-25 17:53:13 +0100113setGeneric("clearAccessToken", function(kco) standardGeneric("clearAccessToken") )
Marc Kupietz4862b862019-11-07 10:13:53 +0100114
Marc Kupietzb956b812019-11-25 17:53:13 +0100115#' @aliases clearAccessToken
Marc Kupietz4862b862019-11-07 10:13:53 +0100116#' @rdname KorAPConnection-class
117#' @import keyring
118#' @export
119#' @examples
120#' \dontrun{
Marc Kupietz6ae76052021-09-21 10:34:00 +0200121#'
Marc Kupietz4862b862019-11-07 10:13:53 +0100122#' kco <- new("KorAPConnection")
Marc Kupietzb956b812019-11-25 17:53:13 +0100123#' clearAccessToken(kco)
Marc Kupietz4862b862019-11-07 10:13:53 +0100124#' }
125#'
Marc Kupietzb956b812019-11-25 17:53:13 +0100126setMethod("clearAccessToken", "KorAPConnection", function(kco) {
127 key_delete(accessTokenServiceName, kco@KorAPUrl)
Marc Kupietz4862b862019-11-07 10:13:53 +0100128})
129
130#' @import keyring
Marc Kupietzb956b812019-11-25 17:53:13 +0100131getAccessToken <- function(KorAPUrl) {
Marc Kupietz59e449b2019-12-12 12:53:54 +0100132 keyList <- tryCatch(withCallingHandlers(key_list(service = accessTokenServiceName),
Marc Kupietzddce5562019-11-24 15:45:38 +0100133 warning = function(w) invokeRestart("muffleWarning"),
Marc Kupietz59e449b2019-12-12 12:53:54 +0100134 error = function(e) return(NULL)),
135 error = function(e) { })
Marc Kupietz01c24772021-07-14 18:27:36 +0200136 if (KorAPUrl %in% keyList$username)
Marc Kupietzb956b812019-11-25 17:53:13 +0100137 key_get(accessTokenServiceName, KorAPUrl)
Marc Kupietzfd9e7492019-11-08 15:45:18 +0100138 else
139 NULL
Marc Kupietz4862b862019-11-07 10:13:53 +0100140}
Marc Kupietz0a96b282019-10-01 11:05:31 +0200141
Marc Kupietz581a29b2021-09-04 20:51:04 +0200142
143warnIfNoAccessToken <- function(kco) {
144 if (is.null(kco@accessToken)) {
145 warning(
146 paste0(
147 "In order to receive KWICSs also from corpora with restricted licenses, you need an access token.\n",
148 "To generate an access token, login to KorAP and navigite to KorAP's OAuth settings <",
149 kco@KorAPUrl,
150 "settings/oauth#page-top>"
151 )
152 )
153 }
154}
155
Marc Kupietz0a96b282019-10-01 11:05:31 +0200156KorAPCacheSubDir <- function() {
Marc Kupietz70b2c722020-02-18 13:32:09 +0100157 paste0("RKorAPClient_",
158 gsub(
159 "^([0-9]+\\.[0-9]+).*",
160 "\\1",
161 packageVersion("RKorAPClient"),
162 perl = TRUE
163 ))
Marc Kupietz0a96b282019-10-01 11:05:31 +0200164}
165
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200166setGeneric("apiCall", function(kco, ...) standardGeneric("apiCall") )
167
Marc Kupietz4de53ec2019-10-04 09:12:00 +0200168## quiets concerns of R CMD check re: the .'s that appear in pipelines
169if(getRversion() >= "2.15.1") utils::globalVariables(c("."))
170
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200171#' @aliases apiCall
172#' @rdname KorAPConnection-class
173#' @param kco KorAPConnection object
174#' @param url request url
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100175#' @param json logical that determines if json result is expected
Marc Kupietzb49afa02020-06-04 15:50:29 +0200176#' @param getHeaders logical that determines if headers and content should be returned (as a list)
Marc Kupietz69cc54a2019-09-30 12:06:54 +0200177#' @importFrom jsonlite fromJSON
178#' @export
Marc Kupietzb49afa02020-06-04 15:50:29 +0200179setMethod("apiCall", "KorAPConnection", function(kco, url, json = TRUE, getHeaders = FALSE, cache = kco@cache) {
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100180 result <- ""
181 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200182 result <- R.cache::loadCache(dir=KorAPCacheSubDir(), key=list(url, kco@accessToken, kco@indexRevision))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100183 if (!is.null(result)) {
184 if (!is.null(result$meta))
185 result$meta$cached <- "local"
186 return(result)
Marc Kupietz0a96b282019-10-01 11:05:31 +0200187 }
188 }
Marc Kupietzb956b812019-11-25 17:53:13 +0100189 if (!is.null(kco@accessToken))
190 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout), add_headers(Authorization = paste("Bearer", kco@accessToken)))
Marc Kupietz4862b862019-11-07 10:13:53 +0100191 else
192 resp <- GET(url, user_agent(kco@userAgent), timeout(kco@timeout))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100193 if (json || status_code(resp) != 200) {
194 if (json && !http_type(resp) %in% c("application/json", "application/ld+json")) {
195 stop("API did not return json", call. = FALSE)
196 }
197 result <- jsonlite::fromJSON(content(resp, "text", encoding = "UTF-8"))
198 if (!is.null(result$warnings)) {
199 message <- if (nrow(result$warnings) > 1)
200 sapply(result$warnings, function(warning) paste(sprintf("%s: %s", warning[1], warning[2]), sep="\n"))
201 else
202 sprintf("%s: %s", result$warnings[1], result$warnings[2])
203 warning(message, call. = FALSE)
204 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200205 }
206 if (status_code(resp) != 200) {
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100207 if (kco@verbose) {
208 cat("\n")
209 }
210 message <- sprintf("%s KorAP API request failed", status_code(resp))
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100211 if (!is.null(result$errors)) {
212 message <- sprintf("%s - %s %s", message, result$errors[1], result$errors[2])
Marc Kupietzb7d8c272020-01-31 18:51:50 +0100213 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200214 stop(message, call. = FALSE)
215 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100216 if (!json) {
217 result <- content(resp, "text", encoding = "UTF-8")
Marc Kupietz0a96b282019-10-01 11:05:31 +0200218 }
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100219 if (cache) {
Marc Kupietzb49afa02020-06-04 15:50:29 +0200220 R.cache::saveCache(result, key = list(url, kco@accessToken, kco@indexRevision), dir = KorAPCacheSubDir(), compress = TRUE)
Marc Kupietzb2b32a32020-03-24 13:56:50 +0100221 }
Marc Kupietzb49afa02020-06-04 15:50:29 +0200222 if (getHeaders) {
223 list(httr::headers(resp), result)
224 } else {
225 result
226 }
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200227})
228
Marc Kupietz0a96b282019-10-01 11:05:31 +0200229setGeneric("clearCache", function(kco) standardGeneric("clearCache") )
230
231#' @aliases clearCache
232#' @rdname KorAPConnection-class
233#' @export
234setMethod("clearCache", "KorAPConnection", function(kco) {
235 R.cache::clearCache(dir=KorAPCacheSubDir())
236})
237
Marc Kupietze95108e2019-09-18 13:23:58 +0200238#' @rdname KorAPConnection-class
239#' @param object KorAPConnection object
240#' @export
241setMethod("show", "KorAPConnection", function(object) {
242 cat("<KorAPConnection>", "\n")
243 cat("apiUrl: ", object@apiUrl, "\n")
244})
245
Marc Kupietzd0d3e9b2019-09-24 17:36:03 +0200246##' Funtion KorAPConnection()
247##'
248##' Wrappper function for new("KorAPConnection")
249##'
250##' @rdname KorAPConnection-constructor
251##' @name KorAPConnection-constructor
252##' @export
253## XKorAPConnection <- function(...) new("KorAPConnection", ...)