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

Error with Create Patient FHIR API payload in Firely .NET SDK #2657

Closed
fayazshaik07 opened this issue Jan 9, 2024 · 10 comments
Closed

Error with Create Patient FHIR API payload in Firely .NET SDK #2657

fayazshaik07 opened this issue Jan 9, 2024 · 10 comments

Comments

@fayazshaik07
Copy link

fayazshaik07 commented Jan 9, 2024

I'm trying to create a patient in the EPIC Sandbox using the below API Url
https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/Patient

I'm using the firely .Net Sdk for EPIC FHIR API Integration. I'm using the Patient.Create (R4).
In Postman, I'm able to create a patient using the below sample payload from the Sandbox environment itself.

{
"resourceType": "Patient",
"identifier": [
    {
        "use": "usual",
        "system": "urn:oid:2.16.840.1.113883.4.1",
        "value": "000-00-0000"
    }
],
"name": [
    {
        "use": "usual",
        "family": "family",
        "given": [
            "firstName",
            "lastName"
        ]
    }
],
"gender": "male",
"birthDate": "2000-01-01"
}

But the Firely SDK Patient Class generates the below payload:

{
"identifier": [
    {
        "system": {
            "value": "urn:oid:2.16.840.1.113883.4.1"
        },
        "value": {
            "value": "000-00-0000"
        }
    }
],
"name": [
    {
        "family": {
            "value": "family"
        },
        "given": [
            {
                "value": "firstName"
            },
            {
                "value": "lastName"
            }
        ]
    }
],
"gender": {
    "value": "male"
},
"birthDate": {
    "value": "2000-10-10"
}
}

Please find the code below(ignore the hardcodes) I'm using to create a patient

public Patient CreatePatient()
{
Patient newPatient = new Patient();

// Add identifier
newPatient.Identifier.Add(new Identifier
{
    Use = Identifier.IdentifierUse.Usual,
    System = "urn:oid:2.16.840.1.113883.4.1",
    Value = "000-00-0000"
});

// Add name
newPatient.Name.Add(new HumanName
{
    Use = HumanName.NameUse.Usual,
    Family = "family",
    Given = new string[] { "firstName", "lastName" }
});

// Set gender
newPatient.Gender = AdministrativeGender.Male;

// Set birth date
newPatient.BirthDate = "2000-10-10";

// Send FHIR request to create patient on the server
var response = _fhirService.Execute(client => client.Create(newPatient));

if (response is NewPatient patient)
{
    return patient;
}
else
{
    throw new Exception("Failed to create patient");
}
}

For resolving the issue, I tried the below code as well

        // Serialize the Patient instance to JSON
        var serializer = new FhirJsonSerializer();
        string json = serializer.SerializeToString(newPatient);

Still, I'm unable to create a patient as its giving the below error:

<OperationOutcome xmlns="http://hl7.org/fhir">
<issue>
    <severity value="fatal" />
    <code value="structure" />
    <diagnostics value="Failed to parse; structural issues in the content." />
    <expression value="$this" />
</issue>
@fayazshaik07 fayazshaik07 changed the title Error with Create Patient FHIR API payload in Firely .Net Sdk Error with Create Patient FHIR API payload in FIRELY .NET SDK Jan 9, 2024
@fayazshaik07 fayazshaik07 changed the title Error with Create Patient FHIR API payload in FIRELY .NET SDK Error with Create Patient FHIR API payload in Firely .NET SDK Jan 9, 2024
@mbaltus
Copy link
Member

mbaltus commented Jan 9, 2024

Can you explain how you got to display the serialized Patient? Because when I copy your code excluding the line with Execute and add a Console.WriteLine(json); after the serialization, it shows that the SDK serializes the Patient correctly.

This leads me to believe that the actual error is in your fhirService or its Execute method. Can you detail the code that is in the fhirService? Are you using the FhirClient to send the Patient to the Epic sandbox? Or maybe the display in the sandbox environment uses a wrong serializer instead of showing the raw data.

@fayazshaik07
Copy link
Author

fayazshaik07 commented Jan 9, 2024

yes the 'json' after string json = serializer.SerializeToString(newPatient); produces the correct payload for Create patient.
But, I'm unable to assingn it to the execute method added below :

        `// Send FHIR request to create patient on the server
        var response = _fhirService.Execute(client => client.Create(newPatient));`

I should be able to send the json in the place of 'newPatient' but it is expecting 'Patient' class object as parameter.
also, I tried deserealizing as below

          ` public Patient CreatePatient()
           {
            // Create a new Patient instance
           Patient newPatient = new Patient();

        // Add identifier
        newPatient.Identifier.Add(new Identifier
        {
            Use = Identifier.IdentifierUse.Usual,
            System = "urn:oid:2.16.840.1.113883.4.1",
            Value = "000-00-0000"
        });

        // Add name
        newPatient.Name.Add(new HumanName
        {
            Use = HumanName.NameUse.Usual,
            Family = "india",
            Given = new string[] { "ravindra", "jadeja" }
        });

        // Set gender
        newPatient.Gender = AdministrativeGender.Male;

        // Set birth date
        newPatient.BirthDate = "2000-10-10";

        // Serialize the Patient instance to JSON
        var serializer = new FhirJsonSerializer();
        string json = serializer.SerializeToString(newPatient);

        // Output the generated JSON
        Console.WriteLine(json);

        // Deserialize the JSON string back to a Patient object
        var parser = new FhirJsonParser();
        Patient deserializedPatient = parser.Parse<Patient>(json);

        // Send FHIR request to create patient on the server
        var response = _fhirService.Execute(client => client.Create(deserializedPatient));

        if (response is Patient patient)
        {
            return patient;
        }
        else
        {
            throw new Exception("Failed to create patient");
        }
    }`

still no luck

@fayazshaik07
Copy link
Author

fayazshaik07 commented Jan 9, 2024

Below is my FHIR Service code

         ` public FhirService()
            {
             // Acquire a token for the specified scopes
        var bearerToken = "someToken"; // Bearer token is added here while debugging

        // Instanciate the authorization handler
        var authHandler = new AuthorizationMessageHandler
        {
            Authorization = new AuthenticationHeaderValue("Bearer", bearerToken)
        };

        // Create client
        var settings = new FhirClientSettings
        {
            PreferredFormat = ResourceFormat.Json,
            PreferredReturn = Prefer.ReturnMinimal,
            Timeout = 10000,
            VerifyFhirVersion = true
        };
        string FhirUrl = "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4/";
        _fhirClient = new FhirClient(FhirUrl, settings, authHandler);
    }`

@mbaltus
Copy link
Member

mbaltus commented Jan 9, 2024

I don't see the Execute method in your FhirService. There must be something wrong in there. If I try it without a FhirService and just do

var cl = new FhirClient("http://localhost:4080");
cl.Create(newPatient);

everything works fine.

You could try and debug further by working with a public endpoint such as https://server.fire.ly/R4 or local FHIR server that you trust, if you suspect the epic sandbox not working correctly.

@fayazshaik07
Copy link
Author

fayazshaik07 commented Jan 10, 2024

Below is the Execute method in my Custom FHIR Service

       ` public T Execute<T>(Func<FhirClient, T> fhirFunction)
       {
        try
        {
            return fhirFunction(_fhirClient);
        }
        catch (FhirOperationException ex)
        {
            throw new Exception($"FHIR operation failed: {ex.Message}");
        }
        catch (Exception ex)
        {
            throw new Exception($"Failed to communicate with FHIR server: {ex.Message}");
        }
        }`

@mbaltus
Copy link
Member

mbaltus commented Jan 10, 2024

It works fine. And it actually does exactly as you have specified you wanted.
The reason that you do not get a Patient as the response, is because you have this setting in your FhirClient: PreferredReturn = Prefer.ReturnMinimal.
I overlooked that yesterday, because I was focusing on the serialization part and checking to see if a server would accept the request correctly. On my own server, I could see it returned a 201, so that was a confirmation that all was good and I did not look at the response further.
The setting asks the server to not send the result back, so your Execute method returns null, and your other code then thinks the Patient was not created successfully. Change the setting to ReturnPreference = ReturnPreference.Representation and you will get the created Patient sent back to you by the server.

If you do still get errors, like the OperationOutcome you mentioned in the beginning, there's something wrong on the server side.

@fayazshaik07
Copy link
Author

fayazshaik07 commented Jan 10, 2024

I'm not even able to create a Patient itself with my payload. May be now you have the total context of my problem, let me reiterate. I'm using the Firely .Net SDK (R4, Version 5.4.0.)
I have created few methods for getting the Patient from EPIC Sandbox, getting AllergyIntolerance, condition etc. All these APIs work perfectly with my code.
while I'm trying to create a Patient, It generated the below payload, with the code mentioned above

`{
"identifier": [
    {
        "system": {
            "value": "urn:oid:2.16.840.1.113883.4.1"
        },
        "value": {
            "value": "000-00-0000"
        }
    }
],
"name": [
    {
        "family": {
            "value": "india"
        },
        "given": [
            {
                "value": "ravindra"
            },
            {
                "value": "jadeja"
            }
        ]
    }
],
"gender": {
    "value": "male"
},
"birthDate": {
    "value": "2000-10-10"
}
}`

This payload has appended "value" before every property. When I use this payload, the API Returns 'unstructured format' error.

Now, as I mentioned above, I used FhirJsonSerializer , to serialize my payload. This now gives me a string (JSON) value with a mandatory input 'resourceType': 'Patient' included in the JSON.

`{
"resourceType": "Patient",
"identifier": [
    {
        "use": "usual",
        "system": "urn:oid:2.16.840.1.113883.4.1",
        "value": "000-00-0000"
    }
],
"name": [
    {
        "use": "usual",
        "family": "FHIRcreate",
        "given": [
            "Jones"
        ]
    }
],
"gender": "male",
"birthDate": "2000-01-01"
 }`

Now, I'm unable to pass this input to the Execute method as it expects a Patient type Resource. I tried DeSerializing the JSON to Patient Type again. It again appends the "value" before every property.

Do I need to include any ParseSettings in my FHIR Service to resolve this? I'm unable to do so. I cannot try any other open FHIR sources as we're considering EPIC Sandbox as standard and all other APIs work fine.

Please let me know if any further info is needed.

@mbaltus
Copy link
Member

mbaltus commented Jan 10, 2024

The Create method on the FhirClient does indeed expect a FHIR resource as argument, not a string. One reason is that it will use the resource type to setup the correct URL to send the resource to. Then with the other settings the SDK's serialization is used to create the actual payload.

I do not know how you get the serialization of the payload that you show in the first block, with the value attributes in it. That is not coming from the SDK's serializer. So that is not the payload that the FhirClient has created, but possibly a view you get from the sandbox on the data, instead of the raw request payload. Make sure to inspect the actual raw request body.

If you have not left out any details of your code (other than real data), it should work. However, if you have other things in your FhirService that could influence serialization or how the request is sent, then please mention them here.

Try the request on the public server: https://server.fire.ly/R4, and see what its response is.

Another idea: does the Epic api accept JSON data at all? The OperationOutcome you mentioned is in XML format, and mentions something about structural errors. Remove your JSON preference from the FhirClient settings, and see if that helps.

Thousands of implementations use the SDK, and there is nothing wrong with the FhirClient or the serialization.

@fayazshaik07
Copy link
Author

@mbaltus Adding this ReturnPreference = ReturnPreference.Representation to the FHIRClientSettings has resolved the whole error for me. I'm able to create a Patient now without any issue. I can't thank you enough for taking time and responding to my queries. Thank you!

Could you please answer two more questions of mine?

  1. While we're using R4 FHIR APIs, Does Firely .Net SDK Support the next version of APIs (R5) in the future with a new SDK or would it require many code changes to accomodate?

  2. Could you please send me a resource for C# code for generating bearer token for the EPIC Sandbox backend services Non-Production Client Id. Thanks in Advance.

@mbaltus
Copy link
Member

mbaltus commented Jan 10, 2024

Glad that it works now!

I can answer only the first of your additional questions, since I do not know the details of generating an Epic token. Maybe there's more information on the Epic documentation site on the steps for that, or you could ask on StackOverflow for help.

The answer to the first question is: there is already a library for R5 - Hl7.Fhir.R5. The main parts of the library are the same as for Hl7.Fhir.R4. For example, the communication using the FhirClient will be the same.
However, a lot for FHIR resource types have differences in the model between R4 and R5, and there are some additional resource- and data types in R5. So anything related to working with the fields of POCO's could require code changes.

@mbaltus mbaltus closed this as completed Jan 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants