fhircore icon indicating copy to clipboard operation
fhircore copied to clipboard

Load resources like Questionnaire, StructureMap,PlanDefinition from resources folder when using appId with `/debug`

Open Raynafs opened this issue 1 year ago • 10 comments

IMPORTANT: Where possible all PRs must be linked to a Github issue

Fixes #3041

Engineer Checklist

  • [ ] I have written Unit tests for any new feature(s) and edge cases for bug fixes
  • [ ] I have added any strings visible on UI components to the strings.xml file
  • [ ] I have updated the CHANGELOG.md file for any notable changes to the codebase
  • [ ] I have run ./gradlew spotlessApply and ./gradlew spotlessCheck to check my code follows the project's style guide
  • [ ] I have built and run the FHIRCore app to verify my change fixes the issue and/or does not break the app
  • [ ] I have checked that this PR does NOT introduce breaking changes that require an update to Content and/or Configs? If it does add a sample here or a link to exactly what changes need to be made to the content.

Code Reviewer Checklist

  • [ ] I have verified Unit tests have been written for any new feature(s) and edge cases
  • [ ] I have verified any strings visible on UI components are in the strings.xml file
  • [ ] I have verifed the CHANGELOG.md file has any notable changes to the codebase
  • [ ] I have verified the solution has been implemented in a configurable and generic way for reuseable components
  • [ ] I have built and run the FHIRCore app to verify the change fixes the issue and/or does not break the app

Raynafs avatar Feb 13 '24 15:02 Raynafs

Codecov Report

Attention: Patch coverage is 50.00000% with 12 lines in your changes are missing coverage. Please review.

Project coverage is 28.0%. Comparing base (ac82739) to head (7a05d5d). Report is 69 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main   #3065     +/-   ##
=========================================
- Coverage     29.6%   28.0%   -1.7%     
- Complexity     658     693     +35     
=========================================
  Files          239     266     +27     
  Lines        11204   12472   +1268     
  Branches      1948    2172    +224     
=========================================
+ Hits          3323    3495    +172     
- Misses        7447    8518   +1071     
- Partials       434     459     +25     
Flag Coverage Δ
engine 64.7% <50.0%> (-1.5%) :arrow_down:
geowidget 18.7% <ø> (-28.5%) :arrow_down:
quest 5.2% <ø> (-0.3%) :arrow_down:

Flags with carried forward coverage won't be shown. Click here to find out more.

Files Coverage Δ
...core/engine/configuration/ConfigurationRegistry.kt 65.9% <50.0%> (-2.1%) :arrow_down:

... and 10 files with indirect coverage changes

codecov[bot] avatar Feb 13 '24 15:02 codecov[bot]

You can create your method in configurationsRegistry to retrieve any resource. Here's a sample method you can use

 inline fun <reified T : Resource> retrieveResourcesFromConfigMap(resourceId: String): T {
    return configsJsonMap.getValue(resourceId).decodeResourceFromString()
  }

and now invoke it whenever a resource is to be loaded from the configMap in debug mode. E.g Questionnaire, StructureMap, PlanDefinition, List.

SebaMutuku avatar Feb 26 '24 16:02 SebaMutuku

Hi Seb. Yes sure. working on it

Raynafs avatar Feb 27 '24 06:02 Raynafs

@SebaMutuku Did you verify if the implementation follows the directory structure required by FHIRCore tools as proposed before?

ellykits avatar Mar 14 '24 07:03 ellykits

@ellykits yes, the resources will be loaded from a folder inside the configs called resources. Inside this folder we will have sub-directorys e.g a sub-directory called questionnaire, structuremap, planDefinition which will hold corresponding resources

SebaMutuku avatar Mar 19 '24 08:03 SebaMutuku

@Raynafs is there anything pending on this PR. Can we close?

SebaMutuku avatar Mar 26 '24 11:03 SebaMutuku

Hey @SebaMutuku I believe I have completed the full implementation for this. Unless @ellykits would like me to add something.

Raynafs avatar Mar 27 '24 08:03 Raynafs

@ellykits This is a video showing that the resources have been loaded from the resources folder when the app Id is app/debug

Screencast from 03-04-2024 11:01:01 ASUBUHI.webm

Here is a video showing that the questionnaire that is in assets actually appears in the app

Screencast from 03-04-2024 11:08:16 ASUBUHI.webm

Raynafs avatar Apr 03 '24 08:04 Raynafs

@ellykits @pld this implementation is now complete. However we need to make changeds on FhirEngine at this function

 private suspend fun insertResource(resource: Resource, lastUpdatedLocal: Instant?): UUID {
    val resourceUuid = UUID.randomUUID()

    // Use the local UUID as the logical ID of the resource
    if (resource.id.isNullOrEmpty()) {
      resource.id = resourceUuid.toString()
    }

    val entity =
      ResourceEntity(
        id = 0,
        resourceType = resource.resourceType,
        resourceUuid = resourceUuid,
        resourceId = resource.logicalId,
        serializedResource = iParser.encodeResourceToString(resource),
        versionId = resource.versionId,
        lastUpdatedRemote = resource.lastUpdated,
        lastUpdatedLocal = lastUpdatedLocal,
      )
    insertResource(entity)

    val index =
      ResourceIndices.Builder(resourceIndexer.index(resource))
        .apply {
          lastUpdatedLocal?.let {
            addDateTimeIndex(
              createLocalLastUpdatedIndex(entity.resourceType, InstantType(Date.from(it))),
            )
          }
        }
        .build()

    updateIndicesForResource(index, resource.resourceType, resourceUuid)

    return entity.resourceUuid
  }

to reuse the same resourceUUID if it doesn't exist on the DB and the resource has a resourceID. Currently, the function above checks if the resource logicalID/resourceID exists in resourcesEntity table and if not generates a random UUID which is used to save the resource there.

Here's the function that checks that for both Remote and Local entries

  private suspend fun insertRemoteResource(resource: Resource): UUID {
    val existingResourceEntity = getResourceEntity(resource.logicalId, resource.resourceType)
    if (existingResourceEntity != null) {
      applyRemoteUpdate(resource)
      return existingResourceEntity.resourceUuid
    }
    return insertResource(resource, null)
  }

I can proceed to do that

cc @f-odhiambo

SebaMutuku avatar May 15 '24 16:05 SebaMutuku

nice, exciting!

pld avatar May 16 '24 02:05 pld

@ellykits @pld this implementation is now complete. However we need to make changeds on FhirEngine at this function

 private suspend fun insertResource(resource: Resource, lastUpdatedLocal: Instant?): UUID {
    val resourceUuid = UUID.randomUUID()

    // Use the local UUID as the logical ID of the resource
    if (resource.id.isNullOrEmpty()) {
      resource.id = resourceUuid.toString()
    }

    val entity =
      ResourceEntity(
        id = 0,
        resourceType = resource.resourceType,
        resourceUuid = resourceUuid,
        resourceId = resource.logicalId,
        serializedResource = iParser.encodeResourceToString(resource),
        versionId = resource.versionId,
        lastUpdatedRemote = resource.lastUpdated,
        lastUpdatedLocal = lastUpdatedLocal,
      )
    insertResource(entity)

    val index =
      ResourceIndices.Builder(resourceIndexer.index(resource))
        .apply {
          lastUpdatedLocal?.let {
            addDateTimeIndex(
              createLocalLastUpdatedIndex(entity.resourceType, InstantType(Date.from(it))),
            )
          }
        }
        .build()

    updateIndicesForResource(index, resource.resourceType, resourceUuid)

    return entity.resourceUuid
  }

to reuse the same resourceUUID if it doesn't exist on the DB and the resource has a resourceID. Currently, the function above checks if the resource logicalID/resourceID exists in resourcesEntity table and if not generates a random UUID which is used to save the resource there.

Here's the function that checks that for both Remote and Local entries

  private suspend fun insertRemoteResource(resource: Resource): UUID {
    val existingResourceEntity = getResourceEntity(resource.logicalId, resource.resourceType)
    if (existingResourceEntity != null) {
      applyRemoteUpdate(resource)
      return existingResourceEntity.resourceUuid
    }
    return insertResource(resource, null)
  }

I can proceed to do that

cc @f-odhiambo

This was resolved

SebaMutuku avatar May 20 '24 07:05 SebaMutuku

I have refactored some lines of code. I've also corrected the file names for the resources directory. The directory should be inside the configs/appId.

ellykits avatar May 31 '24 13:05 ellykits