diff --git a/DESCRIPTION b/DESCRIPTION index 631c223..7be0f01 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: impectR Title: Access Data from the 'Impect' API -Version: 2.5.1 +Version: 2.5.2 Authors@R: c( person("Impect", "GmbH", , "info.impect@impect.com", role = c("cph")), person("Florian", "Schmitt", , "florian.schmitt@impect.com", role = c("aut", "cre")), diff --git a/NEWS.md b/NEWS.md index cbd7e38..d5a1900 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,13 @@ +# impectR 2.5.2 + +## Minor Changes +* fix bugs in the following functions that occured if either the coaches endpoint returned no coaches or the coaches endpoint being blacklisted for the user: + * `getEvents()` + * `getPlayerMatchSums()` + * `getSquadMatchSums()` + * `getPlayerMatchScores()` + * `getSquadMatchScores()` + # impectR 2.5.1 ## Minor Changes diff --git a/R/getEvents.R b/R/getEvents.R index ac60b5f..5a91a78 100644 --- a/R/getEvents.R +++ b/R/getEvents.R @@ -262,35 +262,50 @@ getEvents <- function ( gsub("\\.(.)", "\\U\\1", base::names(squads), perl = TRUE) # get coach master data from API + coaches_blacklisted = FALSE coaches <- purrr::map_df( iterations, ~ { - response <- jsonlite::fromJSON( - httr::content( - .callAPIlimited( - host, - base_url = "/v5/customerapi/iterations/", - id = ., - suffix = "/coaches", - token = token - ), - "text", - encoding = "UTF-8" + response <- .callAPIlimited( + host, + base_url = "/v5/customerapi/iterations/", + id = ., + suffix = "/coaches", + token = token, + ignore_403 = TRUE ) - )$data - if (base::length(response) > 0) { - response <- response %>% - jsonlite::flatten() - } else { - response <- base::data.frame( - id = -1, - name = "", - stringsAsFactors = FALSE - ) + # check status + status <- httr::status_code(response) + + if (status == 403) { + coaches_blacklisted <<- TRUE + + # insert empty df as response + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } else { + response <- jsonlite::fromJSON( + httr::content(response, "text", encoding = "UTF-8") + )$data + + # flatten response + if (base::length(response) > 0) { + response <- response %>% + jsonlite::flatten() + } else { + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } + } } - } ) %>% dplyr::select(.data$id, .data$name) %>% base::unique() @@ -344,7 +359,7 @@ getEvents <- function ( by = base::c("currentAttackingSquadId" = "squadId") ) - # merge events with matchInfo & coaches + # merge events with matchInfo events <- events %>% dplyr::left_join( dplyr::select( @@ -354,24 +369,29 @@ getEvents <- function ( .data$awayCoachId ), by = base::c("matchId" = "matchId") - ) %>% - dplyr::left_join( - dplyr::select( - coaches, - homeCoachId = .data$id, - homeCoachName = .data$name - ), - by = base::c("homeCoachId" = "homeCoachId") - ) %>% - dplyr::left_join( - dplyr::select( - coaches, - awayCoachId = .data$id, - awayCoachName = .data$name - ), - by = base::c("awayCoachId" = "awayCoachId") ) + # merge events with coaches + if (coaches_blacklisted == FALSE) { + events <- events %>% + dplyr::left_join( + dplyr::select( + coaches, + homeCoachId = .data$id, + homeCoachName = .data$name + ), + by = base::c("homeCoachId" = "homeCoachId") + ) %>% + dplyr::left_join( + dplyr::select( + coaches, + awayCoachId = .data$id, + awayCoachName = .data$name + ), + by = base::c("awayCoachId" = "awayCoachId") + ) + } + # merge events with players events <- events %>% dplyr::left_join( @@ -632,6 +652,13 @@ getEvents <- function ( order <- base::c(order, kpis$name) } + # check if coaches are blacklisted + if (coaches_blacklisted) { + order <- order[!order %in% c( + "homeCoachId", "homeCoachName", "awayCoachId", "awayCoachName" + )] + } + # reorder data events <- events %>% dplyr::select(dplyr::all_of(order)) diff --git a/R/getPlayerMatchScores.R b/R/getPlayerMatchScores.R index 76064e5..404fbe1 100644 --- a/R/getPlayerMatchScores.R +++ b/R/getPlayerMatchScores.R @@ -238,49 +238,53 @@ getPlayerMatchScores <- function ( base::unique() # get coach master data from API - - # get coach master data from API + coaches_blacklisted = FALSE coaches <- purrr::map_df( iterations, ~ { - response <- jsonlite::fromJSON( - httr::content( - .callAPIlimited( - host, - base_url = "/v5/customerapi/iterations/", - id = ., - suffix = "/coaches", - token = token - ), - "text", - encoding = "UTF-8" - ) - )$data + response <- .callAPIlimited( + host, + base_url = "/v5/customerapi/iterations/", + id = ., + suffix = "/coaches", + token = token, + ignore_403 = TRUE + ) - if (base::length(response) > 0) { - response <- response %>% - jsonlite::flatten() - } else { + # check status + status <- httr::status_code(response) + + if (status == 403) { + coaches_blacklisted <<- TRUE + + # insert empty df as response response <- base::data.frame( id = -1, name = "", stringsAsFactors = FALSE ) + } else { + response <- jsonlite::fromJSON( + httr::content(response, "text", encoding = "UTF-8") + )$data + + # flatten response + if (base::length(response) > 0) { + response <- response %>% + jsonlite::flatten() + } else { + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } } } - ) - if (base::length(coaches) > 0) { - coaches <- coaches %>% - dplyr::select(.data$id, .data$name) %>% - base::unique() - } else { - coaches <- coaches %>% - dplyr::mutate( - id = NA, - name = NA - ) - } + ) %>% + dplyr::select(.data$id, .data$name) %>% + base::unique() # get kpi names from API score_list <- jsonlite::fromJSON( @@ -471,19 +475,24 @@ getPlayerMatchScores <- function ( ), by = base::c("matchId" = "matchId", "squadId" = "squadId") ) %>% - dplyr::left_join( - dplyr::select( - coaches, - coachId = .data$id, - coachName = .data$name - ), - by = base::c("coachId" = "coachId") - ) %>% # fix some column names dplyr::rename( dateTime = .data$scheduledDate ) + # merge events with coaches + if (coaches_blacklisted == FALSE) { + scores <- scores %>% + dplyr::left_join( + dplyr::select( + coaches, + coachId = .data$id, + coachName = .data$name + ), + by = base::c("coachId" = "coachId") + ) + } + # define column order order <- c( "matchId", @@ -519,6 +528,11 @@ getPlayerMatchScores <- function ( order[order == "positions"] <- "position" } + # check if coaches are blacklisted + if (coaches_blacklisted) { + order <- order[!order %in% c("coachId", "coachName")] + } + # select columns scores <- scores %>% dplyr::select(dplyr::all_of(order)) diff --git a/R/getPlayerMatchsums.R b/R/getPlayerMatchsums.R index a141558..0b76be6 100644 --- a/R/getPlayerMatchsums.R +++ b/R/getPlayerMatchsums.R @@ -173,33 +173,48 @@ getPlayerMatchsums <- function ( base::unique() # get coach master data from API + coaches_blacklisted = FALSE coaches <- purrr::map_df( iterations, ~ { - response <- jsonlite::fromJSON( - httr::content( - .callAPIlimited( - host, - base_url = "/v5/customerapi/iterations/", - id = ., - suffix = "/coaches", - token = token - ), - "text", - encoding = "UTF-8" - ) - )$data + response <- .callAPIlimited( + host, + base_url = "/v5/customerapi/iterations/", + id = ., + suffix = "/coaches", + token = token, + ignore_403 = TRUE + ) - if (base::length(response) > 0) { - response <- response %>% - jsonlite::flatten() - } else { + # check status + status <- httr::status_code(response) + + if (status == 403) { + coaches_blacklisted <<- TRUE + + # insert empty df as response response <- base::data.frame( id = -1, name = "", stringsAsFactors = FALSE ) + } else { + response <- jsonlite::fromJSON( + httr::content(response, "text", encoding = "UTF-8") + )$data + + # flatten response + if (base::length(response) > 0) { + response <- response %>% + jsonlite::flatten() + } else { + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } } } ) %>% @@ -331,19 +346,24 @@ getPlayerMatchsums <- function ( ), by = base::c("matchId" = "matchId", "squadId" = "squadId") ) %>% - dplyr::left_join( - dplyr::select( - coaches, - coachId = .data$id, - coachName = .data$name - ), - by = base::c("coachId" = "coachId") - ) %>% # fix some column names dplyr::rename( dateTime = .data$scheduledDate ) + # merge events with coaches + if (coaches_blacklisted == FALSE) { + matchsums <- matchsums %>% + dplyr::left_join( + dplyr::select( + coaches, + coachId = .data$id, + coachName = .data$name + ), + by = base::c("coachId" = "coachId") + ) + } + # define column order order <- c( "matchId", @@ -375,6 +395,11 @@ getPlayerMatchsums <- function ( kpis$name ) + # check if coaches are blacklisted + if (coaches_blacklisted) { + order <- order[!order %in% c("coachId", "coachName")] + } + # select columns matchsums <- matchsums %>% dplyr::select(dplyr::all_of(order)) diff --git a/R/getSquadMatchScores.R b/R/getSquadMatchScores.R index 422a50b..a01b1b8 100644 --- a/R/getSquadMatchScores.R +++ b/R/getSquadMatchScores.R @@ -147,33 +147,48 @@ getSquadMatchScores <- function ( squads <- .cleanData(squads) # get coach master data from API + coaches_blacklisted = FALSE coaches <- purrr::map_df( iterations, ~ { - response <- jsonlite::fromJSON( - httr::content( - .callAPIlimited( - host, - base_url = "/v5/customerapi/iterations/", - id = ., - suffix = "/coaches", - token = token - ), - "text", - encoding = "UTF-8" - ) - )$data + response <- .callAPIlimited( + host, + base_url = "/v5/customerapi/iterations/", + id = ., + suffix = "/coaches", + token = token, + ignore_403 = TRUE + ) - if (base::length(response) > 0) { - response <- response %>% - jsonlite::flatten() - } else { + # check status + status <- httr::status_code(response) + + if (status == 403) { + coaches_blacklisted <<- TRUE + + # insert empty df as response response <- base::data.frame( id = -1, name = "", stringsAsFactors = FALSE ) + } else { + response <- jsonlite::fromJSON( + httr::content(response, "text", encoding = "UTF-8") + )$data + + # flatten response + if (base::length(response) > 0) { + response <- response %>% + jsonlite::flatten() + } else { + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } } } ) %>% @@ -297,19 +312,24 @@ getSquadMatchScores <- function ( ), by = base::c("matchId" = "matchId", "squadId" = "squadId") ) %>% - dplyr::left_join( - dplyr::select( - coaches, - coachId = .data$id, - coachName = .data$name - ), - by = base::c("coachId" = "coachId") - ) %>% # fix some column names dplyr::rename( dateTime = .data$scheduledDate ) + # merge events with coaches + if (coaches_blacklisted == FALSE) { + scores <- scores %>% + dplyr::left_join( + dplyr::select( + coaches, + coachId = .data$id, + coachName = .data$name + ), + by = base::c("coachId" = "coachId") + ) + } + # define column order order <- c( "matchId", @@ -331,6 +351,11 @@ getSquadMatchScores <- function ( scores_list$name ) + # check if coaches are blacklisted + if (coaches_blacklisted) { + order <- order[!order %in% c("coachId", "coachName")] + } + # select columns scores <- scores %>% dplyr::select(dplyr::all_of(order)) diff --git a/R/getSquadMatchsums.R b/R/getSquadMatchsums.R index 8d924fb..2368d99 100644 --- a/R/getSquadMatchsums.R +++ b/R/getSquadMatchsums.R @@ -147,33 +147,48 @@ getSquadMatchsums <- function ( squads <- .cleanData(squads) # get coach master data from API + coaches_blacklisted = FALSE coaches <- purrr::map_df( iterations, ~ { - response <- jsonlite::fromJSON( - httr::content( - .callAPIlimited( - host, - base_url = "/v5/customerapi/iterations/", - id = ., - suffix = "/coaches", - token = token - ), - "text", - encoding = "UTF-8" - ) - )$data + response <- .callAPIlimited( + host, + base_url = "/v5/customerapi/iterations/", + id = ., + suffix = "/coaches", + token = token, + ignore_403 = TRUE + ) - if (base::length(response) > 0) { - response <- response %>% - jsonlite::flatten() - } else { + # check status + status <- httr::status_code(response) + + if (status == 403) { + coaches_blacklisted <<- TRUE + + # insert empty df as response response <- base::data.frame( id = -1, name = "", stringsAsFactors = FALSE ) + } else { + response <- jsonlite::fromJSON( + httr::content(response, "text", encoding = "UTF-8") + )$data + + # flatten response + if (base::length(response) > 0) { + response <- response %>% + jsonlite::flatten() + } else { + response <- base::data.frame( + id = -1, + name = "", + stringsAsFactors = FALSE + ) + } } } ) %>% @@ -297,19 +312,24 @@ getSquadMatchsums <- function ( ), by = base::c("matchId" = "matchId", "squadId" = "squadId") ) %>% - dplyr::left_join( - dplyr::select( - coaches, - coachId = .data$id, - coachName = .data$name - ), - by = base::c("coachId" = "coachId") - ) %>% # fix some column names dplyr::rename( dateTime = .data$scheduledDate, ) + # merge events with coaches + if (coaches_blacklisted == FALSE) { + matchsums <- matchsums %>% + dplyr::left_join( + dplyr::select( + coaches, + coachId = .data$id, + coachName = .data$name + ), + by = base::c("coachId" = "coachId") + ) + } + # define column order order <- c( "matchId", @@ -331,6 +351,11 @@ getSquadMatchsums <- function ( kpis$name ) + # check if coaches are blacklisted + if (coaches_blacklisted) { + order <- order[!order %in% c("coachId", "coachName")] + } + # select columns matchsums <- matchsums %>% dplyr::select(dplyr::all_of(order)) diff --git a/R/utils.R b/R/utils.R index bdb853c..a61adf2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -12,7 +12,7 @@ #' #' @return Response content of the API endpoint .callAPI <- function(host, base_url, id = "", suffix = "", token, - max_retries = 3, retry_delay = 1) { + max_retries = 3, retry_delay = 1, ignore_403 = FALSE) { # try API call for (i in 1:max_retries) { @@ -33,14 +33,26 @@ base::message(base::paste("Received status code 429 (", message, "), retrying in", retry_delay, "seconds...\n")) Sys.sleep(retry_delay) - } else if (httr::status_code(response) %in% c(401, 403)) { - # handle unauthorized or forbidden (401 or 403 status codes) + } else if (httr::status_code(response) == 401) { + # handle unauthorized (401 status codes) message <- dplyr::coalesce( httr::content(response, "parsed")$message, "Unauthorized" ) stop(base::paste("Received status code", httr::status_code(response), - "(", message, ")")) + "(", message, ")")) + } else if (httr::status_code(response) == 403) { + if (ignore_403 == FALSE) { + # handle forbidden (403 status codes) + message <- dplyr::coalesce( + httr::content(response, "parsed")$message, + "No access to requested resource" + ) + stop(base::paste("Received status code", httr::status_code(response), + "(", message, ")")) + } else { + return(response) + } } else { # handle other errors message <- dplyr::coalesce( @@ -67,13 +79,15 @@ #' @param token bearer token #' #' @return a dataframe containing the response of an API endpoint -.callAPIlimited <- function(host, base_url, id = "", suffix = "", token) { +.callAPIlimited <- function(host, base_url, id = "", suffix = "", + token, ignore_403 = FALSE) { # check if Token bucket exist and create it if not if (is.null(.api_state$bucket)) { # get response from API - response <- .callAPI(host, base_url, id, suffix, token) + response <- .callAPI(host, base_url, id, suffix, token, + ignore_403 = ignore_403) # get rate limit policy policy <- response[["all_headers"]][[1]][["headers"]][["ratelimit-policy"]] @@ -102,7 +116,8 @@ # check if a token is available if (.api_state$bucket$isTokenAvailable()) { # get API response - response <- .callAPI(host, base_url, id, suffix, token) + response <- .callAPI(host, base_url, id, suffix, token, + ignore_403 = ignore_403) # consume a token .api_state$bucket$consumeToken() @@ -111,7 +126,8 @@ Sys.sleep(.api_state$bucket$intervall) # call function again - response <- .callAPIlimited(host, base_url, id, suffix, token) + response <- .callAPIlimited(host, base_url, id, suffix, token, + ignore_403 = ignore_403) } # return response diff --git a/README.Rmd b/README.Rmd index 2a97244..a825039 100644 --- a/README.Rmd +++ b/README.Rmd @@ -21,9 +21,9 @@ knitr::opts_chunk$set( A package provided by: Impect GmbH -Version: v2.5.1 +Version: v2.5.2 -**Updated: October 15th 2025** +**Updated: October 17th 2025** --- @@ -56,7 +56,7 @@ You can also install it from [GitHub](https://github.com/) with: ```r # install.packages("devtools") -devtools::install_github("ImpectAPI/impectR@v2.5.1") +devtools::install_github("ImpectAPI/impectR@v2.5.2") ``` ## Usage diff --git a/README.md b/README.md index 360fa12..fb2ac15 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ A package provided by: Impect GmbH -Version: v2.5.1 +Version: v2.5.2 -**Updated: October 15th 2025** +**Updated: October 17th 2025** ------------------------------------------------------------------------ @@ -44,7 +44,7 @@ You can also install it from [GitHub](https://github.com/) with: ``` r # install.packages("devtools") -devtools::install_github("ImpectAPI/impectR@v2.5.1") +devtools::install_github("ImpectAPI/impectR@v2.5.2") ``` ## Usage