@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
1,149 lines (1,063 loc) • 333 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*eslint-disable max-len */
/**
* OData-based DataBinding
*
* @namespace
* @name sap.ui.model.odata.v2
* @public
*/
//Provides class sap.ui.model.odata.v2.ODataModel
sap.ui.define([
"./_CreatedContextsCache",
"./Context",
"./ODataAnnotations",
"./ODataContextBinding",
"./ODataListBinding",
"./ODataTreeBinding",
"sap/base/assert",
"sap/base/Log",
"sap/base/security/encodeURL",
"sap/base/util/deepEqual",
"sap/base/util/deepExtend",
"sap/base/util/each",
"sap/base/util/extend",
"sap/base/util/isEmptyObject",
"sap/base/util/isPlainObject",
"sap/base/util/merge",
"sap/base/util/uid",
"sap/base/util/UriParameters",
"sap/ui/base/SyncPromise",
"sap/ui/core/Configuration",
"sap/ui/core/library",
"sap/ui/core/message/Message",
"sap/ui/core/message/MessageParser",
"sap/ui/model/_Helper",
"sap/ui/model/BindingMode",
"sap/ui/model/Context",
"sap/ui/model/FilterProcessor",
"sap/ui/model/Model",
"sap/ui/model/odata/CountMode",
"sap/ui/model/odata/MessageScope",
"sap/ui/model/odata/ODataMetadata",
"sap/ui/model/odata/ODataMetaModel",
"sap/ui/model/odata/ODataMessageParser",
"sap/ui/model/odata/ODataPropertyBinding",
"sap/ui/model/odata/ODataUtils",
"sap/ui/model/odata/OperationMode",
"sap/ui/model/odata/UpdateMethod",
"sap/ui/thirdparty/datajs",
"sap/ui/thirdparty/URI",
"sap/ui/util/isCrossOriginURL"
], function(_CreatedContextsCache, Context, ODataAnnotations, ODataContextBinding, ODataListBinding,
ODataTreeBinding, assert, Log, encodeURL, deepEqual, deepExtend, each, extend,
isEmptyObject, isPlainObject, merge, uid, UriParameters, SyncPromise, Configuration,
coreLibrary, Message, MessageParser, _Helper, BindingMode, BaseContext, FilterProcessor,
Model, CountMode, MessageScope, ODataMetadata, ODataMetaModel, ODataMessageParser,
ODataPropertyBinding, ODataUtils, OperationMode, UpdateMethod, OData, URI, isCrossOriginURL
) {
"use strict";
var sClassName = "sap.ui.model.odata.v2.ODataModel",
aDeepCreateParametersAllowlist = ["context", "properties"],
MessageType = coreLibrary.MessageType,
mMessageType2Severity = {},
aRequestSideEffectsParametersAllowList = ["groupId", "urlParameters"];
mMessageType2Severity[MessageType.Error] = 0;
mMessageType2Severity[MessageType.Warning] = 1;
mMessageType2Severity[MessageType.Success] = 2;
mMessageType2Severity[MessageType.Information] = 3;
mMessageType2Severity[MessageType.None] = 4;
/**
* Constructor for a new ODataModel.
*
* @param {string|object} vServiceUrl
* Base URI of the service to request data from; additional URL parameters appended here will
* be appended to every request. If you pass an object, it will be interpreted as the
* parameter object (second parameter). Then <code>mParameters.serviceUrl</code> becomes a
* mandatory parameter.
* @param {object} [mParameters]
* Map which contains the following parameter properties:
* @param {string|string[]} [mParameters.annotationURI]
* The URL (or an array of URLs) from which the annotation metadata should be loaded
* @param {string[]} [mParameters.bindableResponseHeaders=null]
* Set this array to make custom response headers bindable via the entity's
* "__metadata/headers" property
* @param {boolean} [mParameters.canonicalRequests=false]
* Whether the model tries to calculate canonical URLs to request the data.
*
* <b>For example:</b> An application displays the details of a sales order in a form with an
* absolute binding path <code>/SalesOrderSet("1")</code>. The form embeds a table for the
* sales order line items with a relative binding path <code>ToLineItems</code>. If the user
* selects a sales order line item (e.g. Item "10"), the details of this sales order line item
* are displayed in another form, which also contains a table for the sales order line item's
* schedules with a relative binding path <code>ToSchedules</code>.
*
* If the <code>canonicalRequests</code> parameter has the default value <code>false</code>,
* then the OData model would request the data for the sales order line item's details form
* with the following requests:<pre>
* GET /<serviceUrl>/SalesOrderSet("1")/ToLineItems(SalesOrderID="1",ItemPosition="10")
* GET /<serviceUrl>/SalesOrderSet("1")/ToLineItems(SalesOrderID="1",ItemPosition="10")/ToSchedules</pre>
*
* Some back-end implementations do not support more than one navigation property in the
* resource URL. In this case, set the <code>canonicalRequests</code> parameter to
* <code>true</code>. The OData model then converts the long resource URLs to canonical URLs
* and requests the data for the sales order line item's details form with the following
* requests:<pre>
* GET /<serviceUrl>/SalesOrderLineItemsSet(SalesOrderID="1",ItemPosition="10")
* GET /<serviceUrl>/SalesOrderLineItemsSet(SalesOrderID="1",ItemPosition="10")/ToSchedules</pre>
* @param {sap.ui.model.BindingMode} [mParameters.defaultBindingMode=OneWay]
* Sets the default binding mode for the model
* @param {sap.ui.model.odata.CountMode} [mParameters.defaultCountMode=Request]
* Sets the default count mode for the model
* @param {sap.ui.model.odata.OperationMode} [mParameters.defaultOperationMode=Default]
* Sets the default operation mode for the model
* @param {sap.ui.model.odata.UpdateMethod} [mParameters.defaultUpdateMethod=Merge]
* Default update method which is used for all update requests
* @param {boolean} [mParameters.disableHeadRequestForToken=false]
* Set this flag to <code>true</code> if your service does not support <code>HEAD</code>
* requests for fetching the service document (and thus the security token) to avoid sending a
* <code>HEAD</code>-request before falling back to <code>GET</code>
* @param {boolean} [mParameters.disableSoftStateHeader=false]
* Set this flag to <code>true</code> if you don´t want to start a new soft state session with
* context ID (<code>SID</code>) through header mechanism. This is useful if you want to share
* a <code>SID</code> between different browser windows
* @param {boolean} [mParameters.earlyTokenRequest=false]
* Whether the security token is requested at the earliest convenience, if parameter
* <code>tokenHandling</code> is <code>true</code>; supported since 1.79.0.
* @param {Object<string,string>} [mParameters.headers]
* Map of custom headers (name/value pairs) like {"myHeader":"myHeaderValue",...}
* @param {boolean} [mParameters.json=true]
* If set to <code>true</code>, request payloads will be JSON, XML for <code>false</code>
* @param {boolean} [mParameters.loadAnnotationsJoined]
* Whether the <code>metadataLoaded</code> event will be fired only after all annotations have
* been loaded as well
* @param {string}[mParameters.maxDataServiceVersion='2.0']
* Please use the following string format e.g. '2.0' or '3.0'. OData version supported by the
* ODataModel: '2.0'
* @param {Object<string,string>} [mParameters.metadataNamespaces]
* Map of namespace aliases (alias => URI) that can be used in metadata binding paths; each
* alias is mapped to a corresponding namespace URI; when an alias is used in a metadata
* binding path, it addresses a metadata extension that belongs to the corresponding namespace
* URI; if <code>metadataNamespaces</code> is not given, the following default mappings will
* be used:
* <ul>
* <li><code>"sap": "sap:"http://www.sap.com/Protocols/SAPData"</code></li>
* <li><code>"m": "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"</code></li>
* <li><code>"": "http://schemas.microsoft.com/ado/2007/06/edmx</code></li>
* </ul>
* @param {Object<string,string>} [mParameters.metadataUrlParams]
* Map of URL parameters for metadata requests - only attached to a <code>$metadata</code>
* request
* @param {boolean} [mParameters.persistTechnicalMessages]
* Whether technical messages should always be treated as persistent, since 1.83.0
* @param {boolean} [mParameters.preliminaryContext=false]
* Whether a preliminary context will be created/used by a binding. When set to
* <code>true</code>, the model can bundle the OData calls for dependent bindings into fewer
* $batch requests. For more information, see
* {@link topic:6c47b2b39db9404582994070ec3d57a2#loio62149734b5c24507868e722fe87a75db Optimizing Dependent Bindings}
* @param {boolean} [mParameters.refreshAfterChange=true]
* Enable/disable automatic refresh after change operations
* @param {boolean} [mParameters.sequentializeRequests=false]
* Whether to sequentialize all requests, needed in case the service cannot handle parallel
* requests
* @param {string} [mParameters.serviceUrl]
* Base URI of the service to request data from; this property is mandatory when the first
* method parameter <code>serviceUrl</code> is omitted, but ignored otherwise
* @param {Object<string,string>} [mParameters.serviceUrlParams]
* Map of URL parameters (name/value pairs) - these parameters will be attached to all
* requests, except for the <code>$metadata</code> request
* @param {boolean} [mParameters.skipMetadataAnnotationParsing]
* Whether to skip the automated loading of annotations from the metadata document. Loading
* annotations from metadata does not have any effects (except the lost performance by
* invoking the parser) if there are not annotations inside the metadata document
* @param {boolean} [mParameters.tokenHandling=true]
* Enable/disable security token handling
* @param {boolean} [mParameters.tokenHandlingForGet=false]
* Send security token for GET requests in case read access logging is activated for the OData
* Service in the backend.
* @param {boolean} [mParameters.useBatch=true]
* Whether all requests should be sent in batch requests
* @param {boolean} [mParameters.withCredentials]
* Experimental - <code>true</code> when user credentials are to be included in a cross-origin
* request; please note that this only works if all requests are asynchronous
* @param {string} [mParameters.password]
* <b>Deprecated</b> for security reasons. Use strong server side authentication instead.
* Password for the service.
* @param {string} [mParameters.user]
* <b>Deprecated</b> for security reasons. Use strong server side authentication instead.
* UserID for the service.
*
* @class
* Model implementation based on the OData protocol.
*
* See chapter {@link topic:6c47b2b39db9404582994070ec3d57a2 OData V2 Model} for a general
* introduction.
*
* This model is not prepared to be inherited from.
*
* @author SAP SE
* @version 1.111.5
*
* @public
* @alias sap.ui.model.odata.v2.ODataModel
* @extends sap.ui.model.Model
* @since 1.24.0
*/
var ODataModel = Model.extend("sap.ui.model.odata.v2.ODataModel", /** @lends sap.ui.model.odata.v2.ODataModel.prototype */ {
constructor : function(vServiceUrl, mParameters) {
Model.apply(this, arguments);
var sUser,
sPassword,
mHeaders,
bTokenHandling,
bWithCredentials,
sMaxDataServiceVersion,
bUseBatch,
bRefreshAfterChange,
sAnnotationURI,
bLoadAnnotationsJoined,
sDefaultCountMode,
bPreliminaryContext,
sDefaultBindingMode,
sDefaultOperationMode,
mMetadataNamespaces,
mServiceUrlParams,
mMetadataUrlParams,
bJSON,
oMessageParser,
bSkipMetadataAnnotationParsing,
sDefaultUpdateMethod,
bDisableHeadRequestForToken,
bSequentializeRequests,
bDisableSoftStateHeader,
aBindableResponseHeaders,
sWarmupUrl,
bCanonicalRequests,
bTokenHandlingForGet,
bEarlyTokenRequest,
bPersistTechnicalMessages,
that = this;
if (typeof (vServiceUrl) === "object") {
mParameters = vServiceUrl;
this.sServiceUrl = mParameters.serviceUrl;
} else {
this.sServiceUrl = vServiceUrl;
}
// Creates a parameters map to be used for the instantiation of the code list model,
// based on this OData model's parameters
this.mCodeListModelParams = this.createCodeListModelParameters(mParameters);
if (mParameters) {
sUser = mParameters.user;
sPassword = mParameters.password;
mHeaders = mParameters.headers;
bTokenHandling = mParameters.tokenHandling;
bWithCredentials = mParameters.withCredentials;
sMaxDataServiceVersion = mParameters.maxDataServiceVersion;
bUseBatch = mParameters.useBatch;
bRefreshAfterChange = mParameters.refreshAfterChange;
sAnnotationURI = mParameters.annotationURI;
bLoadAnnotationsJoined = mParameters.loadAnnotationsJoined;
sDefaultBindingMode = mParameters.defaultBindingMode;
sDefaultCountMode = mParameters.defaultCountMode;
bPreliminaryContext = mParameters.preliminaryContext;
sDefaultOperationMode = mParameters.defaultOperationMode;
mMetadataNamespaces = mParameters.metadataNamespaces;
mServiceUrlParams = mParameters.serviceUrlParams;
mMetadataUrlParams = mParameters.metadataUrlParams;
bJSON = mParameters.json;
oMessageParser = mParameters.messageParser;
bSkipMetadataAnnotationParsing = mParameters.skipMetadataAnnotationParsing;
sDefaultUpdateMethod = mParameters.defaultUpdateMethod;
bDisableHeadRequestForToken = mParameters.disableHeadRequestForToken;
bSequentializeRequests = mParameters.sequentializeRequests;
bDisableSoftStateHeader = mParameters.disableSoftStateHeader;
aBindableResponseHeaders = mParameters.bindableResponseHeaders;
sWarmupUrl = mParameters.warmupUrl;
bCanonicalRequests = mParameters.canonicalRequests;
bTokenHandlingForGet = mParameters.tokenHandlingForGet;
bEarlyTokenRequest = mParameters.earlyTokenRequest;
bPersistTechnicalMessages = mParameters.persistTechnicalMessages;
}
/* Path cache to avoid multiple expensive resolve operations
* this.mPathCache =
* {
* 'aBindingPath': {
* canonicalPath : 'The canonicalPath'
* }
* }
*/
this.mPathCache = {};
this.mInvalidatedPaths = {};
this.bCanonicalRequests = !!bCanonicalRequests;
this.bTokenHandlingForGet = !!bTokenHandlingForGet;
this.sMessageScope = MessageScope.RequestedObjects;
this.sWarmupUrl = sWarmupUrl;
this.bWarmup = !!sWarmupUrl;
this.mSupportedBindingModes = {"OneWay": true, "OneTime": true, "TwoWay":true};
this.mUnsupportedFilterOperators = {"Any": true, "All": true};
this.sDefaultBindingMode = sDefaultBindingMode || BindingMode.OneWay;
this.bIsMessageScopeSupported = false;
this.iPendingDeferredRequests = 0;
this.bJSON = bJSON !== false;
this.aPendingRequestHandles = [];
this.aCallAfterUpdate = [];
this.mRequests = {};
this.mDeferredRequests = {};
this.mChangedEntities = {};
this.mChangeHandles = {};
this.mDeferredGroups = {};
this.mLaunderingState = {};
this.sDefaultUpdateMethod = sDefaultUpdateMethod || UpdateMethod.Merge;
this.bTokenHandling = bTokenHandling !== false;
this.bWithCredentials = bWithCredentials === true;
this.bUseBatch = bUseBatch !== false;
this.bRefreshAfterChange = bRefreshAfterChange !== false;
this.sMaxDataServiceVersion = sMaxDataServiceVersion;
this.bLoadAnnotationsJoined = bLoadAnnotationsJoined !== false;
this.sAnnotationURI = sAnnotationURI;
this.sDefaultCountMode = sDefaultCountMode || CountMode.Request;
this.sDefaultOperationMode = sDefaultOperationMode || OperationMode.Default;
this.sMetadataLoadEvent = null;
this.oMetadataFailedEvent = null;
this.sRefreshGroupId = undefined;
this.bIncludeInCurrentBatch = false;
this.bSkipMetadataAnnotationParsing = !!bSkipMetadataAnnotationParsing;
this.bDisableHeadRequestForToken = !!bDisableHeadRequestForToken;
this.bSequentializeRequests = !!bSequentializeRequests;
this.bDisableSoftStateHeader = !!bDisableSoftStateHeader;
this.aBindableResponseHeaders = aBindableResponseHeaders ? aBindableResponseHeaders : null;
this.bPreliminaryContext = bPreliminaryContext || false;
this.mMetadataUrlParams = mMetadataUrlParams || {};
this.mChangedEntities4checkUpdate = {};
this.bPersistTechnicalMessages = bPersistTechnicalMessages === undefined
? undefined : !!bPersistTechnicalMessages;
this.oCreatedContextsCache = new _CreatedContextsCache();
// a list of functions to be called to clean up expanded lists when the side effects
// have been processed
this.aSideEffectCleanUpFunctions = [];
if (oMessageParser) {
oMessageParser.setProcessor(this);
}
this.oMessageParser = oMessageParser;
//collect internal changes in a deferred group as default
this.setDeferredGroups(["changes"]);
this.setChangeGroups({"*":{groupId: "changes"}});
this.oData = {};
this.oMetadata = null;
this.oAnnotations = null;
this.aUrlParams = [];
// for sequentialized requests, keep a promise of the last request
this.pSequentialRequestCompleted = Promise.resolve();
// Promise for request chaining
this.pReadyForRequest = Promise.resolve();
// determine the service base url and the url parameters
var aUrlParts = this.sServiceUrl.split("?");
if (aUrlParts.length > 1) {
this.sServiceUrl = aUrlParts[0];
if (aUrlParts[1]) {
this.aUrlParams.push(aUrlParts[1]);
}
}
// Remove trailing slash (if any)
this.sServiceUrl = this.sServiceUrl.replace(/\/$/, "");
// store user and password
this.sUser = sUser;
this.sPassword = sPassword;
if (Configuration.getStatisticsEnabled()) {
// add statistics parameter to every request (supported only on Gateway servers)
this.aUrlParams.push("sap-statistics=true");
}
this.oHeaders = {};
this.setHeaders(mHeaders);
if (!this.bDisableSoftStateHeader) {
this.oHeaders["sap-contextid-accept"] = "header";
this.mCustomHeaders["sap-contextid-accept"] = "header";
}
// Get/create shared data containers
var sServerUrl = this._getServerUrl();
//use warmup url if provided
this.sMetadataUrl = this.sWarmupUrl || this._createMetadataUrl("/$metadata");
this.oSharedServerData = ODataModel._getSharedData("server", sServerUrl);
this.oSharedServiceData = ODataModel._getSharedData("service", this.sServiceUrl);
this.oSharedMetaData = ODataModel._getSharedData("meta", this.sMetadataUrl);
this.bUseCache = this._cacheSupported(this.sMetadataUrl);
if (!this.oSharedMetaData.oMetadata || this.oSharedMetaData.oMetadata.bFailed) {
//create Metadata object
this.oMetadata = new ODataMetadata(this.sMetadataUrl, {
async: true,
cacheKey: this.bUseCache ? this.sMetadataUrl : undefined,
user: this.sUser,
password: this.sPassword,
headers: this.mCustomHeaders,
namespaces: mMetadataNamespaces,
withCredentials: this.bWithCredentials
});
//no caching in warmup scenario
if (!this.bWarmup) {
this.oSharedMetaData.oMetadata = this.oMetadata;
}
} else {
this.oMetadata = this.oSharedMetaData.oMetadata;
}
this.oAnnotations = new ODataAnnotations(this.oMetadata, {
source: this.sAnnotationURI,
skipMetadata: this.bSkipMetadataAnnotationParsing,
headers: this.mCustomHeaders,
combineEvents: true,
cacheKey: this._getAnnotationCacheKey(this.sMetadataUrl),
useCache: this.bUseCache
});
if (!this.bDisableSoftStateHeader) {
delete this.mCustomHeaders["sap-contextid-accept"];
}
this.oAnnotations.attachAllFailed(this.onAnnotationsFailed, this);
this.oAnnotations.attachSomeLoaded(this.onAnnotationsLoaded, this);
this.pAnnotationsLoaded = this.oAnnotations.loaded();
if (mServiceUrlParams) {
// new URL params used -> add to ones from sServiceUrl
// do this after the Metadata request is created to not put the serviceUrlParams on this one
this.aUrlParams = this.aUrlParams.concat(ODataUtils._createUrlParamsArray(mServiceUrlParams));
}
this.onMetadataFailed = function(oEvent) {
that.fireMetadataFailed(oEvent.getParameters());
};
if (!this.oMetadata.isLoaded()) {
this.oMetadata.attachFailed(this.onMetadataFailed);
}
this.oMetadata.loaded().then(function() {
that._initializeMetadata();
});
// set the header for the accepted content types
if (this.bJSON) {
if (this.sMaxDataServiceVersion === "3.0") {
this.oHeaders["Accept"] = "application/json;odata=fullmetadata";
} else {
this.oHeaders["Accept"] = "application/json";
}
} else {
this.oHeaders["Accept"] = "application/atom+xml,application/atomsvc+xml,application/xml";
}
// If a cached token for the service is already available, use it. If no service token
// is known yet, but a token for the same server, set the service token and use it.
// This prevents services having different tokens to override each other token with every
// request to the server.
if (this.bTokenHandling) {
if (this.oSharedServiceData.securityToken) {
this.oHeaders["x-csrf-token"] = this.oSharedServiceData.securityToken;
} else if (this.oSharedServerData.securityToken) {
this.oSharedServiceData.securityToken = this.oSharedServerData.securityToken;
this.oHeaders["x-csrf-token"] = this.oSharedServiceData.securityToken;
}
if (bEarlyTokenRequest) {
this.securityTokenAvailable();
}
}
this.oHeaders["Accept-Language"] = Configuration.getLanguageTag();
// set version to 2.0 because 1.0 does not support e.g. skip/top, inlinecount...
// states the version of the Open Data Protocol used by the client to generate the request.
this.oHeaders["DataServiceVersion"] = "2.0";
// the max version number the client can accept in a response
this.oHeaders["MaxDataServiceVersion"] = "2.0";
if (this.sMaxDataServiceVersion) {
this.oHeaders["MaxDataServiceVersion"] = this.sMaxDataServiceVersion;
}
// "XMLHttpRequest" indicates that a data request is performed.
// Gets only applied to non-crossOrigin requests because cross origin requests could
// result in unwanted preflight requests if this header is set.
// This behaviour is in sync with jQuery.ajax which is used by OData V4.
if (!this.mCustomHeaders["X-Requested-With"] && !isCrossOriginURL(this.sServiceUrl)) {
this.oHeaders["X-Requested-With"] = "XMLHttpRequest";
}
},
metadata : {
publicMethods : ["read", "create", "update", "remove", "submitChanges", "getServiceMetadata", "metadataLoaded",
"hasPendingChanges", "getPendingChanges", "refresh", "refreshMetadata", "resetChanges", "setDefaultCountMode",
"setDefaultBindingMode", "getDefaultBindingMode", "getDefaultCountMode",
"setProperty", "getSecurityToken", "refreshSecurityToken", "setHeaders",
"getHeaders", "setUseBatch", "setDeferredBatchGroups", "getDeferredBatchGroups",
"setChangeBatchGroups", "getChangeBatchGroups"]
}
});
//
ODataModel.M_EVENTS = {
/**
* Event is fired if the metadata document was successfully loaded
*/
MetadataLoaded: "metadataLoaded",
/**
* Event is fired if the metadata document has failed to load
*/
MetadataFailed: "metadataFailed",
/**
* Event is fired if the annotations document was successfully loaded
*/
AnnotationsLoaded: "annotationsLoaded",
/**
* Event is fired if the annotations document has failed to load
*/
AnnotationsFailed: "annotationsFailed",
/**
* Depending on the model implementation a RequestFailed should be fired if a batch request to a backend failed.
* Contains the parameters:
* message, statusCode, statusText and responseText
*
*/
BatchRequestFailed : "batchRequestFailed",
/**
* Depending on the model implementation a RequestSent should be fired when a batch request to a backend is sent.
* Contains Parameters: url, type, async
*
*/
BatchRequestSent : "batchRequestSent",
/**
* Depending on the model implementation a RequestCompleted should be fired when a batch request to a backend is completed regardless if the request failed or succeeded.
* Contains Parameters: url, type, async, success, errorobject
*
*/
BatchRequestCompleted : "batchRequestCompleted"
};
/**
* Fired, when the metadata document was successfully loaded.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#metadataLoaded
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.metadata The parsed metadata
* @public
*/
/**
* Fired, when the metadata document has failed to load.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#metadataFailed
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.metadata The parsed metadata
* @param {string} oEvent.getParameters.message A text that describes the failure.
* @param {string} oEvent.getParameters.statusCode HTTP status code returned by the request (if available)
* @param {string} oEvent.getParameters.statusText The status as a text, details not specified, intended only for diagnosis output
* @param {string} oEvent.getParameters.responseText Response that has been received for the request, as a text string
* @param {object} oEvent.getParameters.response The response object - empty object if no response
* @public
*/
/**
* Fired, when the annotations document was successfully loaded. If there are more than one annotation documents loaded then this
* event is fired if at least one document was successfully loaded. Event is fired only once for all annotation documents.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#annotationsLoaded
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {sap.ui.model.odata.v2.ODataAnnotations.Source[]} oEvent.getParameters.result An array consisting of one or several annotation sources and/or errors containing a source property and error details.
* @public
*/
/**
* Fired, when the annotations document failed to loaded. Event is fired only once for all annotation documents.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#annotationsFailed
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {Error[]} oEvent.getParameters.result An array of Errors
* @public
*/
// document event again, as parameters differ from sap.ui.model.Model#event:requestFailed
/**
* Fired, when data retrieval from a backend failed.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#requestFailed
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.ID The request ID
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} oEvent.getParameters.method The HTTP method
* @param {Object<string,string>} oEvent.getParameters.headers The request headers
* @param {boolean} oEvent.getParameters.async If the request is synchronous or asynchronous (if available)
* @param {boolean} oEvent.getParameters.success Request was successful or not
* @param {object} oEvent.getParameters.response The response object - empty object if no response
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
* @public
*/
// document event again, as parameters differ from sap.ui.model.Model#event:requestSent
/**
* Fired, after a request has been sent to a backend.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#requestSent
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.ID The request ID
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} oEvent.getParameters.method The HTTP method
* @param {Object<string,string>} oEvent.getParameters.headers The request headers
* @param {boolean} oEvent.getParameters.async If the request is synchronous or asynchronous (if available)
*
* @public
*/
// document event again, as parameters differ from sap.ui.model.Model#event:requestCompleted
/**
* Fired, after a request has been completed (includes receiving a response),
* no matter whether the request succeeded or not.
*
* Note: Subclasses might add additional parameters to the event object. Optional parameters can be omitted.
*
* @name sap.ui.model.odata.v2.ODataModel#requestCompleted
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.ID The request ID
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} oEvent.getParameters.method The HTTP method
* @param {Object<string,string>} oEvent.getParameters.headers The request headers
* @param {boolean} oEvent.getParameters.success Request was successful or not
* @param {boolean} oEvent.getParameters.async If the request is synchronous or asynchronous (if available)
* @param {object} oEvent.getParameters.response The response object - empty object if no response:
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
* @public
*/
/**
* Fired, when a batch request failed.
*
* @name sap.ui.model.odata.v2.ODataModel#batchRequestFailed
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.ID The request ID
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} oEvent.getParameters.method The HTTP method
* @param {Object<string,string>} oEvent.getParameters.headers The request headers
* @param {boolean} oEvent.getParameters.async If the request is synchronous or asynchronous (if available)
* @param {boolean} oEvent.getParameters.success Request was successful or not
* @param {object} oEvent.getParameters.response The response object - empty object if no response
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
* @param {array} oEvent.getParameters.requests Array of embedded requests ($batch)
* Each request object within the array contains the following properties: url, method, headers, response object
* @public
*/
/**
* Attaches event handler <code>fnFunction</code> to the {@link #event:batchRequestFailed batchRequestFailed} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload object that will be passed to the event handler
* along with the event object when firing the event
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object to call the event handler with. Defaults to this
* <code>sap.ui.model.odata.v2.ODataModel</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.attachBatchRequestFailed = function(oData, fnFunction, oListener) {
this.attachEvent("batchRequestFailed", oData, fnFunction, oListener);
return this;
};
/**
* Detaches event handler <code>fnFunction</code> from the {@link #event:batchRequestFailed batchRequestFailed} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* The passed function and listener object must match the ones used for event registration.
*
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object on which the given function had to be called
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.detachBatchRequestFailed = function(fnFunction, oListener) {
this.detachEvent("batchRequestFailed", fnFunction, oListener);
return this;
};
/**
* Fires event {@link #event:batchRequestFailed batchRequestFailed} to attached listeners.
*
* @param {object} oParameters Parameters to pass along with the event
* @param {string} oParameters.ID The request ID
* @param {string} oParameters.url The URL which is sent to the backend
* @param {string} oParameters.method The HTTP method
* @param {Object<string,string>} oParameters.headers The request headers
* @param {boolean} oParameters.async If the request is synchronous or asynchronous (if available)
* @param {boolean} oParameters.success Request was successful or not
* @param {object} oParameters.response The response object - empty object if no response
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
* @param {array} oParameters.requests Array of embedded requests ($batch)
* Each request object within the array contains the following properties: URL, method, headers, response object
*
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireBatchRequestFailed = function(oParameters) {
this.fireEvent("batchRequestFailed", oParameters);
return this;
};
/**
* Fired after a request has been sent to a backend.
*
* @name sap.ui.model.odata.v2.ODataModel#batchRequestSent
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} [oEvent.getParameters.type] The type of the request (if available)
* @param {boolean} [oEvent.getParameters.async] If the request is synchronous or asynchronous (if available)
* @param {array} oEvent.getParameters.requests Array of embedded requests ($batch)
* Each request object within the array contains the following properties: url, method, headers
* @public
*/
/**
* Attaches event handler <code>fnFunction</code> to the {@link #event:batchRequestSent batchRequestSent} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload object that will be passed to the event handler
* along with the event object when firing the event
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object to call the event handler with. Defaults to this
* <code>sap.ui.model.odata.v2.ODataModel</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.attachBatchRequestSent = function(oData, fnFunction, oListener) {
this.attachEvent("batchRequestSent", oData, fnFunction, oListener);
return this;
};
/**
* Detaches event handler <code>fnFunction</code> from the {@link #event:batchRequestSent batchRequestSent} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* The passed function and listener object must match the ones used for event registration.
*
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object on which the given function had to be called
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.detachBatchRequestSent = function(fnFunction, oListener) {
this.detachEvent("batchRequestSent", fnFunction, oListener);
return this;
};
/**
* Fires event {@link #event:batchRequestSent batchRequestSent} to attached listeners.
*
* @param {object} [oParameters] Parameters to pass along with the event
* @param {string} [oParameters.url] The URL which is sent to the backend.
* @param {string} [oParameters.type] The type of the request (if available)
* @param {boolean} [oParameters.async] If the request is synchronous or asynchronous (if available)
* @param {array} oParameters.requests Array of embedded requests ($batch)
* Each request object within the array contains the following properties: url, method, headers
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireBatchRequestSent = function(oParameters) {
this.fireEvent("batchRequestSent", oParameters);
return this;
};
/**
* Fired after a request has been completed (includes receiving a response),
* no matter whether the request succeeded or not.
*
* @name sap.ui.model.odata.v2.ODataModel#batchRequestCompleted
* @event
* @param {sap.ui.base.Event} oEvent
* @param {sap.ui.base.EventProvider} oEvent.getSource
* @param {object} oEvent.getParameters
* @param {string} oEvent.getParameters.ID The request ID
* @param {string} oEvent.getParameters.url The URL which is sent to the backend
* @param {string} oEvent.getParameters.method The HTTP method
* @param {Object<string,string>} oEvent.getParameters.headers The request headers
* @param {boolean} oEvent.getParameters.success Request was successful or not
* @param {boolean} oEvent.getParameters.async If the request is synchronous or asynchronous (if available)
* @param {array} oEvent.getParameters.requests Array of embedded requests ($batch)
* Each request object within the array contains the following properties: url, method, headers, response object
* @param {object} oEvent.getParameters.response The response object - empty object if no response:
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
* @public
*/
/**
* Attaches event handler <code>fnFunction</code> to the {@link #event:batchRequestCompleted batchRequestCompleted} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload object that will be passed to the event handler
* along with the event object when firing the event
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object to call the event handler with. Defaults to this
* <code>sap.ui.model.odata.v2.ODataModel</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.attachBatchRequestCompleted = function(oData, fnFunction, oListener) {
this.attachEvent("batchRequestCompleted", oData, fnFunction, oListener);
return this;
};
/**
* Detaches event handler <code>fnFunction</code> from the {@link #event:batchRequestCompleted batchRequestCompleted} event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* The passed function and listener object must match the ones used for event registration.
*
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object on which the given function had to be called
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.detachBatchRequestCompleted = function(fnFunction, oListener) {
this.detachEvent("batchRequestCompleted", fnFunction, oListener);
return this;
};
/**
* Fires event {@link #event:batchRequestCompleted batchRequestCompleted} to attached listeners.
*
* @param {object} oParameters parameters to add to the fired event
* @param {string} oParameters.ID The request ID
* @param {string} oParameters.url The URL which is sent to the backend
* @param {string} oParameters.method The HTTP method
* @param {Object<string,string>} oParameters.headers The request headers
* @param {boolean} oParameters.success Request was successful or not
* @param {boolean} oParameters.async If the request is synchronous or asynchronous (if available)
* @param {array} oParameters.requests Array of embedded requests ($batch) - empty array for non batch requests.
* Each request object within the array contains the following properties: url, method, headers, response object
* @param {object} oParameters.response The response object - empty object if no response:
* The response object contains the following properties: message, success, headers, statusCode, statusText, responseText
*
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireBatchRequestCompleted = function(oParameters) {
this.fireEvent("batchRequestCompleted", oParameters);
return this;
};
// Keep a map of shared data, which is shared across different model instances on the same server,
// OData service or metadata URL
ODataModel.mSharedData = {
server: {},
service: {},
meta: {}
};
/**
* Get the shared data for the section and the key. If it doesn't exist yet, an empty shared
* data object is created and stored under the section and the key.
*
* @param {string} sSection The data section
* @param {string} sKey A key in the section
* @returns {object} The shared data
*
* @private
*/
ODataModel._getSharedData = function(sSection, sKey) {
var oSharedData = this.mSharedData[sSection][sKey];
if (!oSharedData) {
oSharedData = {};
this.mSharedData[sSection][sKey] = oSharedData;
}
return oSharedData;
};
/**
* @private
*/
ODataModel.prototype._initializeMetadata = function() {
if (this.bDestroyed) {
// Don't fire any events for resolving promises on Models that have already been destroyed
return;
}
//check message scope
this.bIsMessageScopeSupported = this.oMetadata._isMessageScopeSupported();
var fnFire = function() {
this.fireMetadataLoaded({
metadata: this.oMetadata
});
Log.debug(this + " - metadataloaded fired");
}.bind(this);
this.initialize();
if (this.bLoadAnnotationsJoined) {
this.oAnnotations.loaded().then(fnFire, this.fireMetadataFailed.bind(this));
} else {
fnFire();
}
};
/**
* Refreshes the metadata for this model, for example when the request for metadata has failed.
*
* <b>Note</b>: Do not use <code>refreshMetadata</code> if the metadata is outdated or should be
* updated. This will lead to inconsistent data in the application.
*
* Returns a new promise which can be resolved or rejected depending on the metadata loading
* state.
*
* @returns {Promise|undefined}
* A promise on metadata loaded state or <code>undefined</code> if metadata is not initialized
* or currently refreshed
*
* @deprecated As of version 1.42.
*
* @public
*/
ODataModel.prototype.refreshMetadata = function() {
if (this.oMetadata && this.oMetadata.refresh) {
return this.oMetadata.refresh();
}
return undefined;
};
/**
* Fires event {@link #event:annotationsLoaded annotationsLoaded} to attached listeners.
*
* @param {object} [oParameters] Parameters to pass along with the event
* @param {sap.ui.model.odata.v2.ODataAnnotations} [oParameters.annotations] The annotations object
*
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireAnnotationsLoaded = function(oParameters) {
this.fireEvent("annotationsLoaded", oParameters);
return this;
};
/**
* Attaches event handler <code>fnFunction</code> to the <code>annotationsLoaded</code> event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload object that will be passed to the event handler
* along with the event object when firing the event
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object to call the event handler with. Defaults to this
* <code>sap.ui.model.odata.v2.ODataModel</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.attachAnnotationsLoaded = function(oData, fnFunction, oListener) {
this.attachEvent("annotationsLoaded", oData, fnFunction, oListener);
return this;
};
/**
* Detaches event handler <code>fnFunction</code> from the <code>annotationsLoaded</code> event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* The passed function and listener object must match the ones used for event registration.
*
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object on which the given function had to be called
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.detachAnnotationsLoaded = function(fnFunction, oListener) {
this.detachEvent("annotationsLoaded", fnFunction, oListener);
return this;
};
/**
* Fires event {@link #event:annotationsFailed annotationsFailed} to attached listeners.
*
* @param {object} [oParameters] Parameters to pass along with the event
* @param {string} [oParameters.message] A text that describes the failure
* @param {string} [oParameters.statusCode] HTTP status code returned by the request (if available)
* @param {string} [oParameters.statusText] The status as a text, details not specified, intended only for diagnosis output
* @param {string} [oParameters.responseText] Response that has been received for the request, as a text string
*
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireAnnotationsFailed = function(oParameters) {
this.fireEvent("annotationsFailed", oParameters);
Log.debug(this + " - annotationsfailed fired");
return this;
};
/**
* Attaches event handler <code>fnFunction</code> to the <code>annotationsFailed</code> event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload object that will be passed to the event handler
* along with the event object when firing the event
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object to call the event handler with. Defaults to this
* <code>sap.ui.model.odata.v2.ODataModel</code> itself
*
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.attachAnnotationsFailed = function(oData, fnFunction, oListener) {
this.attachEvent("annotationsFailed", oData, fnFunction, oListener);
return this;
};
/**
* Detaches event handler <code>fnFunction</code> from the <code>annotationsFailed</code> event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* The passed function and listener object must match the ones used for event registration.
*
* @param {function}
* fnFunction The function to be called, when the event occurs
* @param {object}
* [oListener] Context object on which the given function had to be called
* @returns {this} Reference to <code>this</code> in order to allow method chaining
* @public
*/
ODataModel.prototype.detachAnnotationsFailed = function(fnFunction, oListener) {
this.detachEvent("annotationsFailed", fnFunction, oListener);
return this;
};
/**
* Fires event {@link #event:metadataLoaded metadataLoaded} to attached listeners.
*
* @param {object} [oParameters] Parameters to pass along with the event
* @param {sap.ui.model.odata.ODataMetadata} [oParameters.metadata] the metadata object.
*
* @returns {this} Reference to <code>this</code> to allow method chaining
* @protected
*/
ODataModel.prototype.fireMetadataLoaded = function(oParameters) {
this.fireEvent("metadataLoaded", oParameters);
return this;
};
/**
* Attaches event handler <code>fnFunction</code> to the <code>metadataLoaded</code> event of this
* <code>sap.ui.model.odata.v2.ODataModel</code>.
*
* @param {object}
* [oData] An application-specific payload objec