Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Client Side] implement the practitioner details endpoint. #857

Closed
5 tasks done
Tracked by #950 ...
dubdabasoduba opened this issue Dec 10, 2021 · 58 comments · Fixed by #890
Closed
5 tasks done
Tracked by #950 ...

[Client Side] implement the practitioner details endpoint. #857

dubdabasoduba opened this issue Dec 10, 2021 · 58 comments · Fixed by #890
Assignees
Labels
Enhancement New feature or request Practitioner Details Size - M 4-5 days

Comments

@dubdabasoduba
Copy link
Member

dubdabasoduba commented Dec 10, 2021

Is your feature request related to a problem? Please describe.

Describe the solution you'd like
Getting data

  • Implement a GET of the practitioner details fetch endpoint as the 1st endpoint call after a user performs a remote login.
    • The endpoint parameter should be the user/practitioner id from keycloak. i.e the keycloak uuid

Saving data

  • Currently, we have some keycloak response saved in the secure shared preferences. Update this to save all the keycloak info included in the practitioner details fetch endpoint response.
  • Save all the resources in the payload mentioned above in the ResourceEntity table.

Using data

  • Write helper methods to fetch the keycloak related data from the secure shared preferences
    - Should be able to get all demographics
    - Should be able to get all the roles assigned to the user.
  • Write helper a method to fetch all the practitioner-related resources.
    - This can be generic i.e give it the practitioner-id and resource type then it returns the exact resource required.

Additional context

@pld
Copy link
Member

pld commented Dec 13, 2021

Is there anything needed here that we can architect to be reusable in a supervisor app that fetches the details of a set of practitioners?

@RaaziaTarique
Copy link
Member

RaaziaTarique commented Dec 24, 2021

We tried the possibility to add a custom UserDetails Resource class

import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ResourceType;

public class UserDetails extends Resource {
  @Override
  public ResourceType getResourceType() {
    return ResourceType.Basic;
  }
}

But you see we need to extend getResourceType() method and it can only use ResourceType which is only available in the Fhir library. Even if we want make modifications into extending resource type we need to use pre-defined ResourceType.

Screenshot 2021-12-24 at 1 09 42 PM

@dubdabasoduba @rehammuzzamil @maimoonak

@rehammuzzamil
Copy link

We tried the possibility to add a custom UserDetails Resource class

import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ResourceType;

public class UserDetails extends Resource {
  @Override
  public ResourceType getResourceType() {
    return ResourceType.Basic;
  }
}

But you see we need to extend getResourceType() method and it can only use ResourceType which is only available in the Fhir library. Even if we want make modifications into extending resource type we need to use pre-defined ResourceType.

Screenshot 2021-12-24 at 1 09 42 PM

@dubdabasoduba @rehammuzzamil @maimoonak

@RaaziaTarique As discussed, we need to register our custom Resource Provider in the FHIR. On the server-side, we achieve this by creating a new class with the @ResourceDef annotation. Once the class is created, we need to register our new Resource Provider (custom class) and other supporting classes in the Restful server. Registration of Resource Provider can be done by using registerProvider(practitionerDetailsResourceProvider); method at the backend.

Following is the example of custom resource provider:


@ResourceDef(
        name = "practitioner-details",
        profile = "http://hl7.org/fhir/profiles/custom-resource")
public class PractitionerDetails extends Practitioner {

    @Child(
            name = "KeycloakUserDetails",
            type = {KeycloakUserDetails.class})
    @Description(
            shortDefinition = "Complete KeycloakUserDetails",
            formalDefinition = "KeycloakUserDetails")
    private KeycloakUserDetails keycloakUserDetails;

    @Child(
            name = "fhir",
            type = {FhirPractitionerDetails.class})
    @Description(
            shortDefinition = "Get data from FHIR Server",
            formalDefinition = "Get data from FHIR Server")
    private FhirPractitionerDetails fhirPractitionerDetails;

    @Override
    public Practitioner copy() {
        Practitioner practitioner = new Practitioner();
        Bundle bundle = new Bundle();
        List<Bundle.BundleEntryComponent> theEntry = new ArrayList<>();
        Bundle.BundleEntryComponent entryComponent = new Bundle.BundleEntryComponent();
        entryComponent.setResource(new Bundle());
        theEntry.add(entryComponent);
        bundle.setEntry(theEntry);
        this.copyValues(practitioner);
        return practitioner;
    }

    @Override
    public ResourceType getResourceType() {
        return ResourceType.Bundle;
    }
  //getters and setters for the child fields

}

Since the work is in progress. It is likely to be changed.
cc: @dubdabasoduba @maimoonak

@rehammuzzamil
Copy link

@RaaziaTarique The error may also occur due to the server-side work not being deployed yet. It is throwing a DataFormatException because the FHIR Server does not provide support of the end-point yet, hence it's not understandable by FHIR.
cc : @maimoonak @dubdabasoduba

@dubdabasoduba
Copy link
Member Author

@RaaziaTarique The error may also occur due to the server-side work not being deployed yet. It is throwing a DataFormatException because the FHIR Server does not provide support of the end-point yet, hence it's not understandable by FHIR. cc : @maimoonak @dubdabasoduba

This makes sense. @rehammuzzamil when can we have bits of this done?

@rehammuzzamil
Copy link

@RaaziaTarique The error may also occur due to the server-side work not being deployed yet. It is throwing a DataFormatException because the FHIR Server does not provide support of the end-point yet, hence it's not understandable by FHIR. cc : @maimoonak @dubdabasoduba

This makes sense. @rehammuzzamil when can we have bits of this done?

@dubdabasoduba we can get an initial API PR ready by EOD Friday.

@rehammuzzamil
Copy link

The server-side PR is ready for review opensrp/hapi-fhir-opensrp-extensions#12
(Unit tests will be added)
cc : @dubdabasoduba @f-odhiambo @maimoonak

@RaaziaTarique
Copy link
Member

The server-side PR is ready for review opensrp/hapi-fhir-opensrp-extensions#12 (Unit tests will be added) cc : @dubdabasoduba @f-odhiambo @maimoonak

I have used this PR's Resource classes and I am still getting same error as previous

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: org.smartregister.fhircore.anc, PID: 21132
    ca.uhn.fhir.parser.DataFormatException: Unknown resource name "practitioner-details" (this name is not known in FHIR version "R4")
        at ca.uhn.fhir.context.FhirContext.getResourceDefinition(FhirContext.java:547)
        at ca.uhn.fhir.context.FhirContext.getResourceDefinition(FhirContext.java:463)
        at ca.uhn.fhir.parser.ParserState$PreResourceState.enteringNewElement(ParserState.java:994)
        at ca.uhn.fhir.parser.ParserState.enteringNewElement(ParserState.java:136)
        at ca.uhn.fhir.parser.JsonParser.parseChildren(JsonParser.java:1076)
        at ca.uhn.fhir.parser.JsonParser.parseChildren(JsonParser.java:996)
        at ca.uhn.fhir.parser.JsonParser.parseChildren(JsonParser.java:1079)
        at ca.uhn.fhir.parser.JsonParser.parseChildren(JsonParser.java:1060)
        at ca.uhn.fhir.parser.JsonParser.parseChildren(JsonParser.java:996)
        at ca.uhn.fhir.parser.JsonParser.doParseResource(JsonParser.java:214)
        at ca.uhn.fhir.parser.JsonParser.doParseResource(JsonParser.java:196)
        at ca.uhn.fhir.parser.BaseParser.parseResource(BaseParser.java:603)
        at ca.uhn.fhir.parser.BaseParser.parseResource(BaseParser.java:651)
        at ca.uhn.fhir.parser.BaseParser.parseResource(BaseParser.java:661)
        at org.smartregister.fhircore.engine.auth.AccountAuthenticator.getPractitionerDetails(AccountAuthenticator.kt:189)
        at org.smartregister.fhircore.engine.ui.login.LoginViewModel$callPractitionerDetails$1.invokeSuspend(LoginViewModel.kt:113)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

My code for registering custom classes (currently a sample payload is being used as the server side PR is still not merged)

suspend fun getPractitionerDetails(keycloak_uuid: String): org.hl7.fhir.r4.model.Bundle {
  val iParser: IParser = FhirContext.forR4().newJsonParser()
  FhirContext.forR4().registerCustomType(PractitionerDetails::class.java)
  FhirContext.forR4().registerCustomType(FhirCareTeamExtension::class.java)
  FhirContext.forR4().registerCustomType(FhirOrganizationExtension::class.java)
  FhirContext.forR4().registerCustomType(FhirPractitionerDetails::class.java)
  FhirContext.forR4().registerCustomType(KeycloakUserDetails::class.java)
  FhirContext.forR4().registerCustomType(UserBioData::class.java)
  val qJson =
    context.assets.open("sample_practitionar_payload.json").bufferedReader().use { it.readText() }

  return iParser.parseResource(qJson) as org.hl7.fhir.r4.model.Bundle
}

CC: @rehammuzzamil @maimoonak @dubdabasoduba

@dubdabasoduba
Copy link
Member Author

@RaaziaTarique Please use this Parameter resources sample to hold the Practitioner resource linkage.

{
  "resourceType": "Parameters",
  "parameter": [
    {
      "name": "practitioner",
      "valueString": "003a78c1-a589-435d-8d28-12b3a77b471c", --- Practitioner identifier
      "part": [
        {
          "name": "Organization",
          "valueString": "214" 
        },
        {
          "name": "CareTeam",
          "valueString": "21"
        },
        {
          "name": "CareTeam",
          "valueString": "213"
        }
      ]
    }
  ]
}

  • In the part attribute we have an array of objects. The attributes in each object are to be used as follows

    • name -- this will hold the resource type
    • valueString -- This will hold the resource ID.
  • After the generation of this Parameter resource we will save it and get its ID.

  • We will then save this ID in the secure shared preferences under the tag practitioner_details

@RaaziaTarique
Copy link
Member

RaaziaTarique commented Feb 2, 2022

@f-odhiambo Library for Practitioner details API models is deployed but the API is currently working locally only. @rehammuzzamil needs to check logs inorder to investigate the reason of failure of the API on FHIR. Meanwhile I have removed the models that I copied in the project and used the ones that are deployed here https://oss.sonatype.org/content/repositories/snapshots/org/smartregister/fhir-common-utils/0.0.1-SNAPSHOT/. I will continue working on this after finalising issue #981

@RaaziaTarique
Copy link
Member

While saving the custom resources FhirCareTeamExtension, FhirOrganizationExtension and LocationHierarchy into the ResourceEntity table, I am getting the following error java.lang.ClassCastException: org.hl7.fhir.r4.model.CareTeam cannot be cast to org.smartregister.model.practitioner.FhirCareTeamExtension
java.lang.ClassCastException: class org.hl7.fhir.r4.model.Organization cannot be cast to class org.smartregister.model.practitioner.FhirOrganizationExtension because in PractitionerDetails we are using FhirCareTeamExtension, FhirOrganizationExtension and the server side response is returning list of Organization and CareTeam .
If I try to modify the the saved json response to return list of FhirCareTeamExtension, FhirOrganizationExtension then it throws an exception saying that FhirCareTeamExtension, FhirOrganizationExtension and LocationHierarchy are not recognised by FHIR version R4.
Right now either can change the PractitionerDetails class to use FHIR known resources or to modify FHIR SDK but for now I think it's better to use FHIR known resources .
@maimoonak @dubdabasoduba @rehammuzzamil CC: @f-odhiambo

@FikriMilano
Copy link
Collaborator

@dubdabasoduba
I notice there is 2 API call response that has the same response.

  • The first is from protocol/openid-connect/userinfo that returns this response:
{
  "sub": "b87ff3c2-cbc6-43e6-b753-a9620756f9e4",
  "email_verified": false,
  "realm_access": {
    "roles": [
      "realm-admin",
      "OPENMRS",
      "EDIT_KEYCLOAK_USERS",
      "VIEW_KEYCLOAK_USERS",
      "PLANS_FOR_USER",
      "offline_access",
      "uma_authorization",
      "ALL_EVENTS"
    ]
  },
  "organization": "105",
  "name": "Demo 16909",
  "groups": [
    "realm-admin",
    "OPENMRS",
    "EDIT_KEYCLOAK_USERS",
    "VIEW_KEYCLOAK_USERS",
    "PLANS_FOR_USER",
    "offline_access",
    "uma_authorization",
    "ALL_EVENTS"
  ],
  "preferred_username": "demo",
  "given_name": "Demo",
  "family_name": "16909"
}
{
  "KeycloakUserDetails": {
    "id": "de9ad01f-c485-4da4-9a81-284518e103c1",
    "user-bio": [
      {
        "identifier": "de9ad01f-c485-4da4-9a81-284518e103c1",
        "userName": "practitionera",
        "preferredName": "practitionera",
        "familyName": "Tester",
        "givenName": "Practitioner A",
        "emailVerified": "false"
      }
    ],
    "user-roles": [
      "ROLE_offline_access",
      "ROLE_default-roles-fhir-core",
      "ROLE_uma_authorization"
    ]
  }
}

At the current implementation #890, it's actually saving both, with different shared preference key.
This results in duplication and might potentially inconsistency between data.

So my question is.
Which one are we supposed to save into the shared preference?

@dubdabasoduba
Copy link
Member Author

@FikriMilano I think the first one was added as a stopgap since we didn't have the practitioner details.

I would say let's deprecate the first one. Let's also deprecate that API call. i.e we can have the following flow

  1. Login
  2. Hit the practitioner details then saving of that data takes over.

@FikriMilano
Copy link
Collaborator

Yes that would be ideal.

But from what I see, the first API call is the only way to get the keycloakUuid which is needed to call the practitioner details API.

@FikriMilano
Copy link
Collaborator

@dubdabasoduba
I think my question would be.
Where does a keycloakUuid supposed to come from? or generated

@dubdabasoduba
Copy link
Member Author

@FikriMilano good catch. is the payload returned after login? If not does login return anything else when successful?

@FikriMilano
Copy link
Collaborator

FikriMilano commented Aug 11, 2022

@dubdabasoduba
No, the payload is not returned after login.

After successful login it returns only these folks:

  1. accessToken
  2. tokenType
  3. refreshToken
  4. refreshExpiresIn
  5. expiresIn
  6. scope

So, no keycloakUuid.

@FikriMilano
Copy link
Collaborator

I think the first API call only supposed to return a working dummy keycloakUuid.
I believe it's time to have the real thing, given that each Practitioner supposed to have their own keycloak.

Perhaps something like:
After successful login, also returns matching Practitioner username keycloak.

We are using this endpoint for login protocol/openid-connect/token

@FikriMilano
Copy link
Collaborator

FikriMilano commented Aug 12, 2022

@dubdabasoduba @f-odhiambo
It's a different issue compared to above, but this ticket is blocked by opensrp/fhir-common-utils#5

EDIT: resolved

@FikriMilano FikriMilano added the Blocked Item is blocked by another prerequisite label Aug 12, 2022
@dubdabasoduba
Copy link
Member Author

@dubdabasoduba @f-odhiambo It's a different issue compared to above, but this ticket is blocked by opensrp/fhir-common-utils#5

@FikriMilano Do you mind explaining why the custom extension is needed? The Payload only has 2 custom data models i.e keycloak details and the LocationHeirarchy I think I am a bit lost by the Organization and CareTeam ones.

@dubdabasoduba
Copy link
Member Author

I think the first API call only supposed to return a working dummy keycloakUuid. I believe it's time to have the real thing, given that each Practitioner supposed to have their own keycloak.

Perhaps something like: After successful login, also returns matching Practitioner username keycloak.

We are using this endpoint for login protocol/openid-connect/token

This makes sense. It just seems like a duplication of effort to call both and return the keycloak details.

@FikriMilano
Copy link
Collaborator

@FikriMilano Do you mind explaining why the custom extension is needed? The Payload only has 2 custom data models i.e keycloak details and the LocationHeirarchy I think I am a bit lost by the Organization and CareTeam ones.

Yeah, we need those custom extension for parsing purpose.

Basically, any custom resource needs to be registered to the fhirParser.

And I wasn't able to access those 2 classes.

@FikriMilano
Copy link
Collaborator

This makes sense. It just seems like a duplication of effort to call both and return the keycloak details.

No worries.
Merging those 2 calls is actually better.

@FikriMilano FikriMilano removed the Blocked Item is blocked by another prerequisite label Aug 12, 2022
@FikriMilano
Copy link
Collaborator

FikriMilano commented Aug 15, 2022

@dubdabasoduba
Could you create a ticket for ther server side to track the merging of protocol/openid-connect/token and practitioner-details?keycloak-uuid= API call?

@dubdabasoduba
Copy link
Member Author

@FikriMilano I think we can have both the calls for now. I will be creating another issue to enhance this. In that issue we will remove the keycloak response from the practitioner details.

This protocol/openid-connect/token is a keycloak URL so merging it might be complicated.

@FikriMilano
Copy link
Collaborator

@dubdabasoduba
Thanks, that's ok.

I would request for https://keycloak-stage.smartregister.org/auth/realms/FHIR_Android/protocol/openid-connect/userinfo to return this keycloak 34f0d616-afc8-4446-ae87-bb60be4bdbc9
Since that's the keycloak we use for this payload
https://fhir.labs.smartregister.org/fhir/practitioner-details?keycloak-uuid=34f0d616-afc8-4446-ae87-bb60be4bdbc9

Also, in the payload I request for the Organization that we use is 105 because the current one is still empty/no resource to be shown.

@FikriMilano
Copy link
Collaborator

I don't think it's possible to update it on my end. So I need your help with those.

@dubdabasoduba
Copy link
Member Author

@dubdabasoduba Thanks, that's ok.

I would request for https://keycloak-stage.smartregister.org/auth/realms/FHIR_Android/protocol/openid-connect/userinfo to return this keycloak 34f0d616-afc8-4446-ae87-bb60be4bdbc9 Since that's the keycloak we use for this payload https://fhir.labs.smartregister.org/fhir/practitioner-details?keycloak-uuid=34f0d616-afc8-4446-ae87-bb60be4bdbc9

Also, in the payload I request for the Organization that we use is 105 because the current one is still empty/no resource to be shown.

@FikriMilano I don't think you need to request for the Organization. That should be part of the FHIR practitioner details endpoint. Which user are you using?

@FikriMilano
Copy link
Collaborator

@dubdabasoduba
This one 34f0d616-afc8-4446-ae87-bb60be4bdbc9.

What I mean is, this current Organization/136254 has no proper resource to be shown, like household etc.

That's why I request it to be updated to 105, which has that.

Screen Shot 2022-08-15 at 20 57 14

@FikriMilano
Copy link
Collaborator

FikriMilano commented Aug 15, 2022

Unless that's intentional, then it's ok.

@dubdabasoduba
Copy link
Member Author

@FikriMilano That's okay we can change the assignment from FHiR web

@dubdabasoduba
Copy link
Member Author

dubdabasoduba commented Aug 15, 2022

This issue is just handling the saving and reading of the practitioner-related data. After these ones are complete then the fetching of data based on what you save here will be completed

@FikriMilano
Copy link
Collaborator

Yeah that's right, thanks

@FikriMilano
Copy link
Collaborator

FikriMilano commented Aug 16, 2022

TODOs:

cc @f-odhiambo @dubdabasoduba

FikriMilano added a commit that referenced this issue Aug 17, 2022
We discussed to save the IDs directly into the
SharedPreference, this helps us to avoid too
many searches when using Parameters resource.

Refer to this discussion #857 (comment)
@abbeyseto
Copy link

abbeyseto commented Feb 9, 2023

@RaaziaTarique As discussed, we need to register our custom Resource Provider in the FHIR. On the server-side, we achieve this by creating a new class with the @ResourceDef annotation. Once the class is created, we need to register our new Resource Provider (custom class) and other supporting classes in the Restful server. Registration of Resource Provider can be done by using registerProvider(practitionerDetailsResourceProvider); method at the backend.

Following is the example of custom resource provider:


@ResourceDef(
        name = "practitioner-details",
        profile = "http://hl7.org/fhir/profiles/custom-resource")
public class PractitionerDetails extends Practitioner {

    @Child(
            name = "KeycloakUserDetails",
            type = {KeycloakUserDetails.class})
    @Description(
            shortDefinition = "Complete KeycloakUserDetails",
            formalDefinition = "KeycloakUserDetails")
    private KeycloakUserDetails keycloakUserDetails;

    @Child(
            name = "fhir",
            type = {FhirPractitionerDetails.class})
    @Description(
            shortDefinition = "Get data from FHIR Server",
            formalDefinition = "Get data from FHIR Server")
    private FhirPractitionerDetails fhirPractitionerDetails;

    @Override
    public Practitioner copy() {
        Practitioner practitioner = new Practitioner();
        Bundle bundle = new Bundle();
        List<Bundle.BundleEntryComponent> theEntry = new ArrayList<>();
        Bundle.BundleEntryComponent entryComponent = new Bundle.BundleEntryComponent();
        entryComponent.setResource(new Bundle());
        theEntry.add(entryComponent);
        bundle.setEntry(theEntry);
        this.copyValues(practitioner);
        return practitioner;
    }

    @Override
    public ResourceType getResourceType() {
        return ResourceType.Bundle;
    }
  //getters and setters for the child fields

}

Since the work is in progress. It is likely to be changed. cc: @dubdabasoduba @maimoonak

I am using hapi-fhir-jpaserver-starter, and I'm a bit confused about where these resources will be created in the folder structure. I don't seem to see resource definition files. @dubdabasoduba @FikriMilano

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request Practitioner Details Size - M 4-5 days
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants