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

POSTing a Bundle should not append /Bundle to the URL #2100

Closed
johnkwaters opened this issue May 25, 2022 · 10 comments
Closed

POSTing a Bundle should not append /Bundle to the URL #2100

johnkwaters opened this issue May 25, 2022 · 10 comments

Comments

@johnkwaters
Copy link

See except from Smile CDR:

If you POST to [baseUrl]/Bundle you are submitting the bundle for storage as-is. In other words, the bundle is stored as a Bundle, and the contents inside aren’t looked at by the server (aside from any validation that is enabled). This mode is generally used to store Bundle resources with Bundle.type values such as document, and collection. In its default configuration, Smile CDR will prohibit storing a Bundle with a type value of transaction or batch as this is generally a sign that the client is attempting to perform the operations described below but with an incorrect request URL.

Naming: This is not a FHIR Transaction, but instead is simply a simple resource create where the resource happens to be a Bundle resource.

If you POST to [baseUrl] and your Bundle has a Bundle.type value of transaction you are performing a FHIR “transaction operation”, meaning that all of the individual resources inside the bundle will be processed. It is also possible to include other REST operations such as searches in this kind of bundle. The processing works as an atomic unit, meaning that if anything fails (e.g. invalid data in an individual element) the entire thing will be rolled back.

Naming: This operation is referred to as a FHIR Transaction operation.

If you POST to [baseUrl] and your Bundle has a Bundle.type value of batch, the same processing as the transaction applies, except that individual operations are executed in individual database transactions, so an individual failure doesn’t cause the entire operation to be rolled back. In this case, the response Bundle returned by the server will include status entries indicating the outome for the individual operations within. Note that the batch operation does require the entire Bundle to be valid FHIR at a minimum. This means that it can’t have non-existent resource types in it, malformed datatypes, etc.

Naming: This operation is referred to as a FHIR Batch operation.

@johnkwaters
Copy link
Author

This is the transaction I am sending - it's that Bundle on the end of the URL that is objectionable.
Code to generate this is


            var tx = new TransactionBuilder(fc.Endpoint, Bundle.BundleType.Transaction)
                .Update(p.Id, p);
            var b = tx.ToBundle();

            var res = await fc.CreateAsync(b);

Where p is a new Patient

This is in order to do Client Assigned IDs.

POST https://fhir-st-mpi-clinical-dev.apps.okd.healthconcourse.com/Bundle
{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "00000005",
        "identifier": [
          {
            "system": "http:https://ger911.com/fhir/hc",
            "value": "ORG-GER-00000005"
          }
        ],
        "active": true,
        "name": [
          {
            "family": "Waters",
            "given": [
              "005"
            ]
          }
        ]
      },
      "request": {
        "method": "PUT",
        "url": "Patient/00000005"
      }
    }
  ]
}

@mmsmits
Copy link
Member

mmsmits commented May 25, 2022

Hi @johnkwaters,

What is your question exactly? Is there an issue in our FHIR Client with posting transaction bundles to a /Bundle endpoint?

EDIT:

I think I get it. You want to perform a transaction, but you are using CreateAsync() for that, which automatically does a POST on the /Bundle endpoint.

Please try to use TransactionAsync() instead, and let me know if that works.

So in your code, that last line should be:

var res = await fc.TransactionAsync(b);

@johnkwaters
Copy link
Author

The Philips CDR team told me the same thing - a bundle transaction should post to the base path, not /Bundle:

http:https://hl7.org/fhir/r4/http.html#transaction

@johnkwaters
Copy link
Author

Hi @johnkwaters,

What is your question exactly? Is there an issue in our FHIR Client with posting transaction bundles to a /Bundle endpoint?

EDIT:

I think I get it. You want to perform a transaction, but you are using CreateAsync() for that, which automatically does a POST on the /Bundle endpoint.

Please try to use TransactionAsync() instead, and let me know if that works.

So in your code, that last line should be:

var res = await fc.TransactionAsync(b);

Yes, the problem is that the /Bundle is not supposed to be on the URL - will try using TransactionAsync (I haven't seen that mentioned anywhere before - the docs could and examples sure could use some more complex non 101 cases!).

@mmsmits
Copy link
Member

mmsmits commented May 25, 2022

No problem, happy to help. Let me know if TransactionAsync solved your problem

@johnkwaters
Copy link
Author

I guess I am a little confused here...
So my code was doing this:

            var tx = new TransactionBuilder(fc.Endpoint, Bundle.BundleType.Transaction)
                .Update(p.Id, p);
            var b = tx.ToBundle();

            var res = await fc.CreateAsync(b);

And looking at the source code for TransactionAsync, I see:

        public Task<Bundle> TransactionAsync(Bundle bundle)
        {
            if (bundle == null) throw new ArgumentNullException(nameof(bundle));

            var tx = new TransactionBuilder(Endpoint).Transaction(bundle).ToBundle();
            return executeAsync<Bundle>(tx, HttpStatusCode.OK);
        }

So I already have a TransactionBundle ... with an Update in it - then your code turns the TransactionBundle into.... a TransactionBundle? Seems the only difference is that you then call executeAsync?

So would all of this just be the same as:

            var tx = new TransactionBuilder(fc.Endpoint, Bundle.BundleType.Transaction)
                .Update(p.Id, p);
            var b = tx.ToBundle();

            var res = await fc.executeAsync(b, HttpStatusCode.OK);

And the reason I am doing this convoluted call, is that I am trying to do a client assigned ID for the Patient p, and the CDR vendor said I can only do that by create a request that has this form:

POST https://fhir-st-mpi-clinical-dev.apps.okd.healthconcourse.com
{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "00000005",
        "identifier": [
          {
            "system": http:https://ger911.com/fhir/hc,
            "value": "ORG-GER-00000005"
          }
        ],
        "active": true,
        "name": [
          {
            "family": "Waters",
            "given": [
              "005"
            ]
          }
        ]
      },
      "request": {
        "method": "PUT",
        "url": "Patient/00000005"
      }
    }
  ]
}

The outer part is a POST to the endpoint WITHOUT /Bundle, and inside of it is a PUT for the patient with an ID.

@johnkwaters
Copy link
Author

I can confirm that the change above

var tx = new TransactionBuilder(fc.Endpoint, Bundle.BundleType.Transaction)
  .Update(p.Id, p);
var b = tx.ToBundle();

var res = await fc.executeAsync(b, HttpStatusCode.OK);

generates the request I was looking for:

POST https://cdr-stu3-sandbox.us-east.philips-healthsuite.com/store/fhir/5b1ffebe-46f3-40c4-b12a-653364078073
{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "resource": {
        "resourceType": "Patient",
        "id": "00000005",
        "identifier": [
          {
            "system": "http:https://ger911.com/fhir/hc",
            "value": "ORG-GER-00000005"
          }
        ],
        "active": true,
        "name": [
          {
            "family": "Waters",
            "given": [
              "005"
            ]
          }
        ]
      },
      "request": {
        "method": "PUT",
        "url": "Patient/00000005"
      }
    }
  ]
}

@mmsmits
Copy link
Member

mmsmits commented May 25, 2022

Good! I'll close this issue then :)

@mmsmits mmsmits closed this as completed May 25, 2022
@johnkwaters
Copy link
Author

Thanks @mmsmits . I don't know if you can respond the the discussion on the topic - see #2099
I am hearing different things from diffferent CDR vendors (HAPI, Smile, Philips)
What is your take on how a client assigned ID should be sent for a new Patient record?

@brianpos
Copy link
Collaborator

brianpos commented May 26, 2022

Is this all a result of using the TransactionBuilder class to make a bundle rather than just hand creating the Bundle resource itself?

and yes, CreateAsync passing a bundle would be going to the generic Create method with Bundle as the implied type parameter
image

So it's really doing exactly what you asked in the very initial comment above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants