From 4677b76157298d67978afa18c295dce14f00b29d Mon Sep 17 00:00:00 2001 From: Jonathan Keane Date: Wed, 4 Mar 2026 16:47:09 -0600 Subject: [PATCH 1/3] Accept integers or characters for hit id --- R/parse.R | 4 ++++ R/ptype.R | 2 +- tests/testthat/test-get.R | 5 +++-- tests/testthat/test-parse.R | 43 +++++++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/R/parse.R b/R/parse.R index c917f2da..1af811d3 100644 --- a/R/parse.R +++ b/R/parse.R @@ -67,6 +67,10 @@ ensure_column <- function(data, default, name) { col <- bit64::as.integer64(col) } + if (is.character(default) && is.integer(col)) { + col <- as.character(col) + } + if (inherits(default, "list") && !inherits(col, "list")) { col <- list(col) } diff --git a/R/ptype.R b/R/ptype.R index 6e274f1e..ccc8f91a 100644 --- a/R/ptype.R +++ b/R/ptype.R @@ -41,7 +41,7 @@ connectapi_ptypes <- list( "data_version" = NA_integer_ ), usage = tibble::tibble( - "id" = NA_integer_, + "id" = NA_character_, "user_guid" = NA_character_, "content_guid" = NA_character_, "timestamp" = NA_datetime_, diff --git a/tests/testthat/test-get.R b/tests/testthat/test-get.R index b73e82a4..bbb5c6d1 100644 --- a/tests/testthat/test-get.R +++ b/tests/testthat/test-get.R @@ -388,7 +388,8 @@ with_mock_dir("2025.04.0", { expect_length(usage, 5) - # Check first element + # Check first element (raw list, before conversion to data.frame). + # The id is numeric in the JSON, so it stays numeric in the raw list. expect_equal( usage[[1]], list( @@ -408,7 +409,7 @@ with_mock_dir("2025.04.0", { expect_equal( usage_df, data.frame( - id = c(8966707L, 8966708L, 8967206L, 8967210L, 8966214L), + id = c("8966707", "8966708", "8967206", "8967210", "8966214"), user_guid = c(NA, NA, NA, NA, "fecbd383"), content_guid = c( "475618c9", diff --git a/tests/testthat/test-parse.R b/tests/testthat/test-parse.R index 8b3cc347..ec444200 100644 --- a/tests/testthat/test-parse.R +++ b/tests/testthat/test-parse.R @@ -344,3 +344,46 @@ test_that("parse_connectapi handles mixed null/non-null integer timestamps", { expect_type(result$end_time, "double") expect_identical(result$end_time, c(NA_real_, 1732556770)) }) + +test_that("ensure_column coerces integer to character when ptype expects character", { + # Old Connect returns integer IDs + int_data <- tibble::tibble(id = c(100L, 200L, 300L)) + result <- ensure_column(int_data, NA_character_, "id") + expect_type(result$id, "character") + expect_identical(result$id, c("100", "200", "300")) + + # New Connect returns character IDs + + char_data <- tibble::tibble(id = c("100", "200", "300")) + result <- ensure_column(char_data, NA_character_, "id") + expect_type(result$id, "character") + expect_identical(result$id, c("100", "200", "300")) +}) + +test_that("parse_connectapi_typed produces character id for usage with integer input", { + # Simulates old Connect returning integer IDs + int_hits <- list( + list(id = 123L, user_guid = "abc", content_guid = "def", + timestamp = "2025-04-30T12:00:00Z", data = list(path = "/test")), + list(id = 456L, user_guid = "ghi", content_guid = "jkl", + timestamp = "2025-04-30T13:00:00Z", data = list(path = "/other")) + ) + + result <- parse_connectapi_typed(int_hits, connectapi_ptypes$usage) + expect_type(result$id, "character") + expect_identical(result$id, c("123", "456")) +}) + +test_that("parse_connectapi_typed produces character id for usage with character input", { + # Simulates new Connect returning character IDs + char_hits <- list( + list(id = "123", user_guid = "abc", content_guid = "def", + timestamp = "2025-04-30T12:00:00Z", data = list(path = "/test")), + list(id = "456", user_guid = "ghi", content_guid = "jkl", + timestamp = "2025-04-30T13:00:00Z", data = list(path = "/other")) + ) + + result <- parse_connectapi_typed(char_hits, connectapi_ptypes$usage) + expect_type(result$id, "character") + expect_identical(result$id, c("123", "456")) +}) From 96eb4f1d3452225441e470d0983ffb3a67e715af Mon Sep 17 00:00:00 2001 From: Jonathan Keane Date: Wed, 4 Mar 2026 16:50:09 -0600 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=97=9E=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NEWS.md | 2 ++ R/get.R | 2 +- man/get_usage.Rd | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index bfe01699..8230c92f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # connectapi (development version) +- `get_usage()` now returns the id column as a character to match other parts of the API. + # connectapi 0.11.0 - `get_usage()` now allows for filtering by content GUID with the `content_guid` diff --git a/R/get.R b/R/get.R index 14ee3d31..09998502 100644 --- a/R/get.R +++ b/R/get.R @@ -550,7 +550,7 @@ get_usage_static <- function( #' @return A list of usage records. Each record is a list with all elements #' as character strings unless otherwise specified. #' -#' * `id`: An integer identifier for the hit. +#' * `id`: A string identifier for the hit. #' * `user_guid`: The user GUID if the visitor is logged-in, `NULL` for #' anonymous hits. #' * `content_guid`: The GUID of the visited content. diff --git a/man/get_usage.Rd b/man/get_usage.Rd index 4021dcae..04c196aa 100644 --- a/man/get_usage.Rd +++ b/man/get_usage.Rd @@ -25,7 +25,7 @@ the most recent are returned.} A list of usage records. Each record is a list with all elements as character strings unless otherwise specified. \itemize{ -\item \code{id}: An integer identifier for the hit. +\item \code{id}: A string identifier for the hit. \item \code{user_guid}: The user GUID if the visitor is logged-in, \code{NULL} for anonymous hits. \item \code{content_guid}: The GUID of the visited content. From 211f6ef0482410cae51cfbfa2d4c3c6c309fefc1 Mon Sep 17 00:00:00 2001 From: Jonathan Keane Date: Thu, 5 Mar 2026 11:19:52 -0600 Subject: [PATCH 3/3] Update R/parse.R Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- R/parse.R | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/R/parse.R b/R/parse.R index 1af811d3..a0b90541 100644 --- a/R/parse.R +++ b/R/parse.R @@ -67,8 +67,12 @@ ensure_column <- function(data, default, name) { col <- bit64::as.integer64(col) } - if (is.character(default) && is.integer(col)) { - col <- as.character(col) + if (is.character(default) && (is.integer(col) || is.double(col))) { + if (is.double(col)) { + col <- format(col, scientific = FALSE, trim = TRUE) + } else { + col <- as.character(col) + } } if (inherits(default, "list") && !inherits(col, "list")) {