Skip to content

Commit

Permalink
feat: return deleted resources object from submitChanges (#445)
Browse files Browse the repository at this point in the history
* feat: return deleted resources object from submitChanges

* fixed eslint issues and added test cases

* added jsdoc

* Update FHIRModel.js

* Update FHIRUtils.js

* Update FHIRUtils.js

* Update FHIRUtils.js

* Update FHIRUtils.js

* incorporated review comments

* incorporated review comments

* incorporated review comments

* incorporated review comments

* Fixed ESLint issue
  • Loading branch information
kanikasharma97 authored May 13, 2024
1 parent 707b026 commit 2081bd8
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/tutorials/4 Request Handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ onSavePress: function(){
Depending on the submit mode the callback will be invoked with specific type of parameters.

If the group submit mode is Batch/Transaction then the success callback will contain all the FHIR Resources which were part of the request and in case of failed enteries in Bundle the error callback will be invoked with the successful resources and the operation outcome enteries.
In case of delete if the API doesn't return FHIR resources which got deleted successfuly, while resolving the promise, the removed resources are fetched from the model and assigned to aFHIRResource.

```javascript
onSavePress: function() {
Expand Down
32 changes: 32 additions & 0 deletions src/sap/fhir/model/r4/FHIRModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ sap.ui.define([
*/
FHIRModel.prototype.submitChanges =
function(sGroupId, fnSuccessCallback, fnErrorCallback) {
var aRemovedResource = this._getRemovedResourcesObject();
if (typeof sGroupId === "function") {
fnErrorCallback = fnSuccessCallback;
fnSuccessCallback = FHIRUtils.deepClone(sGroupId);
Expand Down Expand Up @@ -693,9 +694,16 @@ sap.ui.define([
);
aPromises.push(oPromise);
oPromise.then(function (aFHIRResource) {
if (aFHIRResource.length == 0) {
aFHIRResource = aRemovedResource;
}
fnSuccessCallback(aFHIRResource);
}).catch(function (oError) {
if (fnErrorCallback && oError.requestHandle) {
if (aRemovedResource.length != 0) {
var aId = FHIRUtils.getIdFromOperationOutcome(oError.operationOutcomes);
oError.resources = FHIRUtils.filterResourcesByIds(aRemovedResource, aId);
}
var mParameters = {
message: oError.requestHandle.getRequest().statusText,
description: oError.requestHandle.getRequest().responseText,
Expand Down Expand Up @@ -806,6 +814,30 @@ sap.ui.define([
return mRequestHandles;
};

/**
* Retrieves an array of resources that have been removed from the FHIR model.
* Iterates through the removed resources,
* retrieves corresponding resources from the model, and returns them.
* @returns {Array<object>} An array containing the removed resources.
* @private
* @since 2.4.0
*/
FHIRModel.prototype._getRemovedResourcesObject = function () {
var aResource = [];
for (var sType in this.mRemovedResources) {
if (this.mRemovedResources.hasOwnProperty(sType)) {
var oRemovedResource = this.mRemovedResources[sType];
for (var sKey in oRemovedResource) {
var oResource = this.getProperty("/" + oRemovedResource[sKey]);
if (oResource) {
aResource.push(oResource);
}
}
}
}
return aResource;
};

/**
* Checks if an update for the existing bindings is necessary due to the <code>mChangedResources</code>
*
Expand Down
47 changes: 46 additions & 1 deletion src/sap/fhir/model/r4/FHIRUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,51 @@ sap.ui.define([
return sFullUrl;
};

/**
* Extracts resource IDs from the error description in FHIR OperationOutcome.
* @param {Array<object>} aOperationOutcome - The array of FHIR OperationOutcome objects.
* @returns {Array<string>} - An array containing the extracted resource IDs.
* @protected
* @since 2.4.0
*/
FHIRUtils.getIdFromOperationOutcome = function (aOperationOutcome) {
var aID = [];
var aMatchedId;
var sText;
for (var key in aOperationOutcome) {
if (aOperationOutcome.hasOwnProperty(key)) {
var oOperationOutcome = aOperationOutcome[key];
var oIssue = oOperationOutcome._aIssue[0];
if (oIssue) {
sText = oIssue.details.text;
if (sText) {
aMatchedId = sText.match(/[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}/);
}
if (aMatchedId && aMatchedId.length > 0){
aID.push(aMatchedId[0]);
}
}
}
}
return aID;

};

/**
* Filters an array of FHIR resources by their IDs, removing those that match the provided IDs.
* @param {Array<object>} aResource - The array of FHIR resources to filter.
* @param {Array<string>} aID - The array of resource IDs to exclude from the filtered result.
* @returns {Array<object>} - The filtered array of FHIR resources.
* @protected
* @since 2.4.0
*/
FHIRUtils.filterResourcesByIds = function (aResource, aID) {
function isIdNotIncluded(obj) {
return !aID.includes(obj.id);
}
return aResource.filter(isIdNotIncluded);
};

return FHIRUtils;

});
});
43 changes: 43 additions & 0 deletions test/localService/BundleWithDeleteSuccessAndFailureEntries.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"resourceType": "Bundle",
"id": "934953d3-35a3-477d-8c9e-0948a7e553ec",
"links": [],
"type": "batch-response",
"entry": [
{
"response": {
"status": "409 CONFLICT",
"outcome": {
"issue": [
{
"severity": "error",
"code": "conflict",
"details": {
"text": "Referenced resource exist 'e1692049-4bdc-4e40-abb5-4eae27ce0b8b' for resource 'Patient}'"
},
"diagnostics": "Referenced resource exist 'e1692049-4bdc-4e40-abb5-4eae27ce0b8b' for resource 'Patient}'"
}
],
"resourceType": "OperationOutcome",
"meta": {
"profile": [
"http://hl7.org/fhir/StructureDefinition/OperationOutcome"
]
}
}
}
},
{
"response": {
"status": "204 NO_CONTENT"
},
"fullUrl": "urn:uuid:cb62c497-ee6c-4c26-9721-2668f797e6c8"
}
],
"total": 2,
"meta": {
"profile": [
"http://hl7.org/fhir/StructureDefinition/Bundle"
]
}
}
18 changes: 18 additions & 0 deletions test/qunit/model/FHIRModel.integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -920,4 +920,22 @@ sap.ui.define([
oListBinding.attachDataReceived(fnDataReceivedCheck);
});

QUnit.test("Test delete containing successful entry and operation outcome", function (assert) {
var oJSONData = TestUtils.loadJSONFile("BundleWithDeleteSuccessAndFailureEntries");
var done = assert.async();
var sResId = this.oFhirModel.create("Patient", {
resourceType: "Patient",
version: "1.0.0",
name: "Billy"
}, "bundle");
this.oFhirModel.mChangedResources = {};
this.oFhirModel.remove(["/Patient/" + sResId], undefined, "bundle");
var fnErrorCallback = function (oMessage, aFHIRResource, aOperationOutcome) {
assert.strictEqual(aOperationOutcome.length, 1, "Bundle error callback contains the opertion outcome of the failed entry ");
done();
};
TestUtilsIntegration.manipulateResponse("http://localhost:8080/fhir/R4", this.oFhirModel, TestUtilsIntegration.setResponseJSON, undefined, oJSONData);
this.oFhirModel.submitChanges("bundle", undefined, fnErrorCallback);
});

});
7 changes: 7 additions & 0 deletions test/qunit/model/FHIRModel.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -1503,4 +1503,11 @@ sap.ui.define([

});

QUnit.test("Test getRemovedResourcesObject returning the correct value", function (assert) {
this.oFhirModel1.mRemovedResources = { Patient: ["Patient/123"] };
this.oFhirModel1.setProperty("/Patient/123", { id: 123, description: "this is a test object", resourceType: "Patient" });
var aResource = this.oFhirModel1._getRemovedResourcesObject();
assert.deepEqual(aResource, [{ id: 123, description: "this is a test object", resourceType: "Patient" }], "Correct resources returned");
});

});
55 changes: 55 additions & 0 deletions test/qunit/model/FHIRUtils.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,4 +516,59 @@ sap.ui.define(["../utils/TestUtils", "sap/fhir/model/r4/FHIRUtils"], function(Te
sFullUrl = FHIRUtils.generateFullUrl(TestUtils.createUri("url"), "/Patient/123", "123", "http://example.com");
assert.strictEqual(sFullUrl, "http://example.com/Patient/123");
});

QUnit.test("Test getIdFromOperationOutcome", function(assert) {
var oOperationOutcome = {
"0": {
"_sResourceType": "OperationOutcome",
"_aIssue": [
{
"severity": "error",
"code": "conflict",
"details": {
"text": "Referenced resource exist '7b4abf15-8a93-4e11-8d85-96c945530d05' for resource 'Patient}'"
},
"diagnostics": "Referenced resource exist '7b4abf15-8a93-4e11-8d85-96c945530d05' for resource 'Patient}'"
}
]
}
};
var aExpectedId = ["7b4abf15-8a93-4e11-8d85-96c945530d05"];
var aActualId = FHIRUtils.getIdFromOperationOutcome(oOperationOutcome);
assert.deepEqual(aActualId, aExpectedId, "IDs extracted correctly from operationOutcomes");
});

QUnit.test("Test filterResourcesByIds", function (assert) {
var aResource = [
{ id: "1", name: "Resource 1" },
{ id: "2", name: "Resource 2" },
{ id: "3", name: "Resource 3" }
];
var aId = ["2"];
var aFilteredResource = FHIRUtils.filterResourcesByIds(aResource, aId);
assert.deepEqual(aFilteredResource, [{ id: "1", name: "Resource 1" }, { id: "3", name: "Resource 3" }], "Filtered resources should match expected result");
});

QUnit.test("Test getIdFromOperationOutcome when resource ID is not present", function(assert) {
var oOperationOutcome = {
"0": {
"_sResourceType": "OperationOutcome",
"_aIssue": [
{
"severity": "error",
"code": "conflict",
"details": {
"text": "Referenced resource exist for this resource}"
},
"diagnostics": "Referenced resource exist for this resource}"
}
]
}
};

var actualIDs = FHIRUtils.getIdFromOperationOutcome(oOperationOutcome);
assert.deepEqual(actualIDs, [], "Empty array returned");
});


});

0 comments on commit 2081bd8

Please sign in to comment.