Chapter 4 Registering the dsOMOP resources

The databases exist, but Opal does not yet know about them. This step creates, on each site, a DataSHIELD resource that tells the dsOMOP server package where the OMOP data lives and how to open it. It talks to the Opal REST API through R’s opalr package — no Docker needed here.

bash 4_resources/setup_resources.sh

That thin wrapper installs opalr if needed and runs the R script that does the work: 4_resources/create_resources.R. For each site it ensures a resource-only project omop_demo and (re)creates one resource named gibleed — the path omop_demo.gibleed the client logs into.

4.1 The resource format

A resource is an ordinary resourcer resource that the dsOMOP resolver matches by its format, omop.dbi.db. We register it through the dsOMOP provider with structured connection parameters; Opal stores the resolver-ready definition as a readable URL — the engine, host, port and database, with any non-default schemas as query parameters:

omop+dbi:<dbms>://<host>:<port>/<database>?cdm_schema=...&vocabulary_schema=...

For this demo each site’s resource is exactly:

omop+dbi:postgresql://omopdb:5432/omop?cdm_schema=cdm

The schema query parameters are optional and follow a simple rule:

  • Neither given → both the CDM and vocabulary tables are read from the engine’s default schema (PostgreSQL public, SQL Server dbo, Oracle the connecting user, …).
  • cdm_schema only → the vocabulary tables are assumed to sit alongside the CDM tables, so vocabulary_schema defaults to cdm_schema. That is the case here: everything lives in cdm.
  • Both given → the CDM and vocabulary tables are read from their respective schemas.

Two consequences worth highlighting:

  • host/port are Docker-internal. They are the alias omopdb and the in-container port 5432 from the previous chapter — not a published host port. The same resource definition is therefore valid on every site; each Rock session resolves omopdb to its own database.
  • Credentials are not in the URL. The DB user/password (postgres/postgres) are stored separately as the resource’s identity / secret.
  • Bound to the dsOMOP provider. Registering through the provider, rather than as a bare URL, also records which package backs the resource, so no separate package hint is needed.

In R, the script registers the resource through the dsOMOP provider, passing the connection details as structured parameters (site values and credentials are hardcoded to match steps 2–3):

library(opalr)
o <- opal.login("administrator", "password", "http://localhost:48080")
opal.resource_extension_create(
  o, "omop_demo", name = "gibleed",
  provider    = "dsOMOP",
  factory     = "postgresql",
  parameters  = list(host = "omopdb", port = 5432L,
                     database = "omop", cdm_schema = "cdm"),
  credentials = list(username = "postgres", password = "postgres"))
opal.logout(o)

4.2 A note on disclosure control

No DataSHIELD privacyControlLevel is set here, and it would make no difference if it were: dsOMOP does not read that option. The permissive/banana/avocado/… hierarchy is a dsBase mechanism. dsOMOP enforces its own statistical disclosure control on every response — the standard nfilter.* thresholds plus a mandatory, non-disableable identifier strip — so its operations, including extraction, behave the same at every level. The only prerequisite for extraction is that dsOMOP’s assign methods are published on the omop profile, which the image and easy-opal already handled in Chapter 2.

4.3 Verifying

Open any site (e.g. http://localhost:48080), log in as administrator / password, and under project omop_demo → Resources you should see a gibleed resource of type omop.dbi.db. With all three sites carrying that resource, we are ready to connect.