Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
35eb732
refactor: batch 1 client functions remove redundant server calls
timcadman Mar 10, 2026
9ba8796
Update client tests: replace MODULE 5 checks with ds.class verification
timcadman Mar 12, 2026
92c58ff
revert whitespace changes
timcadman Mar 13, 2026
9757545
added missing arg tests
timcadman Mar 16, 2026
f8d0ab4
test: check object creation
timcadman Mar 16, 2026
65b0844
test: add performance tests
timcadman Mar 16, 2026
f2ea79d
removed redundant existence checks, explicitly check silent return
timcadman Mar 16, 2026
20d1510
try: use performance version of dsBase to pass CI
timcadman Mar 30, 2026
261efe4
docs: added as author
timcadman Apr 11, 2026
db931b2
chore: bring in utils file from previous PR
timcadman Apr 13, 2026
d79d871
refactor: move checks to serverside
timcadman Apr 13, 2026
fa381c1
docs: ran document
timcadman Apr 13, 2026
2aa71ad
test: updated tests following refactor
timcadman Apr 13, 2026
d4eabb7
refactor: add back in class consistency checks
timcadman Apr 13, 2026
01d7066
refactor: don't return validity message
timcadman Apr 13, 2026
8d621f8
update dsBase testing tar
timcadman Apr 13, 2026
f2cb293
test: updated expectations with new return value
timcadman Apr 13, 2026
1b921ea
refactor: add classConsistencyCheck parameter, remove from ds.levels
timcadman Apr 13, 2026
a6d9f06
test: add performance tests for batch 2 functions
timcadman Apr 13, 2026
4db816e
docs: update roxygen for classConsistencyCheck, remove stale checks p…
timcadman Apr 13, 2026
836b469
removed rogue file
timcadman Apr 22, 2026
786773f
trigger CI
timcadman May 14, 2026
69fdef4
Merge branch 'v7.0-dev' of github.com:datashield/dsBaseClient into pe…
timcadman May 14, 2026
b692fdc
test: updated test expectation as now export two more functions from …
timcadman May 14, 2026
2621856
Merge branch 'v7.0-dev' of github.com:datashield/dsBaseClient into re…
timcadman May 14, 2026
d015814
docs: deleted file that shouldn't be there
timcadman May 14, 2026
addb723
added cli to description
timcadman May 14, 2026
b52c32a
Merge perf-batch-1 into refactor/perf-batch-2
timcadman Jun 1, 2026
4cc7a77
Revert orphaned cor/cov/mean/var/quantileMean arg tests
timcadman May 31, 2026
eb95b8b
Move ds.length wrong-class check from arg to smk test
timcadman May 31, 2026
b8f85e8
Move ds.dim wrong-class check from arg to smk test
timcadman May 31, 2026
68448fc
Move ds.names wrong-class check from arg to smk and fix message
timcadman May 31, 2026
c9ea2a8
Add ds.levels wrong-class smk check
timcadman May 31, 2026
3cfa2c2
Add ds.isNA wrong-class smk check
timcadman May 31, 2026
30096e2
Add ds.completeCases wrong-class smk check
timcadman May 31, 2026
d877e8c
Update ds.levels smk tests for new levelsDS return and fix wrong-clas…
timcadman May 31, 2026
3e25944
Fix as.symbol(paste0()) calls for refactored server functions
timcadman Jun 1, 2026
c2803bb
Update client functions for new dimDS/lengthDS/numNaDS list returns
timcadman Jun 1, 2026
9be2331
Fix checkClass async and dataFrameFill for missing columns
timcadman Jun 1, 2026
7086e5b
Replace expect_silent with expect_no_error for progress messages
timcadman Jun 1, 2026
130b6f5
Update test expectations for serverside validation and removed Validi…
timcadman Jun 1, 2026
6ddfce8
Revert disc-ds.levels test to commented-out state from v7.0-dev
timcadman Jun 1, 2026
31bc180
try: print errors to diagnose test failures
timcadman Jun 1, 2026
440c4bb
docs: added comment template to document class consistency checks
timcadman Jun 2, 2026
bcc9a94
Updated 'dsBase tar.gz'
StuartWheater Jun 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,16 @@ Depends:
R (>= 4.0.0),
DSI (>= 1.7.1)
Imports:
cli,
fields,
metafor,
meta,
ggplot2,
gridExtra,
data.table,
methods,
dplyr
dplyr,
cli
Suggests:
lme4,
httr,
Expand Down
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ export(ds.var)
export(ds.vectorCalc)
import(DSI)
import(data.table)
importFrom(DSI,datashield.connections_find)
importFrom(cli,cli_abort)
importFrom(stats,as.formula)
importFrom(stats,na.omit)
importFrom(stats,ts)
Expand Down
2 changes: 1 addition & 1 deletion R/checkClass.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
checkClass <- function(datasources=NULL, obj=NULL){
# check the class of the input object
cally <- call("classDS", obj)
classesBy <- DSI::datashield.aggregate(datasources, cally, async = FALSE)
classesBy <- DSI::datashield.aggregate(datasources, cally)
classes <- unique(unlist(classesBy))
for (n in names(classesBy)) {
if (!all(classes == classesBy[[n]])) {
Expand Down
27 changes: 2 additions & 25 deletions R/ds.abs.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#' the input numeric or integer vector specified in the argument \code{x}. The created vectors
#' are stored in the servers.
#' @author Demetris Avraam for DataSHIELD Development Team
#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands
#' @export
#' @examples
#' \dontrun{
Expand Down Expand Up @@ -72,41 +73,17 @@
#'
ds.abs <- function(x=NULL, newobj=NULL, datasources=NULL){

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}
datasources <- .set_datasources(datasources)

if(is.null(x)){
stop("Please provide the name of the input object!", call.=FALSE)
}

# check if the input object is defined in all the studies
isDefined(datasources, x)

# call the internal function that checks the input object is of the same class in all studies.
typ <- checkClass(datasources, x)

# call the internal function that checks the input object(s) is(are) of the same class in all studies.
if(!('numeric' %in% typ) && !('integer' %in% typ)){
stop("Only objects of type 'numeric' or 'integer' are allowed.", call.=FALSE)
}

# create a name by default if the user did not provide a name for the new variable
if(is.null(newobj)){
newobj <- "abs.newobj"
}

# call the server side function that does the operation
cally <- call("absDS", x)
DSI::datashield.assign(datasources, newobj, cally)

# check that the new object has been created and display a message accordingly
finalcheck <- isAssigned(datasources, newobj)

}
101 changes: 3 additions & 98 deletions R/ds.asCharacter.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
#' objects obtained after login. If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.asCharacter} returns the object converted into a class character
#' that is written to the server-side. Also, two validity messages are returned to the client-side
#' indicating the name of the \code{newobj} which has been created in each data source and if
#' it is in a valid form.
#' that is written to the server-side.
#' @examples
#' \dontrun{
#' ## Version 6, for version 5 see the Wiki
Expand Down Expand Up @@ -53,115 +51,22 @@
#'
#' }
#' @author DataSHIELD Development Team
#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands
#' @export
#'
ds.asCharacter <- function(x.name=NULL, newobj=NULL, datasources=NULL){

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}
datasources <- .set_datasources(datasources)

if(is.null(x.name)){
stop("Please provide the name of the input vector!", call.=FALSE)
}

# check if the input object is defined in all the studies
isDefined(datasources, x.name)

# create a name by default if user did not provide a name for the new variable
if(is.null(newobj)){
newobj <- "ascharacter.newobj"
}

# call the server side function that does the job

calltext <- call("asCharacterDS", x.name)

DSI::datashield.assign(datasources, newobj, calltext)


#############################################################################################################
#DataSHIELD CLIENTSIDE MODULE: CHECK KEY DATA OBJECTS SUCCESSFULLY CREATED #
#
#SET APPROPRIATE PARAMETERS FOR THIS PARTICULAR FUNCTION #
test.obj.name<-newobj #
# #
#
# CALL SEVERSIDE FUNCTION #
calltext <- call("testObjExistsDS", test.obj.name) #
#
object.info<-DSI::datashield.aggregate(datasources, calltext) #
#
# CHECK IN EACH SOURCE WHETHER OBJECT NAME EXISTS #
# AND WHETHER OBJECT PHYSICALLY EXISTS WITH A NON-NULL CLASS #
num.datasources<-length(object.info) #
#
#
obj.name.exists.in.all.sources<-TRUE #
obj.non.null.in.all.sources<-TRUE #
#
for(j in 1:num.datasources){ #
if(!object.info[[j]]$test.obj.exists){ #
obj.name.exists.in.all.sources<-FALSE #
} #
if(is.null(object.info[[j]]$test.obj.class) || ("ABSENT" %in% object.info[[j]]$test.obj.class)){ #
obj.non.null.in.all.sources<-FALSE #
} #
} #
#
if(obj.name.exists.in.all.sources && obj.non.null.in.all.sources){ #
#
return.message<- #
paste0("A data object <", test.obj.name, "> has been created in all specified data sources") #
#
#
}else{ #
#
return.message.1<- #
paste0("Error: A valid data object <", test.obj.name, "> does NOT exist in ALL specified data sources") #
#
return.message.2<- #
paste0("It is either ABSENT and/or has no valid content/class,see return.info above") #
#
return.message.3<- #
paste0("Please use ds.ls() to identify where missing") #
#
#
return.message<-list(return.message.1,return.message.2,return.message.3) #
#
} #
#
calltext <- call("messageDS", test.obj.name) #
studyside.message<-DSI::datashield.aggregate(datasources, calltext) #
#
no.errors<-TRUE #
for(nd in 1:num.datasources){ #
if(studyside.message[[nd]]!="ALL OK: there are no studysideMessage(s) on this datasource"){ #
no.errors<-FALSE #
} #
} #
#
#
if(no.errors){ #
validity.check<-paste0("<",test.obj.name, "> appears valid in all sources") #
return(list(is.object.created=return.message,validity.check=validity.check)) #
} #
#
if(!no.errors){ #
validity.check<-paste0("<",test.obj.name,"> invalid in at least one source. See studyside.messages:") #
return(list(is.object.created=return.message,validity.check=validity.check, #
studyside.messages=studyside.message)) #
} #
#
#END OF CHECK OBJECT CREATED CORECTLY MODULE #
#############################################################################################################


}
# ds.asCharacter
101 changes: 3 additions & 98 deletions R/ds.asDataMatrix.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
#' objects obtained after login. If the \code{datasources} argument is not specified
#' the default set of connections will be used: see \code{\link[DSI]{datashield.connections_default}}.
#' @return \code{ds.asDataMatrix} returns the object converted into a matrix
#' that is written to the server-side. Also, two validity messages are returned
#' to the client-side
#' indicating the name of the \code{newobj} which
#' has been created in each data source and if
#' it is in a valid form.
#' that is written to the server-side.
#' @examples
#' \dontrun{
#' ## Version 6, for version 5 see the Wiki
Expand Down Expand Up @@ -54,113 +50,22 @@
#'
#' }
#' @author DataSHIELD Development Team
#' @author Tim Cadman, Genomics Coordination Centre, UMCG, Netherlands
#' @export
#'
ds.asDataMatrix <- function(x.name=NULL, newobj=NULL, datasources=NULL){

# look for DS connections
if(is.null(datasources)){
datasources <- datashield.connections_find()
}

# ensure datasources is a list of DSConnection-class
if(!(is.list(datasources) && all(unlist(lapply(datasources, function(d) {methods::is(d,"DSConnection")}))))){
stop("The 'datasources' were expected to be a list of DSConnection-class objects", call.=FALSE)
}
datasources <- .set_datasources(datasources)

if(is.null(x.name)){
stop("Please provide the name of the input vector!", call.=FALSE)
}

# check if the input object is defined in all the studies
isDefined(datasources, x.name)

# create a name by default if user did not provide a name for the new variable
if(is.null(newobj)){
newobj <- "asdatamatrix.newobj"
}

# call the server side function that does the job
calltext <- call("asDataMatrixDS", x.name)
DSI::datashield.assign(datasources, newobj, calltext)


#############################################################################################################
#DataSHIELD CLIENTSIDE MODULE: CHECK KEY DATA OBJECTS SUCCESSFULLY CREATED #
#
#SET APPROPRIATE PARAMETERS FOR THIS PARTICULAR FUNCTION #
test.obj.name<-newobj #
# #
#
# CALL SEVERSIDE FUNCTION #
calltext <- call("testObjExistsDS", test.obj.name) #
#
object.info<-DSI::datashield.aggregate(datasources, calltext) #
#
# CHECK IN EACH SOURCE WHETHER OBJECT NAME EXISTS #
# AND WHETHER OBJECT PHYSICALLY EXISTS WITH A NON-NULL CLASS #
num.datasources<-length(object.info) #
#
#
obj.name.exists.in.all.sources<-TRUE #
obj.non.null.in.all.sources<-TRUE #
#
for(j in 1:num.datasources){ #
if(!object.info[[j]]$test.obj.exists){ #
obj.name.exists.in.all.sources<-FALSE #
} #
if(is.null(object.info[[j]]$test.obj.class) || ("ABSENT" %in% object.info[[j]]$test.obj.class)){ #
obj.non.null.in.all.sources<-FALSE #
} #
} #
#
if(obj.name.exists.in.all.sources && obj.non.null.in.all.sources){ #
#
return.message<- #
paste0("A data object <", test.obj.name, "> has been created in all specified data sources") #
#
#
}else{ #
#
return.message.1<- #
paste0("Error: A valid data object <", test.obj.name, "> does NOT exist in ALL specified data sources") #
#
return.message.2<- #
paste0("It is either ABSENT and/or has no valid content/class,see return.info above") #
#
return.message.3<- #
paste0("Please use ds.ls() to identify where missing") #
#
#
return.message<-list(return.message.1,return.message.2,return.message.3) #
#
} #
#
calltext <- call("messageDS", test.obj.name) #
studyside.message<-DSI::datashield.aggregate(datasources, calltext) #
#
no.errors<-TRUE #
for(nd in 1:num.datasources){ #
if(studyside.message[[nd]]!="ALL OK: there are no studysideMessage(s) on this datasource"){ #
no.errors<-FALSE #
} #
} #
#
#
if(no.errors){ #
validity.check<-paste0("<",test.obj.name, "> appears valid in all sources") #
return(list(is.object.created=return.message,validity.check=validity.check)) #
} #
#
if(!no.errors){ #
validity.check<-paste0("<",test.obj.name,"> invalid in at least one source. See studyside.messages:") #
return(list(is.object.created=return.message,validity.check=validity.check, #
studyside.messages=studyside.message)) #
} #
#
#END OF CHECK OBJECT CREATED CORECTLY MODULE #
#############################################################################################################


}
# ds.asDataMatrix
Loading