From a046c689b51800078acece16205bb89d2212abf1 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Thu, 30 Jul 2026 13:46:12 +0100 Subject: [PATCH 01/10] Increment version number to 0.2.1.9000 --- DESCRIPTION | 2 +- NEWS.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 263960c..35d67dd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: dsROCrate Title: 'DataSHIELD' RO-Crate Governance Functions -Version: 0.2.1 +Version: 0.2.1.9000 Authors@R: c( person(given = "Roberto", family = "Villegas-Diaz", diff --git a/NEWS.md b/NEWS.md index 8e41bee..d2dd634 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,5 @@ +# dsROCrate (development version) + # dsROCrate 0.2.1 ## Bug Fixes From dd678395b7a4ec45680d0d12130735d232e6c506 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Mon, 10 Aug 2026 12:01:34 +0100 Subject: [PATCH 02/10] Add profile, resources and tables as new arguments for auditing --- R/audit.R | 15 ++++++++++++++- R/audit_engine.R | 29 ++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/R/audit.R b/R/audit.R index 6aa1852..a7216e3 100644 --- a/R/audit.R +++ b/R/audit.R @@ -29,8 +29,15 @@ #' @param ... Additional arguments. #' @param intent Additional object with governance bundle/specification of the #' intent of a project. It takes the same types as `x`. -#' @param project String with project name(s) from which to extra Safe Project +#' @param profile String with profile name (used for OBiBa's Opal backend). +#' @param project String with project name(s) from which to extract Safe Project #' details. +#' @param resources Vector of strings with the names of the resources, part of +#' `project`. Optional, if not provided, all the resources associated to +#' `project` will be included in the RO-Crate. +#' @param tables Vector of strings with the names of the tables/datasets, part +#' of `project`. Optional, if not provided, all the tables/datasets +#' associated to `project` will be included in the RO-Crate. #' @param user String with the user name for which to extract Safe People #' details. #' @param logs_from Lower limit timestamp to filter out the outputs generated @@ -142,7 +149,10 @@ audit.opal <- function( x, ..., intent = NULL, + profile = "default", project = NULL, + resources = NULL, + tables = NULL, user = NULL, logs_from = -Inf, logs_to = Inf, @@ -154,7 +164,10 @@ audit.opal <- function( # call next method main_audit <- audit_engine( x, + profile = profile, project = c(intent_lst$main_audit_args$project, project), + resources = resources, + tables = tables, user = c(intent_lst$main_audit_args$user, user), logs_from = logs_from, logs_to = logs_to, diff --git a/R/audit_engine.R b/R/audit_engine.R index 000cf52..78c1e35 100644 --- a/R/audit_engine.R +++ b/R/audit_engine.R @@ -7,8 +7,15 @@ #' governance archive file, representing the intent of a project and #' associated governance details. #' @param ... Other optional arguments, see full documentation for details. -#' @param project String with project name(s) from which to extra Safe Project +#' @param profile String with profile name (used for OBiBa's Opal backend). +#' @param project String with project name(s) from which to extract Safe Project #' details. +#' @param resources Vector of strings with the names of the resources, part of +#' `project`. Optional, if not provided, all the resources associated to +#' `project` will be included in the RO-Crate. +#' @param tables Vector of strings with the names of the tables/datasets, part +#' of `project`. Optional, if not provided, all the tables/datasets +#' associated to `project` will be included in the RO-Crate. #' @param user String with the user name for which to extract Safe People #' details. #' @param logs_from Lower limit timestamp to filter out the outputs generated @@ -61,7 +68,10 @@ audit_engine.cr8tor <- function(x, ...) { audit_engine.opal <- function( x, ..., + profile = "default", project = NULL, + resources = NULL, + tables = NULL, user = NULL, logs_from = -Inf, logs_to = Inf, @@ -70,11 +80,16 @@ audit_engine.opal <- function( # local bindings name <- NULL - # create RO-Create with the 5 safes profile - crate <- rocrateR::rocrate_5s() - - # validate backend - validate_backend(x, ...) + # initialise empty RO-Crate with audit settings + crate <- x |> + dsROCrate::init( + profile = profile, + project = project, + resources = resources, + tables = tables, + user = user, + path = path + ) # if `project` is missing, then error if (is.null(project)) { @@ -165,7 +180,7 @@ audit_engine.opal <- function( purrr::reduce(rocrateR::remove_entity, .init = crate) # Safe Settings ---- - crate <- safe_setting(x, rocrate = crate) + crate <- safe_setting(crate, connection = x) # Safe Outputs ---- crate <- safe_people_tbl$name |> From 176b17c0a6874f8b9225aa789077fd978fa6b96a Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Mon, 10 Aug 2026 12:02:27 +0100 Subject: [PATCH 03/10] Include profile as an argument to filter extract installed packages' details and filter out logs --- R/backend-opal.R | 4 ++-- R/safe_output.R | 1 + R/safe_setting.R | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/R/backend-opal.R b/R/backend-opal.R index 1d98694..c8dec8c 100644 --- a/R/backend-opal.R +++ b/R/backend-opal.R @@ -9,8 +9,8 @@ backend_options.opal <- function(x, ...) { } #' @export -backend_packages.opal <- function(x, ...) { - opalr::dsadmin.package_descriptions(x, ...) +backend_packages.opal <- function(x, ..., profile = "default") { + opalr::dsadmin.package_descriptions(x, profile = profile, ...) } #' @export diff --git a/R/safe_output.R b/R/safe_output.R index 42055f8..19ccedb 100644 --- a/R/safe_output.R +++ b/R/safe_output.R @@ -223,6 +223,7 @@ safe_output.opal <- function( # filter logs dplyr::filter(`@timestamp` >= logs_from, `@timestamp` <= logs_to) |> dplyr::filter(logger_name == "datashield.user") |> + dplyr::filter(ds_profile == profile) |> dplyr::filter(username %in% user) userlogs <- NULL diff --git a/R/safe_setting.R b/R/safe_setting.R index c13638c..8d90013 100644 --- a/R/safe_setting.R +++ b/R/safe_setting.R @@ -200,7 +200,7 @@ safe_setting.opal <- function( # computational environment ---- # extract information about R packages installed in the environment - pkg_tbl <- backend_packages(x) |> + pkg_tbl <- backend_packages(x, profile = profile) |> tibble::as_tibble() pkg_entities <- pkg_tbl |> purrr::pmap(function(Package, Version, Description, Author, ...) { From a97296eea7f74e05744e238581b696c1dc1768fc Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Mon, 10 Aug 2026 12:02:42 +0100 Subject: [PATCH 04/10] New build --- man/audit.Rd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/man/audit.Rd b/man/audit.Rd index 6d1e009..83fdebd 100644 --- a/man/audit.Rd +++ b/man/audit.Rd @@ -24,7 +24,10 @@ audit(x, ...) x, ..., intent = NULL, + profile = "default", project = NULL, + resources = NULL, + tables = NULL, user = NULL, logs_from = -Inf, logs_to = Inf, @@ -48,9 +51,19 @@ Alternatively, a list of any of the above.} \item{intent}{Additional object with governance bundle/specification of the intent of a project. It takes the same types as \code{x}.} -\item{project}{String with project name(s) from which to extra Safe Project +\item{profile}{String with profile name (used for OBiBa's Opal backend).} + +\item{project}{String with project name(s) from which to extract Safe Project details.} +\item{resources}{Vector of strings with the names of the resources, part of +\code{project}. Optional, if not provided, all the resources associated to +\code{project} will be included in the RO-Crate.} + +\item{tables}{Vector of strings with the names of the tables/datasets, part +of \code{project}. Optional, if not provided, all the tables/datasets +associated to \code{project} will be included in the RO-Crate.} + \item{user}{String with the user name for which to extract Safe People details.} From 437f76184dffe97a4171b295e599a321243a082a Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Tue, 11 Aug 2026 10:08:47 +0100 Subject: [PATCH 05/10] Add placeholder row to handle cases without logs --- R/safe_output.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/safe_output.R b/R/safe_output.R index 19ccedb..d69adf7 100644 --- a/R/safe_output.R +++ b/R/safe_output.R @@ -217,6 +217,7 @@ safe_output.opal <- function( # parse logs userlogs_tbl <- backend_logs(x) |> tibble::as_tibble() |> + dplyr::bind_rows(tibble::tibble(ds_profile = "-999999")) |> dplyr::mutate( `@timestamp` = as.POSIXct(`@timestamp`, format = "%Y-%m-%dT%H:%M:%S") ) |> From 44ce6758eddcc363ef2702a746a0217c50a4ce32 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Fri, 21 Aug 2026 10:59:15 +0100 Subject: [PATCH 06/10] Introduce internal helper, calls_to_tbl, to handle parsing list of logs into tibble and handle empty lists. --- R/safe_output.R | 22 +------------------ R/utils-safe_output.R | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/R/safe_output.R b/R/safe_output.R index d69adf7..57b13a2 100644 --- a/R/safe_output.R +++ b/R/safe_output.R @@ -352,27 +352,7 @@ safe_output.opal <- function( }) # convert list of calls into tibble - calls_tbl <- purrr::map(calls_lst, \(x) { - tibble::tibble( - timestamp = format(x$created_at, '%Y-%m-%dT%H:%M:%S'), - action = "AGGREGATE", - user = x$user, - r_cmd = x$original, - fx = paste0(x$package, x$namespace, x$fx), - args = list(x$args), - symbol = NA, - table = x$args |> - purrr::map(function(x) { - if (!inherits(x, "safe_reference")) { - return(NA_character_) - } - resolve_symbol_asset(x$symbol_id, registry) - }), - session = x$session, - profile = x$profile - ) - }) |> - purrr::list_c() + calls_tbl <- calls_to_tbl(calls_lst, registry) # combine function calls with symbol's registry calls_symbols_tbl <- calls_tbl |> diff --git a/R/utils-safe_output.R b/R/utils-safe_output.R index e637a9d..78f3578 100644 --- a/R/utils-safe_output.R +++ b/R/utils-safe_output.R @@ -1,3 +1,53 @@ +#' Convert log calls into tibble +#' +#' @param calls List with log calls. +#' @param registry Symbol registry. +#' +#' @returns Tibble with parsed log calls. +#' @keywords internal +#' +#' @noRd +calls_to_tbl <- function(calls, registry) { + if (length(calls) == 0L) { + return( + tibble::tibble( + timestamp = character(), + action = character(), + user = character(), + r_cmd = character(), + fx = character(), + args = list(), + symbol = character(), + table = character(), + session = character(), + profile = character() + ) + ) + } + + purrr::map(calls, \(x) { + tibble::tibble( + timestamp = format(x$created_at, "%Y-%m-%dT%H:%M:%S"), + action = "AGGREGATE", + user = x$user, + r_cmd = x$original, + fx = paste0(x$package, x$namespace, x$fx), + args = list(x$args), + symbol = NA_character_, + table = x$args |> + purrr::map(function(x) { + if (!inherits(x, "safe_reference")) { + return(NA_character_) + } + resolve_symbol_asset(x$symbol_id, registry) + }), + session = x$session, + profile = x$profile + ) + }) |> + purrr::list_rbind() +} + #' Extract Safe Output entity(ies) #' #' @inheritParams safe_data From 5a1dadcb1e7680d02a3e56302e18cf0b45bf0d70 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Fri, 21 Aug 2026 11:01:00 +0100 Subject: [PATCH 07/10] Update NEWS to include bug fixess --- NEWS.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index d2dd634..470f7da 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,10 @@ -# dsROCrate (development version) +# dsROCrate 0.2.2 + +## Bug fixes + +* Fixed an issue in `safe_output()` where Opal audit logs containing no `AGGREGATE` events could cause the audit pipeline to fail when constructing the calls table. +* `safe_output()` now handles audit sessions with logs but no `AGGREGATE` calls gracefully, returning an empty calls table rather than attempting to operate on `NULL`. +* Improved robustness of provenance extraction for audit sessions with incomplete or limited audit activity. # dsROCrate 0.2.1 From a9f514ea40f7ffdd336ac3afdab1ce347e836ae1 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Fri, 21 Aug 2026 11:09:01 +0100 Subject: [PATCH 08/10] Bump version to 0.2.2 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 35d67dd..3fc07d9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: dsROCrate Title: 'DataSHIELD' RO-Crate Governance Functions -Version: 0.2.1.9000 +Version: 0.2.2 Authors@R: c( person(given = "Roberto", family = "Villegas-Diaz", From c8f86125d848dfde7c629d36d287cf7dd64eb175 Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Fri, 21 Aug 2026 12:24:47 +0100 Subject: [PATCH 09/10] Add check to update default value for profile, in case this is not given --- R/safe_output.R | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/R/safe_output.R b/R/safe_output.R index 57b13a2..cb183b8 100644 --- a/R/safe_output.R +++ b/R/safe_output.R @@ -104,7 +104,11 @@ safe_output.opal <- function( `@timestamp` <- backend <- logger_name <- safe_people_id <- username <- NULL ds_action <- ds_eval <- ds_id <- ds_function <- ds_symbol <- ds_table <- NULL asset <- action <- is_placeholder <- kind <- symbol_id <- timestamp <- NULL - expr <- fx <- log_id <- r_cmd <- session <- symbol <- NULL + ds_profile <- expr <- fx <- log_id <- r_cmd <- session <- symbol <- NULL + + if (is.null(profile)) { + profile <- "default" + } # create formatted versions of input dates logs_from_is_valid <- FALSE From f1ac8964999160a7fc319ada47b8d234f167b74b Mon Sep 17 00:00:00 2001 From: Roberto Villegas-Diaz Date: Fri, 21 Aug 2026 12:44:04 +0100 Subject: [PATCH 10/10] Add comment on updating profile argument if not given --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 470f7da..ee3e9ee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ * Fixed an issue in `safe_output()` where Opal audit logs containing no `AGGREGATE` events could cause the audit pipeline to fail when constructing the calls table. * `safe_output()` now handles audit sessions with logs but no `AGGREGATE` calls gracefully, returning an empty calls table rather than attempting to operate on `NULL`. +* Fixed an issue where an RO-Crate without an explicit Opal profile attribute could cause safe_output() to fail when filtering audit logs. The default "default" Opal profile is now used when no profile is supplied. * Improved robustness of provenance extraction for audit sessions with incomplete or limited audit activity. # dsROCrate 0.2.1