.. _integrations: Integrations ============ .. note:: This page describes what openFHIR Engine can integrate to for support in its mappings. If you're looking for instructions on how to invoke openFHIR Engine itself and how to integrate engine into your flow, see :ref:`architecture`. openEHR Repository ------------------ openFHIR Engine can be integrated with an existing openEHR Repository (CDR) for the purpose of getting operational templates needed for the mappings. In this case, operational templates do not need to be populated as part of the state configuration. Without this integration, every operational template has to be uploaded into the engine's own database (``POST /opt``) before any mapping can run, which means an operator running openFHIR next to an existing openEHR CDR maintains two copies of the same template and has to keep them in sync. Drift between the two is silent, and only surfaces later as a mapping failure. With the integration enabled, the CDR stays the single source of truth and openFHIR holds no template state of its own. .. note:: This is an enterprise-only feature. The open source engine always resolves templates from its own database. CDR-only mode ^^^^^^^^^^^^^ When the integration is enabled the engine runs in **CDR-only mode**. This is deliberately all-or-nothing: the local ``opt`` collection is not consulted for mapping reads at all, so that the engine can never silently serve a stale local copy of a template that has since changed in the CDR. Concretely, when CDR-only mode is on: - operational templates are fetched from the CDR on demand, for every mapping direction as well as for AQL generation, validation and Atlas, - ``POST /opt``, ``PUT /opt/{id}`` and ``DELETE /opt/{id}`` return ``409 Conflict`` — templates are owned by the CDR and must be loaded there directly. The engine is read-only towards the CDR and never pushes, updates or deletes a template in it, - ``GET /opt`` returns an empty array and ``GET /opt/{id}`` returns ``404``. Templates remain fully resolvable for mapping; they are simply not addressable through the engine's admin API. Query the CDR directly to list them, - ``*.opt`` files in the :ref:`bootstrap directory ` are skipped with a warning and reported with the outcome ``UNCHANGED``. The rest of the scan (model mappers, context mappers, concept maps) is unaffected, and ``GET /health`` still opens normally, - if the CDR is unreachable, a mapping request fails with a clear "CDR is unreachable" error rather than falling back to whatever happens to be stored locally. Configuration ^^^^^^^^^^^^^ The integration is disabled by default. It is configured under ``openfhir.cdr``: .. code-block:: yaml openfhir: cdr: enabled: true base-url: http://localhost:8081/ehrbase/rest/openehr/v1 auth: BASIC # NONE | BASIC | OAUTH2 username: ehrbase-user password: SuperSecretPassword .. list-table:: :header-rows: 1 :widths: 40 15 45 * - Environment variable - Default - Description * - ``OPENFHIR_CDR_ENABLED`` - ``false`` - Enables CDR-only mode. When ``false``, the engine behaves exactly as before and serves templates from its own database. * - ``OPENFHIR_CDR_BASE_URL`` - — - Base URL of the openEHR REST API, e.g. ``http://localhost:8081/ehrbase/rest/openehr/v1``. Required when enabled. * - ``OPENFHIR_CDR_AUTH`` - ``NONE`` - Authentication scheme: ``NONE``, ``BASIC`` or ``OAUTH2`` (client credentials). * - ``OPENFHIR_CDR_USERNAME`` / ``OPENFHIR_CDR_PASSWORD`` - — - Credentials used when ``auth`` is ``BASIC``. * - ``OPENFHIR_CDR_TOKEN_URL`` - — - Token endpoint used when ``auth`` is ``OAUTH2``. * - ``OPENFHIR_CDR_CLIENT_ID`` / ``OPENFHIR_CDR_CLIENT_SECRET`` - — - Client credentials used when ``auth`` is ``OAUTH2``. * - ``OPENFHIR_CDR_SCOPE`` - — - Optional scope requested with the client-credentials grant. * - ``OPENFHIR_CDR_CONNECT_TIMEOUT_MS`` - ``5000`` - Connection timeout, in milliseconds, when talking to the CDR. * - ``OPENFHIR_CDR_READ_TIMEOUT_MS`` - ``15000`` - Read timeout, in milliseconds, when talking to the CDR. Only ADL 1.4 operational templates are fetched, over ``GET {base-url}/definition/template/adl1.4/{template_id}``. Credentials and OAuth2 tokens are never written to the log. .. note:: CDR template ids are case-sensitive, while openFHIR normalizes template ids internally (lowercasing them and replacing spaces with underscores). The engine therefore sends the template id as written in your FHIR Connect context mapper (``context.template.id``), and only falls back to the normalized form if that is not found. Keep ``context.template.id`` identical to the template id in the CDR. Caching ^^^^^^^ Fetched templates go through the same OPT cache described in :doc:`caching`, which matters more here than in the local case: a single mapping request looks the same template up twice, so without the cache every request would cost two round-trips to the CDR. .. warning:: The default ``OPENFHIR_CACHE_OPT_TTL`` is ``-1``, meaning entries never expire and are only evicted when a template is upserted through the API. CDR-only mode has no local upsert, so a cached template would never refresh on its own. Set a finite ``OPENFHIR_CACHE_OPT_TTL`` when running in CDR-only mode, or evict explicitly with ``POST /opt/$refresh?templateId=...`` after a template changes in the CDR. The engine logs a warning at startup if CDR-only mode is on while the TTL is infinite. ``POST /opt/$refresh?templateId=...`` evicts a single template cluster-wide, so the next mapping request re-fetches it from the CDR. It returns ``204 No Content``, or ``409 Conflict`` when the engine is not in CDR-only mode (where templates are evicted automatically on upsert). Per-tenant configuration ^^^^^^^^^^^^^^^^^^^^^^^^ The block above is the global default. An individual tenant can point at a different CDR, or opt out of the integration entirely, through the ``cdr_opt_source`` tenant property — see :doc:`multitenancy`. Verifying the integration ^^^^^^^^^^^^^^^^^^^^^^^^^ ``GET /status`` reports the resolved configuration together with a liveness probe, so the wiring can be confirmed without firing a mapping request at the engine: .. code-block:: json { "cdr": { "enabled": true, "baseUrl": "http://localhost:8081/ehrbase/rest/openehr/v1", "reachable": true } } ``reachable`` is only probed when the integration is enabled. Terminology server ------------------ As describe in the :ref:`terminology`, an important aspect to efficient mapping is access to a terminology service providing bidirectional mappings between openEHR and FHIR terminologies. For this purpose, openFHIR Engine can be integrated with an external FHIR terminology server compliant with the following IG https://build.fhir.org/terminology-service.html Required operations that the external terminology server needs to support are: - $translate // todo