/*!
* SAP SE
* (c) Copyright 2009-2024 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides class sap.fhir.model.r4.FHIRListBinding
sap.ui.define([
"sap/ui/model/ListBinding",
"sap/ui/model/ChangeReason",
"sap/ui/model/Sorter",
"sap/ui/model/FilterProcessor",
"sap/ui/model/SorterProcessor",
"sap/fhir/model/r4/FHIRUtils",
"sap/fhir/model/r4/OperationMode",
"sap/ui/model/Filter",
"sap/fhir/model/r4/lib/HTTPMethod",
"sap/base/Log",
"sap/base/util/each",
"sap/fhir/model/r4/Context"
], function(ListBinding, ChangeReason, Sorter, FilterProcessor, SorterProcessor, FHIRUtils, OperationMode, Filter, HTTPMethod, Log, each, Context) {
"use strict";
/**
* Constructor for a new FHIRListBinding
*
* @class
* @classdesc List binding implementation for the FHIRModel
* @param {sap.fhir.model.r4.FHIRModel} oModel The FHIRModel
* @param {string} sPath The binding path in the model
* @param {sap.fhir.model.r4.Context} [oContext] The parent context which is required as base for a relative path
* @param {sap.ui.model.Sorter | sap.ui.model.Sorter[]} [aSorters] The dynamic sorters to be used initially (can be either a sorter or an array of sorters)
* @param {sap.ui.model.Filter | sap.ui.model.Filter[]} [aFilters] The dynamic application filters to be used initially (can be either a filter or an array of filters)
* @param {object} [mParameters] The map which contains additional parameters for the binding
* @param {string} [mParameters.groupId] The group id
* @param {sap.fhir.model.r4.OperationMode} [mParameters.operationMode] The operation mode, how to handle operations like filtering and sorting
* @param {string} [mParameters.valueSetLookupInStructureDefinition=true] If the list binding should trigger an automatic call of the structuredefinition to get the valueset behind the property
* @param {string} [mParameters.request] The parameters which will be passed with the request as url parameters, Note: any additional url parameters can be mentioned as new key/value pairs
* @param {string} [mParameters.request._include] The _include parameter, e.g. in a list which is bind to '/Patient' it's possible to include 'Coverage:payor'
* @param {string} [mParameters.request._revinclude] The _revinclude parameter, e.g. in a list which is bind to '/Practitioner' it's possible to reverse include 'PractitionerRole:practitioner'
* @param {string} [mParameters.request.url] The url parameter, adds the given url as a search parameter to the request
* @alias sap.fhir.model.r4.FHIRListBinding
* @author SAP SE
*
* @extends sap.ui.model.ListBinding
* @public
* @since 1.0.0
* @version 2.4.0
*/
var FHIRListBinding = ListBinding.extend("sap.fhir.model.r4.FHIRListBinding", {
constructor : function(oModel, sPath, oContext, aSorters, aFilters, mParameters) {
ListBinding.apply(this, arguments);
this.aFilters = aFilters instanceof Filter ? [aFilters] : aFilters;
this.aSorters = aSorters instanceof Sorter ? [aSorters] : aSorters;
this.mParameters = mParameters;
this.sOperationMode = (mParameters && mParameters.operationMode) || this.oModel.sDefaultOperationMode;
if (this.sOperationMode !== OperationMode.Server) {
throw new Error("Unsupported OperationMode: " + this.sOperationMode + ". Only sap.fhir.model.r4.OperationMode.Server is supported.");
}
this.sGroupId = mParameters && mParameters.groupId || oContext && oContext.sGroupId;
this.bSecureSearch = oModel.bSecureSearch;
this.bUnique = mParameters && mParameters.unique;
if (mParameters) {
this.bValueSetLookupInStructureDefinition = mParameters.valueSetLookupInStructureDefinition === undefined ? true : mParameters.valueSetLookupInStructureDefinition;
} else {
this.bValueSetLookupInStructureDefinition = true;
}
this.sId = FHIRUtils.uuidv4();
this._resetData();
},
initialize: function () {
// List doesn't get invalidated when context length is 0
// as per suggestion Server-side bindings (e.g. ODataListBinding) are expected to start with a "refresh" event
// overwrite the ListBindings "initialize" + fire refresh-event (although not defined in the metadata)
this.fireEvent("refresh", { reason: ChangeReason.Refresh });
return this;
}
});
/**
* @typedef {object} sap.fhir.model.r4.FHIRListBinding.Parameter
* @prop {object} [urlParameters] The parameters that will be passed as query strings
* @public
* @since 1.0.0
*/
/**
* Creates the parameters for the FHIR request based on the configured filters and sorters
*
* @param {number} [iLength] The number of contexts to retrieve beginning from the start index
* @returns {sap.fhir.model.r4.FHIRListBinding.Parameter} The map of parameters
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._buildParameters = function(iLength) {
var mParameters = {
urlParameters : {
_sort : FHIRUtils.createSortParams(this.aSorters),
_count : iLength
}
};
FHIRUtils.addRequestQueryParameters(this, mParameters);
FHIRUtils.filterBuilder(this.aFilters, mParameters.urlParameters, this.oModel.iSupportedFilterDepth, this._isValueSet());
return mParameters;
};
/**
* Returns already created binding contexts for all entities in this list binding for the range determined by the given start index <code>iStart</code> and <code>iLength</code>.
* Resource profiles which are mentioned in the context but aren't loaded already, are requested
*
* @param {number} [iStartIndex] The index where to start the retrieval of contexts
* @param {number} [iLength] The number of contexts to retrieve beginning from the start index
* @returns {sap.fhir.model.r4.Context[]} The array of all binding contexts
* @protected
* @since 1.0.0
*/
FHIRListBinding.prototype.getContexts = function(iStartIndex, iLength) {
if (!this.iLength && iLength !== undefined){
this.iLength = iLength > this.oModel.iSizeLimit ? this.oModel.iSizeLimit : iLength;
} else if (!this.iLength) {
this.iLength = this.oModel.iSizeLimit;
}
var mParameters = this._buildParameters(this.iLength);
var fnSuccess = function(oData) {
if (oData.total === undefined){
throw new Error("FHIR Server error: The \"total\" property is missing in the response for the requested FHIR resource " + this.sPath);
}
this.bDirectCallPending = false;
iStartIndex = this.aKeys.length;
if (oData.entry && oData.entry.length){
var oResource;
var oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique);
var sBindingResType = oBindingInfo.getResourceType();
for (var i = 0; i < oData.entry.length; i++) {
oResource = oData.entry[i].resource;
oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique, oResource);
if (oResource.resourceType === sBindingResType){
this.aKeys[iStartIndex + i] = oBindingInfo.getAbsolutePath().substring(1);
}
}
}
this._markSuccessRequest(oData, oData.total);
}.bind(this);
var fnSuccessValueSet = function(oData) {
var iTotal = oData.expansion.total || (oData.expansion.contains && oData.expansion.contains.length) || 0;
this._buildKeys("ValueSet/" + "§" + oData.expansion.identifier + "§", oData.expansion.contains, iTotal);
this._markSuccessRequest(oData, iTotal);
}.bind(this);
var fnLoadResources = function() {
var sValueSetUri = this._getValueSetUriFromStructureDefinition();
if (sValueSetUri) {
this._submitRequest("/ValueSet/$expand", {
urlParameters : {
url : sValueSetUri,
displayLanguage: sap.ui.getCore().getConfiguration().getLanguage()
}
}, fnSuccessValueSet);
} else {
this._loadResources(iLength);
}
}.bind(this);
var fnSuccessStructDef = function(oData) {
this.bDirectCallPending = false;
if (oData && oData.entry) {
this.oStructureDefinition = oData.entry[0].resource;
fnLoadResources();
} else {
this.bPendingRequest = false;
this.bInitial = false;
var oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique);
var oResource = this.oModel.getProperty(oBindingInfo.getResourcePath()) || {};
oResource.resourceType = oResource.resourceType || oBindingInfo.getResourceType();
var sStrucDefUrl = this.oModel.getStructureDefinitionUrl(oResource);
throw new Error("The structuredefinition " + sStrucDefUrl + " could not be loaded from the server for binding with path " + oBindingInfo.getRelativePath());
}
}.bind(this);
if (!this.bPendingRequest && !this.bResourceNotAvailable) {
if (this._isValueSetHardCoded() && this.iTotalLength === undefined) { // load hardcoded valueset
mParameters.urlParameters.displayLanguage = sap.ui.getCore().getConfiguration().getLanguage();
this._submitRequest("/ValueSet/$expand", mParameters, fnSuccessValueSet);
} else if (!this.aSortersCache && !this.aFilterCache && this.sNextLink && iLength > this.iLastLength) {
this.iLastLength += this.iLength;
// the direct next links will not be used by default to send the request
// instead its converted into the necessary parameters and path before sending
// this is to address the if the service url is relative
if (this.sNextLink && this.sNextLink.indexOf("?") > -1) {
var oNextLink = this.oModel.getNextLink(this.sNextLink, this.sPath, mParameters);
this._submitRequest(oNextLink.url, oNextLink.parameters, fnSuccess, true);
} else {
this._submitRequest(this.sNextLink, undefined, fnSuccess, true);
}
} else if (this.iTotalLength === undefined){ // load context based resources
this.iLastLength = this.iLength;
this._loadProfile(fnSuccessStructDef, fnLoadResources, fnSuccess, mParameters, iLength);
} else if (!this._isValueSet()){
if (iLength > this.iLastLength){
this.iLastLength += this.iLength;
}
if (!this.iLastLength){
this.iLastLength = this.iLength;
}
this._loadResources(this.iLastLength);
}
}
this._buildContexts(iLength);
return this.aContexts;
};
/**
* Loads the FHIR resources based on the configured <code>oContext</code> and <code>sPath</code>
*
* @param {number} iLength The requested length of data
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._loadResources = function(iLength) {
var oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique);
var vValue = this.oModel.getProperty(this.sPath, this.oContext);
var iValuesLength = 0;
if (oBindingInfo && oBindingInfo.getAbsolutePath().endsWith("]")){
iValuesLength = vValue && Object.keys(vValue).length;
this._buildKeys(oBindingInfo.getAbsolutePath().substring(1), vValue, iValuesLength);
} else if (Array.isArray(vValue)){
var vValueOld = this.oModel.getProperty(this.sPath, this.oContext, this.oModel.oDataServerState);
var iValuesLengthOld = vValueOld ? Object.keys(vValueOld).length : 0;
iValuesLength = Object.keys(vValue).length;
this.iClientChanges = iValuesLength - iValuesLengthOld;
this._buildKeys(oBindingInfo.getAbsolutePath().substring(1), vValue, iLength || iValuesLength);
iValuesLength -= this.iClientChanges;
} else if (vValue && typeof vValue === "object") {
var aClientChanges = this.oModel.mOrderResources[oBindingInfo.getResourceType()];
this.iClientChanges = aClientChanges && aClientChanges.length || 0;
if (this.iClientChanges > 0){
this.aKeys = aClientChanges.concat(this.aKeysServerState);
} else {
this.aKeys = FHIRUtils.deepClone(this.aKeysServerState);
}
iValuesLength = this.aKeys.length - this.iClientChanges;
var aClientRemovedResources = this.oModel.mRemovedResources[oBindingInfo.getResourceType()];
if (aClientRemovedResources){
this.aKeys = this.aKeys.filter(function (sResPath) {
return !aClientRemovedResources.includes(sResPath);
});
this.iTotalLength = this.aKeys.length;
}
} else {
this.iClientChanges = 0;
this.aKeys = this.aKeysServerState;
}
if (this.iTotalLength === undefined){
this._markSuccessRequest(vValue, iValuesLength);
}
};
/**
* Builds the binding context array for the list
*
* @param {number} iLength the current requested length of the paging
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._buildContexts = function(iLength) {
if (!this.aContexts) {
this.aContexts = [];
}
if (this.aKeys) {
if (iLength === undefined || iLength > this.aKeys.length || isNaN(iLength)){
iLength = this.aKeys.length;
}
this.aContexts = [];
for (var j = 0; j < iLength; j++) {
this.aContexts.push(Context.create(this.oModel, this, "/" + this.aKeys[j], this.sGroupId));
}
}
};
/**
* Executes an ajax call with given <code>sPath</code>, <code>mParameters</code>. Additionally, it's possible to assign the given function <code>fnSuccessCallback</code> as
* a callback function which is executed when the ajax call was executed successfully
*
* @param {string} sPath The path of the resource which will be requested, relative to the root URL of the FHIR server
* @param {object} mParameters The URL parameters which are send by the request e.g. _count, _summary
* @param {function} fnSuccessCallback The callback function which is executed if the request was successful
* @param {boolean} [bForceDirectCall] Determines if this binding should avoid the bundle request
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._submitRequest = function(sPath, mParameters, fnSuccessCallback, bForceDirectCall) {
var fnErrorCallback = function(oData) {
if (oData.message !== "abort"){
this.bPendingRequest = false;
this.bResourceNotAvailable = true;
this.fireDataReceived({
data : oData
});
}
}.bind(this);
this.bDirectCallPending = bForceDirectCall;
if (!mParameters){
mParameters = {};
}
mParameters.binding = this;
mParameters.forceDirectCall = bForceDirectCall;
mParameters.successBeforeMapping = fnSuccessCallback;
mParameters.error = fnErrorCallback;
this.oModel.loadData(sPath, mParameters);
this.bPendingRequest = true;
};
/**
* Writes the aKeys based on the given <code>sUri</code> and <code>iNumberOfKeys</code> and <code>oValues</code>
*
* @param {string} sUri The URI for the keys, e.g. Patient/1234/gender
* @param {any} vValues The data of the whole list
* @param {number} iNumberOfKeys The number of keys which will be created
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._buildKeys = function(sUri, vValues, iNumberOfKeys) {
this.aKeys = [];
var j = 0;
for (var sKey in vValues) {
var sPathForKey = sUri + "/" + sKey;
if (j < iNumberOfKeys){
this.aKeys[j] = sPathForKey;
j++;
}
}
};
/**
* Mark the pending request as successful and attach a new data received callback
*
* @param {object} oData The data retrieved from the server
* @param {number} iTotalLength The number of resources retrieved from the server
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._markSuccessRequest = function(oData, iTotalLength) {
if (oData && oData.hasOwnProperty("link")) {
this.sNextLink = FHIRUtils.getLinkUrl(oData.link, "next");
this.sPrevLink = FHIRUtils.getLinkUrl(oData.link, "previous");
}
this.aKeysServerState = FHIRUtils.deepClone(this.aKeys);
this.iTotalLength = iTotalLength;
this.bPendingRequest = false;
this.bInitial = false;
this.oModel.attachAfterUpdate(function() {
this.fireDataReceived({
data : oData
});
}.bind(this));
};
/**
* Loads the resource profile based on the given <code>mParameters</code> or if a binding exits by the configured <code>sPath</code> and <code>oContext</code>
*
* @param {function} fnSuccessCallbackStructureDefintion The callback which is executed when the StructureDefinition is loaded successfully
* @param {function} fnLoadResources The function which loads the desired resources
* @param {function} fnSuccessCallback The callback which is executed if no binding is found and the profile is loaded by the given <code>mParameters</code>
* @param {object} mParameters The URL parameters which are send by the request, e.g. _count, _summary
* @param {number} iLength The requested data length for this list binding
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._loadProfile = function(fnSuccessCallbackStructureDefintion, fnLoadResources, fnSuccessCallback, mParameters, iLength) {
if (!this.isRelative()) {
this._submitRequest(this.sPath, mParameters, fnSuccessCallback, this.bSecureSearch);
} else if (this.oContext && this.bValueSetLookupInStructureDefinition) {
var oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique);
var oResource = this.oModel.getProperty(oBindingInfo.getResourcePath()) || {};
oResource.resourceType = oResource.resourceType || oBindingInfo.getResourceType();
var sStrucDefUrl = this.oModel.getStructureDefinitionUrl(oResource);
var aStructDefs = [];
FHIRUtils.filterObject(this.oModel.oData.StructureDefinition, "url", sStrucDefUrl, 1, aStructDefs);
if (aStructDefs.length > 0) {
this.oStructureDefinition = aStructDefs[0];
fnLoadResources();
} else {
this._submitRequest("/StructureDefinition", { urlParameters : {url : sStrucDefUrl}}, fnSuccessCallbackStructureDefintion, true);
}
} else {
this._loadResources(iLength);
}
};
/**
* Check whether this binding would provide new values and in case it changed, inform interested parties about this
*
* @param {boolean} [bForceUpdate] Force update of binding
* @param {object} [mChangedEntities] The map of changed entities
* @param {string} [sMethod] The URL request method
* @protected
* @since 1.0.0
*/
FHIRListBinding.prototype.checkUpdate = function(bForceUpdate, mChangedEntities, sMethod) {
var oBindingInfo = this.oModel.getBindingInfo(this.sPath, this.oContext, this.bUnique);
var mResources = oBindingInfo && mChangedEntities && mChangedEntities[oBindingInfo.getResourceType()];
if (mResources && sMethod && sMethod !== HTTPMethod.GET) {
var sResType = oBindingInfo.getResourceType();
for (var sId in mResources) {
var sResPath = sResType + "/" + sId;
if (!this.isRelative()) {
if (sMethod === HTTPMethod.DELETE) {
// check for context
if (this.oModel.mRemovedResources[sResType] && this.oModel.mRemovedResources[sResType].indexOf(sResPath) > -1) {
// client changes (not yet submitted to server)
this.aKeys.splice(this.aKeys.indexOf(oBindingInfo.getResourceType() + "/" + sId), 1);
} else {
// server changes (response from server after submitted the removed resources directly)
this.aKeysServerState.splice(this.aKeysServerState.indexOf(oBindingInfo.getResourceType() + "/" + sId), 1);
}
} else if (sMethod === HTTPMethod.POST) {
this.iTotalLength++;
this.aKeysServerState.unshift(oBindingInfo.getResourceType() + "/" + sId);
} else if (sMethod === HTTPMethod.PUT && oBindingInfo.getBinding().length === 3) { // in case of history entry update
this.iTotalLength++;
this.aKeysServerState.unshift(oBindingInfo.getAbsolutePath().substring(1) + "/" + mResources[sId].meta.versionId);
}
} else if (sMethod === HTTPMethod.PUT) {
this.iTotalLength = undefined;
}
}
}
var sPath = this.oModel._getProperty(mChangedEntities, ["path", "lastUpdated"]);
if (oBindingInfo && (sPath && FHIRUtils.getNumberOfLevelsByPath(sPath) < 3 && sPath.indexOf(oBindingInfo.getResourceType()) > -1 || sPath === oBindingInfo.getAbsolutePath()) || bForceUpdate === true || sMethod) {
this._fireChange({
reason: ChangeReason.Change
});
}
};
/**
* Determines if the value set configured by <code>mParameters</code> is hard coded or came by a reference
*
* @returns {boolean} if this binding has a hard coded value set url
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._isValueSetHardCoded = function() {
return this.mParameters && this.mParameters.request && this.mParameters.request.hasOwnProperty("url");
};
/**
* Determines the URI of an value set from the configured <code>oStructureDefinition</code>
*
* @returns {string} The URI of an ValueSet
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._getValueSetUriFromStructureDefinition = function() {
var aElement = this.oModel._getProperty(this.oStructureDefinition, ["snapshot", "element"]);
if (aElement && aElement.length > 0) {
var aType = this.oModel._getProperty(this.oStructureDefinition, [
"snapshot",
"element",
"[id=" + aElement[0].id + "." + this.sPath + "]",
"type"
]);
if (aType && (aType[0].code === "code" || aType[0].code === "CodeableConcept")){
return this.oModel._getProperty(this.oStructureDefinition, [
"snapshot",
"element",
"[id=" + aElement[0].id + "." + this.sPath + "]",
"binding",
"valueSet"
]);
}
}
return undefined;
};
/**
* Determines if the current binding underlies an value set
*
* @returns {boolean} if this binding is binded to a valueset
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._isValueSet = function() {
if (this.aKeys && this.aKeys.length > 0) {
return this.aKeys[0].indexOf("ValueSet") > -1;
} else if (this.sPath.indexOf("ValueSet") > -1 || this._isValueSetHardCoded() || this._getValueSetUriFromStructureDefinition()) {
return true;
}
return false;
};
/**
* Determines the number of entities contained by the actual list binding
*
* @returns {number} The number of entities contained by the current list binding
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.getLength = function() {
if ( this.iTotalLength !== undefined ){
return this.iTotalLength + this.iClientChanges;
} else {
return undefined;
}
};
/**
* Filters the actual list binding depending on the given <code>aFilters</code>
*
* @param {sap.ui.model.Filter[]} [aFilters] The filters defined for the list binding
* @param {sap.ui.model.FilterType} sFilterType Type of the filter which should be adjusted, if it is not given, the standard behaviour applies
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.filter = function (aFilters, sFilterType) {
FHIRUtils.filter(aFilters, this, sFilterType);
};
/**
* Sorts the actual list binding based on the given <code>aSorters</code>
*
* @param {sap.ui.model.Sorter[]} aSorters The sorters defined for the list binding
* @param {boolean} bRefresh If the binding should directly send a call or wait for the filters, for p13ndialog
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.sort = function(aSorters, bRefresh) {
FHIRUtils.sort(aSorters, this, bRefresh);
};
/**
* Sets the context of the list binding and refreshes the binding
*
* @param {sap.fhir.model.r4.Context} oContext The context object
* @protected
* @since 1.0.0
*/
FHIRListBinding.prototype.setContext = function(oContext) {
if (this.oContext !== oContext && this.isRelative()){
this.oContext = oContext;
this.refresh(ChangeReason.Context);
}
};
/**
* Refreshes the list binding
*
* @param {sap.ui.model.ChangeReason} sChangeReason The reason for refreshing the binding
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.refresh = function(sChangeReason) {
this._resetData();
this._fireChange({
reason : sChangeReason
});
};
/**
* Resets the data of the list binding
*
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._resetData = function() {
this.aKeys = [];
this.aKeysServerState = [];
this.bResourceNotAvailable = false;
this.aContexts = undefined;
this.iTotalLength = undefined;
this.bInitial = true;
this.sNextLink = undefined;
this.sPrevLink = undefined;
this.iLastLength = undefined;
this.aFilterCache = undefined;
this.aSortersCache = undefined;
this.iClientChanges = 0;
};
/**
* Determines if the list binding is configured in client mode
*
* @returns {boolean} if the model is initialized with the client mode
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._isClientMode = function() {
return this.sOperationMode === OperationMode.Client;
};
/**
* Determines if the list binding is configured in server mode
*
* @returns {boolean} if the model is initialized with the server mode
* @private
* @since 1.0.0
*/
FHIRListBinding.prototype._isServerMode = function() {
return this.sOperationMode === OperationMode.Server;
};
/**
* Determines the actual filters of the list binding
*
* @returns {sap.ui.model.Filter[]} The array containing the actual filters
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.getFilters = function() {
return this.aFilters;
};
/**
* Determines the actual sorters of the list binding
*
* @returns {sap.ui.model.Sorter[]} The array containing the actual sorters
* @public
* @since 1.0.0
*/
FHIRListBinding.prototype.getSorters = function() {
return this.aSorters;
};
return FHIRListBinding;
});