From acaf276d5fb03676d394a901073e723939f2b7db Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Fri, 10 Oct 2025 17:26:43 +0200 Subject: [PATCH 1/9] feat: added session related S4 methods --- DESCRIPTION | 1 + NAMESPACE | 5 +++ R/DSConnection.R | 73 +++++++++++++++++++++++++++++++++------ R/DSSession.R | 68 ++++++++++++++++++++++++++++++++++++ man/DSConnection-class.Rd | 7 ++-- man/DSDriver-class.Rd | 3 +- man/DSObject-class.Rd | 3 +- man/DSResult-class.Rd | 3 +- man/DSSession-class.Rd | 26 ++++++++++++++ man/dsAggregate.Rd | 7 +++- man/dsAssignExpr.Rd | 7 +++- man/dsAssignResource.Rd | 7 +++- man/dsAssignTable.Rd | 7 +++- man/dsDisconnect.Rd | 4 ++- man/dsGetInfo.Rd | 4 ++- man/dsHasResource.Rd | 7 +++- man/dsHasSession.Rd | 53 ++++++++++++++++++++++++++++ man/dsHasTable.Rd | 7 +++- man/dsIsAsync.Rd | 9 +++-- man/dsIsReady.Rd | 39 +++++++++++++++++++++ man/dsKeepAlive.Rd | 4 ++- man/dsListMethods.Rd | 4 ++- man/dsListPackages.Rd | 4 ++- man/dsListProfiles.Rd | 7 ++-- man/dsListResources.Rd | 7 +++- man/dsListSymbols.Rd | 7 +++- man/dsListTables.Rd | 4 ++- man/dsListWorkspaces.Rd | 4 ++- man/dsRestoreWorkspace.Rd | 4 ++- man/dsRmSymbol.Rd | 4 ++- man/dsRmWorkspace.Rd | 4 ++- man/dsSaveWorkspace.Rd | 4 ++- man/dsSession.Rd | 58 +++++++++++++++++++++++++++++++ man/dsStateMessage.Rd | 39 +++++++++++++++++++++ 34 files changed, 455 insertions(+), 39 deletions(-) create mode 100644 R/DSSession.R create mode 100644 man/DSSession-class.Rd create mode 100644 man/dsHasSession.Rd create mode 100644 man/dsIsReady.Rd create mode 100644 man/dsSession.Rd create mode 100644 man/dsStateMessage.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 01073da..d4acb88 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -45,6 +45,7 @@ Collate: 'DSI-package.R' 'DSLoginBuilder.R' 'DSResult.R' + 'DSSession.R' 'datashield.aggregate.R' 'datashield.assign.R' 'datashield.connections.R' diff --git a/NAMESPACE b/NAMESPACE index be40a13..b958aa5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -37,9 +37,11 @@ export(dsDisconnect) export(dsFetch) export(dsGetInfo) export(dsHasResource) +export(dsHasSession) export(dsHasTable) export(dsIsAsync) export(dsIsCompleted) +export(dsIsReady) export(dsKeepAlive) export(dsListMethods) export(dsListPackages) @@ -52,11 +54,14 @@ export(dsRestoreWorkspace) export(dsRmSymbol) export(dsRmWorkspace) export(dsSaveWorkspace) +export(dsSession) +export(dsStateMessage) export(newDSLoginBuilder) exportClasses(DSConnection) exportClasses(DSDriver) exportClasses(DSObject) exportClasses(DSResult) +exportClasses(DSSession) import(R6) import(progress) importFrom(cli,cli_abort) diff --git a/R/DSConnection.R b/R/DSConnection.R index 35c557d..dca4af8 100644 --- a/R/DSConnection.R +++ b/R/DSConnection.R @@ -29,7 +29,6 @@ setClass("DSConnection", representation(name = "character"), contains = c("DSObj #' accessible through this connection. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. -#' #' @return A character vector of table names. #' #' @family DSConnection generics @@ -53,6 +52,7 @@ setGeneric("dsListTables", #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. #' @param table the table fully qualified name +#' @return A logical indicating if the table exists. #' #' @family DSConnection generics #' @examples @@ -73,6 +73,7 @@ setGeneric("dsHasTable", #' accessible through this connection. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. +#' @return A character vector of resource names. #' #' @family DSConnection generics #' @examples @@ -94,6 +95,7 @@ setGeneric("dsListResources", #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. #' @param resource the resource fully qualified name +#' @return A logical indicating if the resource exists. #' #' @family DSConnection generics #' @examples @@ -108,6 +110,52 @@ setGeneric("dsHasResource", def = function(conn, resource) standardGeneric("dsHasResource"), valueClass = "logical") +#' Check remote R session exists +#' +#' Check if a remote R session exists (not necessarily running and ready to accept +#' R commands submissions). +#' +#' @param conn An object that inherits from \code{\link{DSConnection-class}}. +#' @return A logical indicating if a remote R session exists accessible through this connection. +#' +#' @family DSConnection generics +#' @examples +#' \dontrun{ +#' con <- dsConnect(DSOpal::Opal(), "server1", +#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +#' dsHasSession(con) +#' dsDisconnect(con) +#' } +#' @export +setGeneric("dsHasSession", + def = function(conn) standardGeneric("dsHasSession"), + valueClass = "logical") + +#' Create a remote R session +#' +#' Create a remote R session if none exists. If a remote R session already exists, +#' it will be reused. Returns a logical indicating if a remote R session exists +#' accessible through this connection. +#' +#' @param conn An object that inherits from \code{\link{DSConnection-class}}. +#' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) +#' the calls are parallelized over the connections, when the connection supports +#' that feature, with an extra overhead of requests. +#' @return An object of class \code{\link{DSSession-class}} representing the remote R session. +#' +#' @family DSConnection generics +#' @examples +#' \dontrun{ +#' con <- dsConnect(DSOpal::Opal(), "server1", +#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +#' dsSession(con, async=TRUE) +#' dsDisconnect(con) +#' } +#' @export +setGeneric("dsSession", + def = function(conn, async=TRUE) standardGeneric("dsSession"), + valueClass = "DSSession") + #' Assign a data table #' #' Assign a data table from the data repository to a symbol in the DataSHIELD R session. @@ -126,7 +174,8 @@ setGeneric("dsHasResource", #' will be the data frame row names. When specified this column can be used to perform joins between data frames. #' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over #' the connections, when the connection supports that feature, with an extra overhead of requests. -#' +#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +#' #' @family DSConnection generics #' @examples #' \dontrun{ @@ -150,7 +199,8 @@ setGeneric("dsAssignTable", #' @param resource Fully qualified name of a resource reference in the data repository. #' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over #' the connections, when the connection supports that feature, with an extra overhead of requests. -#' +#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +#' #' @family DSConnection generics #' @examples #' \dontrun{ @@ -174,7 +224,8 @@ setGeneric("dsAssignResource", #' @param expr A R expression with allowed assign functions calls. #' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over #' the connections, when the connection supports that feature, with an extra overhead of requests. -#' +#' @return An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +#' #' @family DSConnection generics #' @examples #' \dontrun{ @@ -197,7 +248,8 @@ setGeneric("dsAssignExpr", #' @param expr Expression to evaluate. #' @param async Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over #' the connections, when the connection supports that feature, with an extra overhead of requests. -#' +#' @return An object of class \code{\link{DSResult-class}} representing the result of the aggregation operation. +#' #' @family DSConnection generics #' @examples #' \dontrun{ @@ -217,6 +269,7 @@ setGeneric("dsAggregate", #' After assignments have been performed, some symbols live in the DataSHIELD R session on the server side. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. +#' @return A character vector of symbol names. #' #' @family DSConnection generics #' @examples @@ -257,8 +310,8 @@ setGeneric("dsRmSymbol", #' Get the list of DataSHIELD profiles that have been configured on the remote data repository. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. -#' -#' @return A list containing the "available" character vector of profile names and the "current" profile (in case a default one was assigned). +#' @return A list containing the "available" character vector of profile names and +#' the "current" profile (in case a default one was assigned). #' #' @family DSConnection generics #' @examples @@ -279,7 +332,6 @@ setGeneric("dsListProfiles", #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. #' @param type Type of the method: "aggregate" (default) or "assign". -#' #' @return A data.frame with columns: name, type ('aggregate' or 'assign'), class ('function' or 'script'), value, package, version. #' #' @family DSConnection generics @@ -300,7 +352,6 @@ setGeneric("dsListMethods", #' Get the list of DataSHIELD packages with their version, that have been configured on the remote data repository. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. -#' #' @return A data.frame with columns: name, version. #' #' @family DSConnection generics @@ -322,7 +373,6 @@ setGeneric("dsListPackages", #' Get the list of DataSHIELD workspaces, that have been saved on the remote data repository. #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. -#' #' @return A data.frame with columns: name, lastAccessDate, size. #' #' @family DSConnection generics @@ -407,10 +457,11 @@ setGeneric("dsRmWorkspace", #' When a \code{\link{DSResult-class}} object is returned on aggregation or assignment operation, #' the raw result can be accessed asynchronously, allowing parallelization of DataSHIELD calls #' over multpile servers. The returned named list of logicals will specify if asynchronicity is supported for: -#' aggregation operation ('aggregate'), table assignment operation ('assignTable'), +#' session operation ('session'), aggregation operation ('aggregate'), table assignment operation ('assignTable'), #' resource assignment operation ('assignResource') and expression assignment operation ('assignExpr'). #' #' @param conn An object that inherits from \code{\link{DSConnection-class}}. +#' @return A named list of logicals indicating if asynchronicity is supported for session, aggregation and assignment operations. #' #' @family DSConnection generics #' @examples diff --git a/R/DSSession.R b/R/DSSession.R new file mode 100644 index 0000000..978f541 --- /dev/null +++ b/R/DSSession.R @@ -0,0 +1,68 @@ +#' DSSession class +#' +#' This virtual class describes the state of the R session. +#' +#' The default show method displays a summary of the R session state using other +#' DS generics. +#' +#' @name DSSession-class +#' @docType class +#' @family DS classes +#' @family DSSession generics +#' @export +#' @include DSObject.R +setClass("DSSession", contains = c("DSObject", "VIRTUAL")) + +#' Get whether the remote R session is up and running +#' +#' Get whether the remote R session is up and running, ready to accept R commands. +#' The primary use of this function is to know whether the session is ready after it has been +#' created in an asynchronous way. +#' +#' @param session An object inheriting from \code{\link{DSSession-class}}. +#' @return A logical +#' +#' @family DSSession generics +#' @examples +#' \dontrun{ +#' con <- dsConnect(DSOpal::Opal(), "server1", +#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +#' session <- dsSession(con, async = TRUE) +#' ready <- dsIsReady(session) +#' while (!ready) { +#' Sys.sleep(1) +#' ready <- dsIsReady(session) +#' cat(".") +#' } +#' dsDisconnect(con) +#' } +#' @export +setGeneric("dsIsReady", + def = function(session) standardGeneric("dsIsReady")) + +#' Get the state of the remote R session +#' +#' Get a human-readable message that informs about the state of the remote R session. +#' The primary use of this function is to inform the user about the session state process +#' after it has been created in an asynchronous way. +#' +#' @param session An object inheriting from \code{\link{DSSession-class}}. +#' @return A character string +#' +#' @family DSSession generics +#' @examples +#' \dontrun{ +#' con <- dsConnect(DSOpal::Opal(), "server1", +#' username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +#' session <- dsSession(con, async = TRUE) +#' ready <- dsIsReady(session) +#' while (!ready) { +#' Sys.sleep(1) +#' ready <- dsIsReady(session) +#' cat(dsStateMessage(session), "\n") +#' } +#' dsDisconnect(con) +#' } +#' @export +setGeneric("dsStateMessage", + def = function(session) standardGeneric("dsStateMessage")) diff --git a/man/DSConnection-class.Rd b/man/DSConnection-class.Rd index ea6cfe8..4b8853d 100644 --- a/man/DSConnection-class.Rd +++ b/man/DSConnection-class.Rd @@ -27,7 +27,8 @@ dsDisconnect(con) Other DS classes: \code{\link{DSDriver-class}}, \code{\link{DSObject-class}}, -\code{\link{DSResult-class}} +\code{\link{DSResult-class}}, +\code{\link{DSSession-class}} Other DSConnection generics: \code{\link{dsAggregate}()}, @@ -37,6 +38,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -50,7 +52,8 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DS classes} \concept{DSConnection generics} diff --git a/man/DSDriver-class.Rd b/man/DSDriver-class.Rd index cc1793b..e39dc44 100644 --- a/man/DSDriver-class.Rd +++ b/man/DSDriver-class.Rd @@ -13,7 +13,8 @@ connections. Other DS classes: \code{\link{DSConnection-class}}, \code{\link{DSObject-class}}, -\code{\link{DSResult-class}} +\code{\link{DSResult-class}}, +\code{\link{DSSession-class}} Other DSDriver generics: \code{\link{dsConnect}()}, diff --git a/man/DSObject-class.Rd b/man/DSObject-class.Rd index 754bb2f..3aef8e0 100644 --- a/man/DSObject-class.Rd +++ b/man/DSObject-class.Rd @@ -53,6 +53,7 @@ dsDisconnect(con) Other DS classes: \code{\link{DSConnection-class}}, \code{\link{DSDriver-class}}, -\code{\link{DSResult-class}} +\code{\link{DSResult-class}}, +\code{\link{DSSession-class}} } \concept{DS classes} diff --git a/man/DSResult-class.Rd b/man/DSResult-class.Rd index 8ef40a8..6e39395 100644 --- a/man/DSResult-class.Rd +++ b/man/DSResult-class.Rd @@ -21,7 +21,8 @@ DS generics. Other DS classes: \code{\link{DSConnection-class}}, \code{\link{DSDriver-class}}, -\code{\link{DSObject-class}} +\code{\link{DSObject-class}}, +\code{\link{DSSession-class}} Other DSResult generics: \code{\link{dsFetch}()}, diff --git a/man/DSSession-class.Rd b/man/DSSession-class.Rd new file mode 100644 index 0000000..4681315 --- /dev/null +++ b/man/DSSession-class.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DSSession.R +\docType{class} +\name{DSSession-class} +\alias{DSSession-class} +\title{DSSession class} +\description{ +This virtual class describes the state of the R session. +} +\details{ +The default show method displays a summary of the R session state using other +DS generics. +} +\seealso{ +Other DS classes: +\code{\link{DSConnection-class}}, +\code{\link{DSDriver-class}}, +\code{\link{DSObject-class}}, +\code{\link{DSResult-class}} + +Other DSSession generics: +\code{\link{dsIsReady}()}, +\code{\link{dsStateMessage}()} +} +\concept{DS classes} +\concept{DSSession generics} diff --git a/man/dsAggregate.Rd b/man/dsAggregate.Rd index 4da9640..6fbfd17 100644 --- a/man/dsAggregate.Rd +++ b/man/dsAggregate.Rd @@ -14,6 +14,9 @@ dsAggregate(conn, expr, async = TRUE) \item{async}{Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over the connections, when the connection supports that feature, with an extra overhead of requests.} } +\value{ +An object of class \code{\link{DSResult-class}} representing the result of the aggregation operation. +} \description{ Aggregate some data from the DataSHIELD R session using a valid R expression. The aggregation expression must satisfy the data repository's DataSHIELD configuration. @@ -36,6 +39,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -49,6 +53,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsAssignExpr.Rd b/man/dsAssignExpr.Rd index a592472..d3d8a9c 100644 --- a/man/dsAssignExpr.Rd +++ b/man/dsAssignExpr.Rd @@ -16,6 +16,9 @@ dsAssignExpr(conn, symbol, expr, async = TRUE) \item{async}{Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over the connections, when the connection supports that feature, with an extra overhead of requests.} } +\value{ +An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +} \description{ Assign the result of the evaluation of an expression to a symbol the DataSHIELD R session The assignment expression must satisfy the data repository's DataSHIELD configuration. @@ -37,6 +40,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -50,6 +54,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsAssignResource.Rd b/man/dsAssignResource.Rd index c66b817..6bd899a 100644 --- a/man/dsAssignResource.Rd +++ b/man/dsAssignResource.Rd @@ -16,6 +16,9 @@ dsAssignResource(conn, symbol, resource, async = TRUE) \item{async}{Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over the connections, when the connection supports that feature, with an extra overhead of requests.} } +\value{ +An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +} \description{ Assign a resource object of class 'ResourceClient' from the data repository to a symbol in the DataSHIELD R session. The resource reference to be assigned must exist (i.e. proper permissions apply) for the DataSHIELD user. @@ -37,6 +40,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -50,6 +54,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsAssignTable.Rd b/man/dsAssignTable.Rd index ec07e68..2014f6f 100644 --- a/man/dsAssignTable.Rd +++ b/man/dsAssignTable.Rd @@ -37,6 +37,9 @@ will be the data frame row names. When specified this column can be used to perf \item{async}{Whether the result of the call should be retrieved asynchronously. When TRUE (default) the calls are parallelized over the connections, when the connection supports that feature, with an extra overhead of requests.} } +\value{ +An object of class \code{\link{DSResult-class}} representing the result of the assignment operation. +} \description{ Assign a data table from the data repository to a symbol in the DataSHIELD R session. The table to be assigned must exist (i.e. proper permissions apply) for the DataSHIELD user. @@ -58,6 +61,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -71,6 +75,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsDisconnect.Rd b/man/dsDisconnect.Rd index 2916ad8..42ef4d1 100644 --- a/man/dsDisconnect.Rd +++ b/man/dsDisconnect.Rd @@ -31,6 +31,7 @@ Other DSConnection generics: \code{\link{dsAssignTable}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -44,6 +45,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsGetInfo.Rd b/man/dsGetInfo.Rd index c1b970d..4fcb14a 100644 --- a/man/dsGetInfo.Rd +++ b/man/dsGetInfo.Rd @@ -50,6 +50,7 @@ Other DSConnection generics: \code{\link{dsAssignTable}()}, \code{\link{dsDisconnect}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -63,7 +64,8 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} Other DSResult generics: \code{\link{DSResult-class}}, diff --git a/man/dsHasResource.Rd b/man/dsHasResource.Rd index 28acf43..c48f39d 100644 --- a/man/dsHasResource.Rd +++ b/man/dsHasResource.Rd @@ -11,6 +11,9 @@ dsHasResource(conn, resource) \item{resource}{the resource fully qualified name} } +\value{ +A logical indicating if the resource exists. +} \description{ Check if a remote resource reference exists in the data repository. Returns a logical indicating the existence of a remote resource accessible through this connection. @@ -32,6 +35,7 @@ Other DSConnection generics: \code{\link{dsAssignTable}()}, \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +49,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsHasSession.Rd b/man/dsHasSession.Rd new file mode 100644 index 0000000..ee24135 --- /dev/null +++ b/man/dsHasSession.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DSConnection.R +\name{dsHasSession} +\alias{dsHasSession} +\title{Check remote R session exists} +\usage{ +dsHasSession(conn) +} +\arguments{ +\item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} +} +\value{ +A logical indicating if a remote R session exists accessible through this connection. +} +\description{ +Check if a remote R session exists (not necessarily running and ready to accept +R commands submissions). +} +\examples{ +\dontrun{ +con <- dsConnect(DSOpal::Opal(), "server1", + username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +dsHasSession(con) +dsDisconnect(con) +} +} +\seealso{ +Other DSConnection generics: +\code{\link{DSConnection-class}}, +\code{\link{dsAggregate}()}, +\code{\link{dsAssignExpr}()}, +\code{\link{dsAssignResource}()}, +\code{\link{dsAssignTable}()}, +\code{\link{dsDisconnect}()}, +\code{\link{dsGetInfo}()}, +\code{\link{dsHasResource}()}, +\code{\link{dsHasTable}()}, +\code{\link{dsIsAsync}()}, +\code{\link{dsKeepAlive}()}, +\code{\link{dsListMethods}()}, +\code{\link{dsListPackages}()}, +\code{\link{dsListProfiles}()}, +\code{\link{dsListResources}()}, +\code{\link{dsListSymbols}()}, +\code{\link{dsListTables}()}, +\code{\link{dsListWorkspaces}()}, +\code{\link{dsRestoreWorkspace}()}, +\code{\link{dsRmSymbol}()}, +\code{\link{dsRmWorkspace}()}, +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} +} +\concept{DSConnection generics} diff --git a/man/dsHasTable.Rd b/man/dsHasTable.Rd index 7944d90..cc2f89a 100644 --- a/man/dsHasTable.Rd +++ b/man/dsHasTable.Rd @@ -11,6 +11,9 @@ dsHasTable(conn, table) \item{table}{the table fully qualified name} } +\value{ +A logical indicating if the table exists. +} \description{ Check if a remote table exists in the data repository. Returns a logical indicating the existence of a remote table accessible through this connection. @@ -33,6 +36,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, \code{\link{dsListMethods}()}, @@ -45,6 +49,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsIsAsync.Rd b/man/dsIsAsync.Rd index 12c5a2c..5bf9840 100644 --- a/man/dsIsAsync.Rd +++ b/man/dsIsAsync.Rd @@ -9,11 +9,14 @@ dsIsAsync(conn) \arguments{ \item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} } +\value{ +A named list of logicals indicating if asynchronicity is supported for session, aggregation and assignment operations. +} \description{ When a \code{\link{DSResult-class}} object is returned on aggregation or assignment operation, the raw result can be accessed asynchronously, allowing parallelization of DataSHIELD calls over multpile servers. The returned named list of logicals will specify if asynchronicity is supported for: -aggregation operation ('aggregate'), table assignment operation ('assignTable'), +session operation ('session'), aggregation operation ('aggregate'), table assignment operation ('assignTable'), resource assignment operation ('assignResource') and expression assignment operation ('assignExpr'). } \examples{ @@ -34,6 +37,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsKeepAlive}()}, \code{\link{dsListMethods}()}, @@ -46,6 +50,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsIsReady.Rd b/man/dsIsReady.Rd new file mode 100644 index 0000000..69c35ee --- /dev/null +++ b/man/dsIsReady.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DSSession.R +\name{dsIsReady} +\alias{dsIsReady} +\title{Get whether the remote R session is up and running} +\usage{ +dsIsReady(session) +} +\arguments{ +\item{session}{An object inheriting from \code{\link{DSSession-class}}.} +} +\value{ +A logical +} +\description{ +Get whether the remote R session is up and running, ready to accept R commands. +The primary use of this function is to know whether the session is ready after it has been +created in an asynchronous way. +} +\examples{ +\dontrun{ +con <- dsConnect(DSOpal::Opal(), "server1", + username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +session <- dsSession(con, async = TRUE) +ready <- dsIsReady(session) +while (!ready) { + Sys.sleep(1) + ready <- dsIsReady(session) + cat(".") +} +dsDisconnect(con) +} +} +\seealso{ +Other DSSession generics: +\code{\link{DSSession-class}}, +\code{\link{dsStateMessage}()} +} +\concept{DSSession generics} diff --git a/man/dsKeepAlive.Rd b/man/dsKeepAlive.Rd index 983a200..e213b03 100644 --- a/man/dsKeepAlive.Rd +++ b/man/dsKeepAlive.Rd @@ -32,6 +32,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsListMethods}()}, @@ -44,6 +45,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListMethods.Rd b/man/dsListMethods.Rd index c646adf..d42b704 100644 --- a/man/dsListMethods.Rd +++ b/man/dsListMethods.Rd @@ -35,6 +35,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -47,6 +48,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListPackages.Rd b/man/dsListPackages.Rd index a0dc12b..8e938f9 100644 --- a/man/dsListPackages.Rd +++ b/man/dsListPackages.Rd @@ -33,6 +33,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +46,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListProfiles.Rd b/man/dsListProfiles.Rd index f2a015a..9dfd1be 100644 --- a/man/dsListProfiles.Rd +++ b/man/dsListProfiles.Rd @@ -10,7 +10,8 @@ dsListProfiles(conn) \item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} } \value{ -A list containing the "available" character vector of profile names and the "current" profile (in case a default one was assigned). +A list containing the "available" character vector of profile names and +the "current" profile (in case a default one was assigned). } \description{ Get the list of DataSHIELD profiles that have been configured on the remote data repository. @@ -33,6 +34,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +47,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListResources.Rd b/man/dsListResources.Rd index 44eb9cb..65ebbe9 100644 --- a/man/dsListResources.Rd +++ b/man/dsListResources.Rd @@ -9,6 +9,9 @@ dsListResources(conn) \arguments{ \item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} } +\value{ +A character vector of resource names. +} \description{ List remote resources from the data repository. Returns the unquoted names of remote resources accessible through this connection. @@ -31,6 +34,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -43,6 +47,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListSymbols.Rd b/man/dsListSymbols.Rd index 352aba5..31de995 100644 --- a/man/dsListSymbols.Rd +++ b/man/dsListSymbols.Rd @@ -9,6 +9,9 @@ dsListSymbols(conn) \arguments{ \item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} } +\value{ +A character vector of symbol names. +} \description{ After assignments have been performed, some symbols live in the DataSHIELD R session on the server side. } @@ -31,6 +34,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -43,6 +47,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListTables.Rd b/man/dsListTables.Rd index 36c42ff..aa257e8 100644 --- a/man/dsListTables.Rd +++ b/man/dsListTables.Rd @@ -34,6 +34,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -46,6 +47,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsListWorkspaces.Rd b/man/dsListWorkspaces.Rd index de32409..635b260 100644 --- a/man/dsListWorkspaces.Rd +++ b/man/dsListWorkspaces.Rd @@ -33,6 +33,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +46,7 @@ Other DSConnection generics: \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsRestoreWorkspace.Rd b/man/dsRestoreWorkspace.Rd index 1e62045..dbca63b 100644 --- a/man/dsRestoreWorkspace.Rd +++ b/man/dsRestoreWorkspace.Rd @@ -34,6 +34,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -46,6 +47,7 @@ Other DSConnection generics: \code{\link{dsListWorkspaces}()}, \code{\link{dsRmSymbol}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsRmSymbol.Rd b/man/dsRmSymbol.Rd index 2d5ad28..97292a2 100644 --- a/man/dsRmSymbol.Rd +++ b/man/dsRmSymbol.Rd @@ -33,6 +33,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +46,7 @@ Other DSConnection generics: \code{\link{dsListWorkspaces}()}, \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmWorkspace}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsRmWorkspace.Rd b/man/dsRmWorkspace.Rd index b4e627c..b0d41bf 100644 --- a/man/dsRmWorkspace.Rd +++ b/man/dsRmWorkspace.Rd @@ -36,6 +36,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -48,6 +49,7 @@ Other DSConnection generics: \code{\link{dsListWorkspaces}()}, \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, -\code{\link{dsSaveWorkspace}()} +\code{\link{dsSaveWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsSaveWorkspace.Rd b/man/dsSaveWorkspace.Rd index 0983307..79685c7 100644 --- a/man/dsSaveWorkspace.Rd +++ b/man/dsSaveWorkspace.Rd @@ -33,6 +33,7 @@ Other DSConnection generics: \code{\link{dsDisconnect}()}, \code{\link{dsGetInfo}()}, \code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, \code{\link{dsHasTable}()}, \code{\link{dsIsAsync}()}, \code{\link{dsKeepAlive}()}, @@ -45,6 +46,7 @@ Other DSConnection generics: \code{\link{dsListWorkspaces}()}, \code{\link{dsRestoreWorkspace}()}, \code{\link{dsRmSymbol}()}, -\code{\link{dsRmWorkspace}()} +\code{\link{dsRmWorkspace}()}, +\code{\link{dsSession}()} } \concept{DSConnection generics} diff --git a/man/dsSession.Rd b/man/dsSession.Rd new file mode 100644 index 0000000..d4cd12f --- /dev/null +++ b/man/dsSession.Rd @@ -0,0 +1,58 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DSConnection.R +\name{dsSession} +\alias{dsSession} +\title{Create a remote R session} +\usage{ +dsSession(conn, async = TRUE) +} +\arguments{ +\item{conn}{An object that inherits from \code{\link{DSConnection-class}}.} + +\item{async}{Whether the result of the call should be retrieved asynchronously. When TRUE (default) +the calls are parallelized over the connections, when the connection supports +that feature, with an extra overhead of requests.} +} +\value{ +An object of class \code{\link{DSSession-class}} representing the remote R session. +} +\description{ +Create a remote R session if none exists. If a remote R session already exists, +it will be reused. Returns a logical indicating if a remote R session exists +accessible through this connection. +} +\examples{ +\dontrun{ +con <- dsConnect(DSOpal::Opal(), "server1", + username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +dsSession(con, async=TRUE) +dsDisconnect(con) +} +} +\seealso{ +Other DSConnection generics: +\code{\link{DSConnection-class}}, +\code{\link{dsAggregate}()}, +\code{\link{dsAssignExpr}()}, +\code{\link{dsAssignResource}()}, +\code{\link{dsAssignTable}()}, +\code{\link{dsDisconnect}()}, +\code{\link{dsGetInfo}()}, +\code{\link{dsHasResource}()}, +\code{\link{dsHasSession}()}, +\code{\link{dsHasTable}()}, +\code{\link{dsIsAsync}()}, +\code{\link{dsKeepAlive}()}, +\code{\link{dsListMethods}()}, +\code{\link{dsListPackages}()}, +\code{\link{dsListProfiles}()}, +\code{\link{dsListResources}()}, +\code{\link{dsListSymbols}()}, +\code{\link{dsListTables}()}, +\code{\link{dsListWorkspaces}()}, +\code{\link{dsRestoreWorkspace}()}, +\code{\link{dsRmSymbol}()}, +\code{\link{dsRmWorkspace}()}, +\code{\link{dsSaveWorkspace}()} +} +\concept{DSConnection generics} diff --git a/man/dsStateMessage.Rd b/man/dsStateMessage.Rd new file mode 100644 index 0000000..c85788c --- /dev/null +++ b/man/dsStateMessage.Rd @@ -0,0 +1,39 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DSSession.R +\name{dsStateMessage} +\alias{dsStateMessage} +\title{Get the state of the remote R session} +\usage{ +dsStateMessage(session) +} +\arguments{ +\item{session}{An object inheriting from \code{\link{DSSession-class}}.} +} +\value{ +A character string +} +\description{ +Get a human-readable message that informs about the state of the remote R session. +The primary use of this function is to inform the user about the session state process +after it has been created in an asynchronous way. +} +\examples{ +\dontrun{ +con <- dsConnect(DSOpal::Opal(), "server1", + username = "dsuser", password = "password", url = "https://opal-demo.obiba.org") +session <- dsSession(con, async = TRUE) +ready <- dsIsReady(session) +while (!ready) { + Sys.sleep(1) + ready <- dsIsReady(session) + cat(dsStateMessage(session), "\n") +} +dsDisconnect(con) +} +} +\seealso{ +Other DSSession generics: +\code{\link{DSSession-class}}, +\code{\link{dsIsReady}()} +} +\concept{DSSession generics} From e32dd590bf7ae6df2b0cd00033f8588802d2b416 Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Sat, 11 Oct 2025 10:17:18 +0200 Subject: [PATCH 2/9] feat: datashield.sessions added --- DESCRIPTION | 1 + NAMESPACE | 1 + R/datashield.aggregate.R | 1 + R/datashield.assign.R | 2 + R/datashield.sessions.R | 133 +++++++++++++++++++++++++++++++++++++ R/datashield.symbol.R | 1 + R/datashield.workspace.R | 2 + man/datashield.sessions.Rd | 48 +++++++++++++ 8 files changed, 189 insertions(+) create mode 100644 R/datashield.sessions.R create mode 100644 man/datashield.sessions.Rd diff --git a/DESCRIPTION b/DESCRIPTION index d4acb88..efa2d60 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -54,6 +54,7 @@ Collate: 'datashield.list.R' 'datashield.login.R' 'datashield.logout.R' + 'datashield.sessions.R' 'datashield.status.R' 'datashield.symbol.R' 'datashield.workspace.R' diff --git a/NAMESPACE b/NAMESPACE index b958aa5..3a434f6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -21,6 +21,7 @@ export(datashield.profiles) export(datashield.resource_status) export(datashield.resources) export(datashield.rm) +export(datashield.sessions) export(datashield.symbols) export(datashield.table_status) export(datashield.tables) diff --git a/R/datashield.aggregate.R b/R/datashield.aggregate.R index 490c326..23cd068 100644 --- a/R/datashield.aggregate.R +++ b/R/datashield.aggregate.R @@ -40,6 +40,7 @@ #' #' @export datashield.aggregate <- function(conns, expr, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) { + datashield.sessions(conns) .clearLastErrors() rval <- NULL diff --git a/R/datashield.assign.R b/R/datashield.assign.R index 69265be..19c05e4 100644 --- a/R/datashield.assign.R +++ b/R/datashield.assign.R @@ -114,6 +114,7 @@ datashield.assign <- function(conns, symbol, value, variables=NULL, missings=FAL #' } #' @export datashield.assign.table <- function(conns, symbol, table, variables=NULL, missings=FALSE, identifiers=NULL, id.name=NULL, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) { + datashield.sessions(conns) .clearLastErrors() if (is.null(table) || length(table) == 0) { stop("Not a valid table name", call.=FALSE) @@ -415,6 +416,7 @@ datashield.assign.resource <- function(conns, symbol, resource, async=TRUE, succ #' } #' @export datashield.assign.expr <- function(conns, symbol, expr, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) { + datashield.sessions(conns) .clearLastErrors() # prepare expressions as a named list diff --git a/R/datashield.sessions.R b/R/datashield.sessions.R new file mode 100644 index 0000000..09a8ac8 --- /dev/null +++ b/R/datashield.sessions.R @@ -0,0 +1,133 @@ +#' R/DataSHIELD remote sessions +#' +#' Ensure that the remote R sessions are up and running during the analysis. +#' +#' @param conns \code{\link{DSConnection-class}} object or a list of \code{\link{DSConnection-class}}s. +#' @param async Whether the remote R/DataSHIELD session should be created asynchronously. When TRUE (default) the calls are parallelized over +#' the connections, when the connection supports that feature, with an extra overhead of requests. +#' @param success Callback function that will be called each time an R session has been created from a connection. +#' The expected function signature is the connection/study name. Default is NULL (no callback). +#' @param error Callback function that will be called each time the R session creation request has failed. +#' The expected function signature is the connection/study name and the error message. Default is NULL (no callback). +#' @param errors.print Boolean, whether to print datashield errors in the console or return a message indicating that they can be retrieved using `datashield.errors`. +#' +#' @examples +#'\dontrun{ +#' # call sessions function on server side asynchronously +#' # i.e. each study connection will create a remote R session in parallel +#' datashield.sessions(conns) +#' +#' # call sessions function with callback functions +#' result <- datashield.sessions(conns, +#' success = function(server) { +#' # do something with server's success +#' }, +#' error = function(server, error) { +#' # do something with server's error +#' }) +#' } +#' +#' @export +datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) { + .clearLastErrors() + + if (is.list(conns)) { + # filter conns not having connection + fconns <- Filter(function(conn) { !dsHasSession(conn) }, conns) + if (length(fconns) == 0) { + return(invisible(NULL)) + } + + sessions <- list() + async <- lapply(fconns, function(conn) { ifelse(async, dsIsAsync(conn)$session, FALSE) }) + pb <- .newProgress(total = 1 + length(fconns)) + # async first + for (n in names(fconns)) { + if(async[[n]]) { + tryCatch({ + sessions[[n]] <- dsSession(fconns[[n]], async=TRUE) + }, error = function(e) { + .appendError(n, conditionMessage(e)) + if (.is.callback(error)) { + error(n, conditionMessage(e)) + } + }) + } + } + # not async (blocking calls) + for (n in names(fconns)) { + if(!async[[n]]) { + tryCatch({ + .tickProgress(pb, tokens = list(what = paste0("Session ", fconns[[n]]@name))) + sessions[[n]] <- dsSession(fconns[[n]], async=FALSE) + }, error = function(e) { + .appendError(n, conditionMessage(e)) + if (.is.callback(error)) { + error(n, conditionMessage(e)) + } + }) + } + } + # polling + completed <- replicate(length(fconns), FALSE) + names(completed) <- names(fconns) + checks <- 1 + while (!all(completed)) { + for (n in names(fconns)) { + if (!completed[[n]]) { + if (!.hasLastErrors(n)) { + tryCatch({ + msg <- dsStateMessage(sessions[[n]]) + .updateProgress(pb, step = length(subset(completed, completed == TRUE)), total = length(fconns), tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) + if(async[[n]]) { + completed[[n]] <- dsIsReady(sessions[[n]]) + if (completed[[n]]) { + .tickProgress(pb, tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) + } + } else { + completed[[n]] <- TRUE + .tickProgress(pb, tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) + } + if (completed[[n]] && .is.callback(success)) { + success(n) + } + }, error = function(e) { + .appendError(n, conditionMessage(e)) + completed[[n]] <- TRUE + if (.is.callback(error)) { + error(n, conditionMessage(e)) + } + }) + } else { + completed[[n]] <- TRUE + } + } else { + # heart beat request + dsKeepAlive(fconns[[n]]) + } + } + if (!all(completed)) { + .updateProgress(pb, step = length(subset(completed, completed == TRUE)), total = length(fconns), tokens = list(what = paste0("Waiting for R sessions to be ready..."))) + Sys.sleep(.getSleepTime(checks)) + checks <- checks + 1 + } + } + ignore <- .tickProgress(pb, tokens = list(what = paste0("All R sessions ready"))) + } else { + tryCatch({ + if (!dsHasSession(conns)) { + dsSession(conns, async = FALSE) + } + if (.is.callback(success)) { + success(conns@name) + } + }, error = function(e) { + .appendError(conns@name, conditionMessage(e)) + if (.is.callback(error)) { + error(conns@name, conditionMessage(e)) + } + }) + } + .handle_errors(errors.print) + invisible(NULL) +} diff --git a/R/datashield.symbol.R b/R/datashield.symbol.R index 3915a29..e5017f8 100644 --- a/R/datashield.symbol.R +++ b/R/datashield.symbol.R @@ -6,6 +6,7 @@ #' #' @export datashield.symbols <- function(conns) { + datashield.sessions(conns) if (is.list(conns)) { lapply(conns, FUN=datashield.symbols) } else { diff --git a/R/datashield.workspace.R b/R/datashield.workspace.R index f745f9f..a5ea80a 100644 --- a/R/datashield.workspace.R +++ b/R/datashield.workspace.R @@ -42,6 +42,7 @@ datashield.workspaces <- function(conns) { #' @param ws The workspace name #' @export datashield.workspace_save <- function(conns, ws) { + datashield.sessions(conns) if (is.list(conns)) { ignore <- lapply(conns, function(c) datashield.workspace_save(c, ws)) } else { @@ -60,6 +61,7 @@ datashield.workspace_save <- function(conns, ws) { #' @param ws The workspace name #' @export datashield.workspace_restore <- function(conns, ws) { + datashield.sessions(conns) if (is.list(conns)) { ignore <- lapply(conns, function(c) datashield.workspace_restore(c, ws)) } else { diff --git a/man/datashield.sessions.Rd b/man/datashield.sessions.Rd new file mode 100644 index 0000000..d509496 --- /dev/null +++ b/man/datashield.sessions.Rd @@ -0,0 +1,48 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/datashield.sessions.R +\name{datashield.sessions} +\alias{datashield.sessions} +\title{R/DataSHIELD remote sessions} +\usage{ +datashield.sessions( + conns, + async = TRUE, + success = NULL, + error = NULL, + errors.print = getOption("datashield.errors.print", FALSE) +) +} +\arguments{ +\item{conns}{\code{\link{DSConnection-class}} object or a list of \code{\link{DSConnection-class}}s.} + +\item{async}{Whether the remote R/DataSHIELD session should be created asynchronously. When TRUE (default) the calls are parallelized over +the connections, when the connection supports that feature, with an extra overhead of requests.} + +\item{success}{Callback function that will be called each time an R session has been created from a connection. +The expected function signature is the connection/study name. Default is NULL (no callback).} + +\item{error}{Callback function that will be called each time the R session creation request has failed. +The expected function signature is the connection/study name and the error message. Default is NULL (no callback).} + +\item{errors.print}{Boolean, whether to print datashield errors in the console or return a message indicating that they can be retrieved using `datashield.errors`.} +} +\description{ +Ensure that the remote R sessions are up and running during the analysis. +} +\examples{ +\dontrun{ +# call sessions function on server side asynchronously +# i.e. each study connection will create a remote R session in parallel +datashield.sessions(conns) + +# call sessions function with callback functions +result <- datashield.sessions(conns, + success = function(server) { + # do something with server's success + }, + error = function(server, error) { + # do something with server's error + }) +} + +} From 58593d50eb8f39eda4fd51169c18c5ad743b5e27 Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Sun, 12 Oct 2025 09:08:21 +0200 Subject: [PATCH 3/9] feat: use workspace restore and assignment functions in login function to handle R sessions properly --- R/datashield.login.R | 147 ++++++------------------------------------- 1 file changed, 19 insertions(+), 128 deletions(-) diff --git a/R/datashield.login.R b/R/datashield.login.R index a417e0c..187e223 100644 --- a/R/datashield.login.R +++ b/R/datashield.login.R @@ -156,10 +156,6 @@ datashield.login <- function(logins=NULL, assign=FALSE, variables=NULL, missings doConnection <- function(i) { # connection options conn.opts <- append(opts, eval(parse(text=as.character(options[[i]])))) - restoreId <- restore - if (!is.null(restore)) { - restoreId <- paste0(stdnames[i], ":", restore) - } # instanciate the DSDriver drv <- new(drivers[i]) # if the connection is HTTPS use ssl options else they are not required @@ -171,12 +167,12 @@ datashield.login <- function(logins=NULL, assign=FALSE, variables=NULL, missings cert <- userids[i] private <- pwds[i] conn.opts <- append(conn.opts, list(sslcert=cert, sslkey=private)) - conn.obj <- dsConnect(drv, name=stdnames[i], url=urls[i], opts=conn.opts, profile=profiles[i], restore=restoreId) + conn.obj <- dsConnect(drv, name=stdnames[i], url=urls[i], opts=conn.opts, profile=profiles[i]) } else { - conn.obj <- dsConnect(drv, name=stdnames[i], username=userids[i], password=pwds[i], token=tokens[i], url=urls[i], profile=profiles[i], opts=conn.opts, restore=restoreId) + conn.obj <- dsConnect(drv, name=stdnames[i], username=userids[i], password=pwds[i], token=tokens[i], url=urls[i], profile=profiles[i], opts=conn.opts) } } else { - conn.obj <- dsConnect(drv, name=stdnames[i], username=userids[i], password=pwds[i], token=tokens[i], url=urls[i], profile=profiles[i], opts=conn.opts, restore=restoreId) + conn.obj <- dsConnect(drv, name=stdnames[i], username=userids[i], password=pwds[i], token=tokens[i], url=urls[i], profile=profiles[i], opts=conn.opts) } conn.obj } @@ -227,135 +223,30 @@ datashield.login <- function(logins=NULL, assign=FALSE, variables=NULL, missings rconnections <- append(rconnections, x) } } + + # if restore is not null, restore the workspaces + if (!is.null(restore) && length(rconnections) > 0) { + datashield.workspace_restore(rconnections, restore) + } # if argument 'assign' is true assign data/resources to the data repository server(s) you logged in to. if(assign && length(rconnections) > 0) { - + #warning("Assignment of table/resources at login time is deprecated. Use datashield.assign functions instead.", call.=FALSE, immediate.=TRUE) isNotEmpty <- Vectorize(function(x) { !(is.null(x) || is.na(x) || length(x) == 0 || nchar(x) == 0) }) - - assignResources <- function() { - # Assign resource data in parallel - message("\nAssigning resource data...") - results <- list() - async <- c() - for (i in 1:length(connections)) { - if (excluded[i]) { - async <- append(async, FALSE) - } else { - async <- append(async, dsIsAsync(connections[[i]])$assignResource) - } - } - # async first - - pb <- .newProgress(total = 1 + length(connections) - length(excluded[excluded == TRUE])) - for (i in 1:length(connections)) { - if(!excluded[i] && async[i]) { - results[[i]] <- dsAssignResource(connections[[i]], symbol, resources[i]) - } - } - # not async (blocking calls) - for (i in 1:length(connections)) { - if(!excluded[i] && !async[i]) { - .tickProgress(pb, tokens = list(what = paste0("Assigning ", stdnames[i], " (", resources[i], ")"))) - results[[i]] <- dsAssignResource(connections[[i]], symbol, resources[i]) - } - } - for (i in 1:length(connections)) { - if(!excluded[i]) { - res <- results[[i]] - if (!is.null(res)) { - if (async[i]) { - .tickProgress(pb, tokens = list(what = paste0("Assigning ", stdnames[i], " (", resources[i], ")"))) - } - resInfo <- dsGetInfo(res) - if (resInfo$status == "FAILED") { - warning("Resource assignment of '", resources[i], "' failed for '", stdnames[i],"': ", resInfo$error, call.=FALSE, immediate.=TRUE) - } - } - } - } - .tickProgress(pb, tokens = list(what = "Assigned all resources")) - } - - assignTables <- function() { - if(is.null(variables)) { - # if the user does not specify variables (default behaviour) - # display a message telling the user that the whole dataset - # will be assigned since he did not specify variables - message("\n No variables have been specified. \n All the variables in the table \n (the whole dataset) will be assigned to R!") - } - - # Assign table data in parallel - message("\nAssigning table data...") - results <- list() - async <- c() - for (i in 1:length(connections)) { - if (excluded[i]) { - async <- append(async, FALSE) - } else { - async <- append(async, dsIsAsync(connections[[i]])$assignTable) - } - } - # async first - - pb <- .newProgress(total = 1 + length(connections) - length(excluded[excluded == TRUE])) - for (i in 1:length(connections)) { - if(!excluded[i] && async[i]) { - results[[i]] <- dsAssignTable(connections[[i]], symbol, tables[i], variables=variables, missings=missings, identifiers=idmappings[i], id.name=id.name) - } - } - # not async (blocking calls) - for (i in 1:length(connections)) { - if(!excluded[i] && !async[i]) { - .tickProgress(pb, tokens = list(what = paste0("Assigning ", stdnames[i], " (", tables[i], ")"))) - results[[i]] <- dsAssignTable(connections[[i]], symbol, tables[i], variables=variables, missings=missings, identifiers=idmappings[i], id.name=id.name) - } - } - for (i in 1:length(connections)) { - if(!excluded[i]) { - res <- results[[i]] - if (!is.null(res)) { - if (async[i]) { - .tickProgress(pb, tokens = list(what = paste0("Assigning ", stdnames[i], " (", tables[i], ")"))) - } - resInfo <- dsGetInfo(res) - if (resInfo$status == "FAILED") { - warning("Data assignment of '", tables[i], "' failed for '", stdnames[i],"': ", resInfo$error, call.=FALSE, immediate.=TRUE) - } - } - } - } - .tickProgress(pb, tokens = list(what = "Assigned all tables")) - - # Get column names in parallel - # Ensure the colnames aggregation is available - aggs <- datashield.method_status(rconnections, type="aggregate") - hasColnames <- aggs[aggs$name == "colnames",] - if (nrow(hasColnames)>0) { - message("\nVariables assigned:") - lapply(names(rconnections), function(n) { - if (hasColnames[[n]]) { - varnames <- dsFetch(dsAggregate(rconnections[[n]], paste0('colnames(', symbol,')'), async = FALSE)) - if(length(varnames[[1]]) > 0) { - message(n, " -- ", paste(unlist(varnames), collapse=", ")) - } else { - message(n, " -- No variables assigned. Please check login details for this study and verify that the variables are available!") - } - } else { - message(n, " -- ? (colnames() aggregation method not available)") - } - }) - } - } - if (all(isNotEmpty(resources))) { - assignResources() + tryCatch({ + datashield.assign.resource(rconnections, symbol = symbol, resource = logins) + }, error = function(e) { + warning("Resource assignmnt failed", call.=FALSE, immediate.=TRUE) + }) } - if (all(isNotEmpty(tables))) { - assignTables() + tryCatch({ + datashield.assign.table(rconnections, symbol = symbol, table = logins, variables = variables, missings = missings, id.name = id.name) + }, error = function(e) { + warning("Resource assignmnt failed", call.=FALSE, immediate.=TRUE) + }) } - } .clearCache() From a865bd73fffd68e546f01aaf882b38d99b65ffff Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Sun, 12 Oct 2025 18:29:05 +0200 Subject: [PATCH 4/9] feat: display state of each R server --- R/datashield.sessions.R | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/R/datashield.sessions.R b/R/datashield.sessions.R index 09a8ac8..33ba371 100644 --- a/R/datashield.sessions.R +++ b/R/datashield.sessions.R @@ -73,12 +73,13 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err names(completed) <- names(fconns) checks <- 1 while (!all(completed)) { + messages <- c() for (n in names(fconns)) { if (!completed[[n]]) { if (!.hasLastErrors(n)) { tryCatch({ - msg <- dsStateMessage(sessions[[n]]) - .updateProgress(pb, step = length(subset(completed, completed == TRUE)), total = length(fconns), tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) + msg <- paste0(fconns[[n]]@name, ": ", dsStateMessage(sessions[[n]])) + messages <- append(messages, msg) if(async[[n]]) { completed[[n]] <- dsIsReady(sessions[[n]]) if (completed[[n]]) { @@ -107,7 +108,7 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err } } if (!all(completed)) { - .updateProgress(pb, step = length(subset(completed, completed == TRUE)), total = length(fconns), tokens = list(what = paste0("Waiting for R sessions to be ready..."))) + .updateProgress(pb, step = length(subset(completed, completed == TRUE)), total = length(fconns), tokens = list(what = paste(messages, collapse = ", "))) Sys.sleep(.getSleepTime(checks)) checks <- checks + 1 } From 31bf6106136c8cc4cb69537deb0c26edc1d5633b Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Wed, 15 Oct 2025 08:42:16 +0200 Subject: [PATCH 5/9] feat: ensure backward compatibility with older DSI implementations (ie not having session support) --- R/datashield.sessions.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/datashield.sessions.R b/R/datashield.sessions.R index 33ba371..326cc30 100644 --- a/R/datashield.sessions.R +++ b/R/datashield.sessions.R @@ -32,8 +32,8 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err .clearLastErrors() if (is.list(conns)) { - # filter conns not having connection - fconns <- Filter(function(conn) { !dsHasSession(conn) }, conns) + # filter conns supporting session API and not having connection + fconns <- Filter(function(conn) { !is.null(dsIsAsync(conn)$session) && !dsHasSession(conn) }, conns) if (length(fconns) == 0) { return(invisible(NULL)) } @@ -114,7 +114,7 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err } } ignore <- .tickProgress(pb, tokens = list(what = paste0("All R sessions ready"))) - } else { + } else if (!is.null(dsIsAsync(conns)$session)) { tryCatch({ if (!dsHasSession(conns)) { dsSession(conns, async = FALSE) From e5c4df9d6aa2c7340ba5d43f528be8c75af55e9d Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Wed, 15 Oct 2025 09:47:54 +0200 Subject: [PATCH 6/9] fix: typo --- R/datashield.login.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/datashield.login.R b/R/datashield.login.R index 187e223..c092245 100644 --- a/R/datashield.login.R +++ b/R/datashield.login.R @@ -237,14 +237,14 @@ datashield.login <- function(logins=NULL, assign=FALSE, variables=NULL, missings tryCatch({ datashield.assign.resource(rconnections, symbol = symbol, resource = logins) }, error = function(e) { - warning("Resource assignmnt failed", call.=FALSE, immediate.=TRUE) + warning("Resource assignment failed", call.=FALSE, immediate.=TRUE) }) } if (all(isNotEmpty(tables))) { tryCatch({ datashield.assign.table(rconnections, symbol = symbol, table = logins, variables = variables, missings = missings, id.name = id.name) }, error = function(e) { - warning("Resource assignmnt failed", call.=FALSE, immediate.=TRUE) + warning("Resource assignment failed", call.=FALSE, immediate.=TRUE) }) } } From 74e61bf3020c5654648858f3cb71b27cf157d31a Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Wed, 15 Oct 2025 09:49:42 +0200 Subject: [PATCH 7/9] fix: ensure R sessions before perfoming resource assignment --- R/datashield.assign.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/datashield.assign.R b/R/datashield.assign.R index 19c05e4..4ddb7bd 100644 --- a/R/datashield.assign.R +++ b/R/datashield.assign.R @@ -270,6 +270,7 @@ datashield.assign.table <- function(conns, symbol, table, variables=NULL, missin #' } #' @export datashield.assign.resource <- function(conns, symbol, resource, async=TRUE, success=NULL, error=NULL, errors.print = getOption("datashield.errors.print", FALSE)) { + datashield.sessions(conns) .clearLastErrors() if (is.null(resource) || length(resource) == 0) { stop("Not a valid resource name", call.=FALSE) From 08d9581f776d8b8aa8c1a29d6c3d3a0aa5eefe41 Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Sat, 25 Oct 2025 08:08:38 +0200 Subject: [PATCH 8/9] chore: update roxygen --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index efa2d60..fff8a59 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -36,7 +36,7 @@ License: LGPL (>= 2.1) URL: https://github.com/datashield/DSI/, https://datashield.github.io/DSI/, https://datashield.org/ BugReports: https://github.com/datashield/DSI/issues -RoxygenNote: 7.3.2 +RoxygenNote: 7.3.3 Encoding: UTF-8 Collate: 'DSObject.R' From da2a359971ca28fbcccf14be539164a967bb1666 Mon Sep 17 00:00:00 2001 From: Yannick Marcon Date: Sat, 25 Oct 2025 15:35:45 +0200 Subject: [PATCH 9/9] fix: update progress when not async session creation --- R/datashield.sessions.R | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/R/datashield.sessions.R b/R/datashield.sessions.R index 326cc30..bffd2d8 100644 --- a/R/datashield.sessions.R +++ b/R/datashield.sessions.R @@ -39,13 +39,13 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err } sessions <- list() - async <- lapply(fconns, function(conn) { ifelse(async, dsIsAsync(conn)$session, FALSE) }) + asyncs <- lapply(fconns, function(conn) { ifelse(async, dsIsAsync(conn)$session, FALSE) }) pb <- .newProgress(total = 1 + length(fconns)) # async first for (n in names(fconns)) { - if(async[[n]]) { + if(asyncs[[n]]) { tryCatch({ - sessions[[n]] <- dsSession(fconns[[n]], async=TRUE) + sessions[[n]] <- dsSession(fconns[[n]], async=async) }, error = function(e) { .appendError(n, conditionMessage(e)) if (.is.callback(error)) { @@ -56,7 +56,7 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err } # not async (blocking calls) for (n in names(fconns)) { - if(!async[[n]]) { + if(!asyncs[[n]]) { tryCatch({ .tickProgress(pb, tokens = list(what = paste0("Session ", fconns[[n]]@name))) sessions[[n]] <- dsSession(fconns[[n]], async=FALSE) @@ -80,14 +80,13 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err tryCatch({ msg <- paste0(fconns[[n]]@name, ": ", dsStateMessage(sessions[[n]])) messages <- append(messages, msg) - if(async[[n]]) { + if(asyncs[[n]]) { completed[[n]] <- dsIsReady(sessions[[n]]) if (completed[[n]]) { - .tickProgress(pb, tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) + .tickProgress(pb, tokens = list(what = msg)) } } else { completed[[n]] <- TRUE - .tickProgress(pb, tokens = list(what = paste0(fconns[[n]]@name, ": ", msg))) } if (completed[[n]] && .is.callback(success)) { success(n) @@ -117,7 +116,7 @@ datashield.sessions <- function(conns, async=TRUE, success=NULL, error=NULL, err } else if (!is.null(dsIsAsync(conns)$session)) { tryCatch({ if (!dsHasSession(conns)) { - dsSession(conns, async = FALSE) + dsSession(conns, async=FALSE) } if (.is.callback(success)) { success(conns@name)