opensrp / fhircore

FHIR Core / OpenSRP 2 is a Kotlin application for delivering offline-capable, mobile-first healthcare project implementations from local community to national and international scale using FHIR and WHO Smart Guidelines on Android.
https://smartregister.org
Apache License 2.0
50 stars 39 forks source link

Add Support for ImplementationGuide to Define Resources Required by an App #3150

Closed qiarie closed 2 months ago

qiarie commented 3 months ago

Describe the feature request. In line with configs versioning issue and the subsequent discussion, add support for ImplementationGuide resource to define the configs that define an OpenSRP 2 app.

Additional context OpenSRP 2 is being updated with new features and improvements in line with project requirements and organic maturity of the platform. Updates to OpenSRP 2 may require changes to definitions in app configurations.

In line with this, there is a need to ensure compatibility between FHIR configs downloaded from the server and the version of the OpenSRP 2 app running on a device.

Acceptance criteria

  1. OpenSRP 2 should support definition of the full set of app configs using either a Composition or ImplementationGuide (IG) resource
    • If an IG is available, the app should retrieve the composition resource referenced in definition.resource.reference. The app should then retrieve all configs referenced to the composition resource
    • If an IG is not defined, the app should look for a composition resource and retrieve referenced resources
    • If both IG and composition resources are not available, the app should fail with a message to the user

Implementation plan (For Engineers)

  1. Add an IG that references the existing composition config with at least the fields below:
    • url - globally unique URI for the implementation guide
      • Configure the IG URI as ImplementationGuide/{unique-identifier}
    • version - sequential version number of the config
    • name - computer friendly name for the IG. Have the appId in this field and use it to retrieve the IG
    • title - Human friendly name for the IG
    • status - publication status of the IG (draft, active, retired, unknown)
    • packageId - package name for the IG
    • fhirVersion - FHIR version(s) the IG targets
    • useContext.valueRange - a range of lowest and highest supported APK versions codes. Using the version code over the SEMVER version simplifies filtering by range when fetching from the server
    • definition.resource - a reference to the existing composition resource
  2. Update OpenSRP to support syncing using both IG and composition configs
    • For apps that do not have an IG, follow the current sync flow using the composition config
    • For apps that have an IG configured:
      • Fetch the highest version of the IG config from the server whose useContext range applies for the app’s version.
      • Use the composition config referenced in the IG and follow the standard sync using composition config.
    • In cases where both an IG and a composition config are defined for an app, the IG takes precedence over the composition. The flow in (ii) applies.
    • If both IG and composition resources are not available, the app should fail with a message to the user
  3. Documentation on how to set IG’s version and useContext range values

    • The IG useContext value should be a range of the app's supported version codes. Using version codes over the app's semantic version allows us to more easily filter from the server by range.

      • useContext.valueRange.low - the minimum supported version code. Skip this value if the support starts from the earliest version
      • useContext.valueRange.high - the maximum supported version code. Skip this value if the support starts from low and supports every version above that

      Sample ImplementationGuide

      {
        "resourceType": "ImplementationGuide",
        "id": "12967310",
        "url": "ImplementationGuide/fd76e175-a68a-436c-896e-e825a3970d2a",
        "version": "0",
        "name": "appId",
        "title": "Quest Implementation Guide",
        "status": "draft",
        "packageId": "org.smartregister.fhircore",
        "date": "2024-04-01",
        "publisher": "Ona Systems, Inc.",
        "fhirVersion": [
          {
            "code": {
              "coding": [
                {
                  "system": "http://hl7.org/fhir/FHIR-version",
                  "code": "4.3.0",
                  "display": "4.3.0"
                }
              ],
              "text": "FHIR Release 4B."
            }
          }
        ],
        "useContext": {
          "code": {
            "coding": [
              {
                "system": "http://terminology.hl7.org/CodeSystem/usage-context-type",
                "code": "program",
                "display": "Program"
              }
            ],
            "text": "Program"
          },
          "valueRange": {
            "low": {
              "value": 1
            },
            "high": {
              "value": 10
            }
          }
        },
        "definition": {
          "resource": [
            {
              "reference": {
                "reference": "Composition/8294"
              }
            }
          ]
        }
      }
pld commented 3 months ago

@qiarie can you please update or remove the Additional context, Acceptance criteria, and Area path

pld commented 3 months ago

For (1.) this is to add in tests and the docs, no where in the code, is that correct?

For (2.) what happens when there are >1 IGs with the same app ID, version, and useContext.valueRange?

For (1.), (2.), and (3.) can you describe how to define the IG .url so that the client knows how to construct that URL from the app ID?

qiarie commented 3 months ago

For (1.) this is to add in tests and the docs, no where in the code, is that correct?

For (2.) what happens when there are >1 IGs with the same app ID, version, and useContext.valueRange?

For (1.), (2.), and (3.) can you describe how to define the IG .url so that the client knows how to construct that URL from the app ID?

  1. Yes, for documentation purposes
  2. The composition resource is currently referenced using the appId (stored in the identifier field). The IG does not have an identifier field. We propose to use a IG URL reference in the application config.
  3. Update the instructions with this information
pld commented 3 months ago

@qiarie for (2.) the question still stands, what happens if there are >1 IGs with the same app ID (as defined in the URL), version, and useContext.valueRange?

qiarie commented 3 months ago

@pld The IG URL field is meant to be globally unique. From discussions, we'd agreed to create a copy of the IG with a different identifier (URL in this case) for updates. This way the conflict of multiple IGs for the same app is resolved. The IG URL in the application config would then be updated to the new one.

pld commented 3 months ago

Is the global uniqueness enforced, i.e. if you to a POST/PUT with a duplicate URL is it rejected?

qiarie commented 3 months ago

@pld I will run a confirmation test and update

qiarie commented 3 months ago

@pld It is possible to post multiple IGs with the same URL. We can have the URL reference the app and query with it. We can then combine the URL with the version to fetch the specific IG for the app version.

ellykits commented 2 months ago

@qiarie How do you intend to include the IG URL in ApplicationConfiguration that is referenced via the Composition resource? The ApplicationConfiguration is only downloaded once the Composition resource is synced.

qiarie commented 2 months ago

@pld @ellykits After further discussions we've settled on using the name field for the appId in the absence of an identifier field. We are then able to query the IG by the appId and filter for the supported versions in the server request.

qiarie commented 2 months ago

Additionally, we can use ImplementationGuide.version field to store the version of the application that is supported by the IG. Each app version will have one IG.

The version field is also indexed by the SDK the database.

hilpitome commented 2 months ago

To use the useContext ranges to search for matching Implementation guide(s) we will add prefixes to the search parameter values. From playing around in postman, prefix ranges would serve to find the intersecting set of IGs that will in the HAPI FHIR server. e.g if we have two IGs with the following useContexts

    "useContext": [
                    {
                        "valueRange": {
                            "low": {
                                "value": 10
                            },
                            "high": {
                                "value": 15
                            }
                        }
                    }
                ]
    "useContext": [
                    {
                        "valueRange": {
                            "low": {
                                "value": 13
                            },
                            "high": {
                                "value": 20
                            }
                        }
                    }
                ]

The request below will bring back both resources. {{base_url}}/ImplementationGuide?name={{appId}}&context-quantity=ge12&context-quantity=le17 while the following will return or include the IG with low value 13 and high value 20 {{base_url}}/ImplementationGuide?name={{appId}}&context-quantity=ge16&context-quantity=le25

To visualize

10                  15
|--------------------|
    12                       17
    |-------------------------|
           13                         20 
            |-------------------------|
hilpitome commented 2 months ago

Since the above does not pick out a single IG, we can filter using _sort and ensure we get the top one using count like in the this example ?name={{appId}}&context-quantity=le20&_sort=-context-quantity&_count=1