@sapui5/sap.ushell_abap 1.90.2 → 1.90.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. package/package.json +1 -1
  2. package/src/main/js/sap/ui2/srvc/ODataWrapper.js +1297 -1274
  3. package/src/main/js/sap/ushell_abap/.library +1 -1
  4. package/src/main/js/sap/ushell_abap/adapters/abap/AppStateAdapter.js +1 -1
  5. package/src/main/js/sap/ushell_abap/adapters/abap/ClientSideTargetResolutionAdapter.js +1 -1
  6. package/src/main/js/sap/ushell_abap/adapters/abap/ConfigurationDefaultsAdapter.js +1 -1
  7. package/src/main/js/sap/ushell_abap/adapters/abap/ContainerAdapter.js +1 -1
  8. package/src/main/js/sap/ushell_abap/adapters/abap/EndUserFeedbackAdapter.js +1 -1
  9. package/src/main/js/sap/ushell_abap/adapters/abap/LaunchPageAdapter.js +1 -1
  10. package/src/main/js/sap/ushell_abap/adapters/abap/NavTargetResolutionAdapter.js +1 -1
  11. package/src/main/js/sap/ushell_abap/adapters/abap/PageBuildingAdapter.js +1 -1
  12. package/src/main/js/sap/ushell_abap/adapters/abap/PagePersistenceAdapter.js +3 -5
  13. package/src/main/js/sap/ushell_abap/adapters/abap/PersonalizationAdapter.js +1 -1
  14. package/src/main/js/sap/ushell_abap/adapters/abap/SearchAdapter.js +1 -1
  15. package/src/main/js/sap/ushell_abap/adapters/abap/SupportTicketAdapter.js +1 -1
  16. package/src/main/js/sap/ushell_abap/adapters/abap/Ui5ComponentLoaderAdapter.js +1 -1
  17. package/src/main/js/sap/ushell_abap/adapters/hana/ContainerAdapter.js +1 -1
  18. package/src/main/js/sap/ushell_abap/bootstrap/evo/abap.configure.ushell.js +1 -1
  19. package/src/main/js/sap/ushell_abap/bootstrap/evo/abap.request.catalog.js +6 -8
  20. package/src/main/js/sap/ushell_abap/bootstrap/evo/abap.request.pageset.js +10 -9
  21. package/src/main/js/sap/ushell_abap/library.js +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sapui5/sap.ushell_abap",
3
- "version": "1.90.2",
3
+ "version": "1.90.6",
4
4
  "description": "SAPUI5 Library sap.ushell_abap",
5
5
  "homepage": "https://sap.github.io/ui5-tooling/pages/SAPUI5/",
6
6
  "keywords": [
@@ -1,1274 +1,1297 @@
1
- // Copyright (c) 2009-2020 SAP SE, All Rights Reserved
2
- /**
3
- * @fileOverview A wrapper around <code>OData</code>, providing CSRF token handling, caching and
4
- * generic batch support.
5
- */
6
-
7
- (function () {
8
- "use strict";
9
- /*global jQuery, OData, sap, window */
10
- jQuery.sap.declare("sap.ui2.srvc.ODataWrapper");
11
-
12
- // Note: Only the section between @begin and @end is included in pbs-template.js.
13
- // In pbs-template fnRequire is differently initialized (in case UI5 is not available)!
14
- // Thus this variable is used in the coding below and not directly jQuery.sap.require.
15
- // avoid fnRequire = jQuery.sap.require as require cannot be spied on afterwards
16
- var fnRequire = function () {
17
- jQuery.sap.require.apply(this, arguments);
18
- };
19
- function nop() {/* null object pattern */}
20
-
21
- sap.ui2.srvc.testPublishAt = sap.ui2.srvc.testPublishAt || function () {
22
- // intentionally left blank
23
- };
24
-
25
- /// @begin
26
- // if OData is missing, automatically require SAPUI5's datajs implementation
27
- if (typeof OData !== "object") {
28
- fnRequire("sap.ui.thirdparty.datajs");
29
- }
30
-
31
- /**
32
- * Reject the given <code>jQuery.Deferred</code> object(s) (array or single one) with the
33
- * given error response.
34
- *
35
- * @param {jQuery.Deferred|jQuery.Deferred[]} vDeferred
36
- * deferred or array of deferreds to be rejected
37
- * @param {object} oResponse
38
- * parameter of (each) reject call
39
- */
40
- function reject(vDeferred, oResponse) {
41
- if (sap.ui2.srvc.isArray(vDeferred)) {
42
- // a failed change set: reject each single jQuery.Deferred in the same way
43
- vDeferred.forEach(function (oDeferred) {
44
- oDeferred.reject(oResponse);
45
- });
46
- } else {
47
- // a failed GET request
48
- vDeferred.reject(oResponse);
49
- }
50
- }
51
-
52
- // "public class" ************************************************************
53
-
54
- /**
55
- * Constructs a wrapper around <code>OData</code>, providing CSRF token handling, caching and
56
- * generic batch support. The sap-statistics header is automatically added to all requests if
57
- * the URL query parameter <code>sap-statistics=true</code> is set (see
58
- * <a href="http://help.sap.com/saphelp_nw74/helpdata/de/40/93b81292194d6a926e105c10d5048d/content.htm">
59
- * SAP Performance Statistics</a>).
60
- * If <code>OData</code> is missing, "sap.ui.thirdparty.datajs" is required automatically.
61
- * <p>
62
- * The preferred way to call the constructor is from a "sub-class" of
63
- * {@link sap.ui2.srvc.ODataService}:
64
- * <pre>
65
- * function Service() {
66
- * var oWrapper = new sap.ui2.srvc.ODataWrapper(oSettings, this);
67
- * sap.ui2.srvc.ODataService.call(this, oWrapper, fnDefaultFailure);
68
- * }
69
- * var myService = new Service();
70
- * </pre>
71
- * This provides public inheritance of <code>sap.ui2.srvc.ODataService</code> methods and
72
- * private inheritance of <code>sap.ui2.srvc.ODataWrapper</code> methods. In case you are not
73
- * providing a public "sub-class" of <code>sap.ui2.srvc.ODataService</code> but only want to
74
- * use methods from <code>sap.ui2.srvc.ODataWrapper</code>,
75
- * {@link sap.ui2.srvc.createODataWrapper} is the preferred way to construct an instance.
76
- *
77
- * @param {object} oSettings
78
- * An object containing various properties:
79
- * <pre>
80
- * {
81
- * baseUrl: "/OData/OData.svc", // Mandatory base URL of the OData service
82
- * supportsChangeSets: false, // Type: boolean, Default: false
83
- * // Whether the OData service supports change sets with <b>multiple</b>
84
- * // operations bundled into a single logical unit of work. Otherwise
85
- * // each modifying operation is isolated in a change set of its own.
86
- * "sap-language": "EN", // header which is set for all requests sent
87
- * "sap-client": 120, // header which is set for all requests sent
88
- * "sap-statistics": true // header which is set for all requests sent; in order to receive
89
- * // some performance statistics
90
- * }
91
- * </pre>
92
- * @param {sap.ui2.srvc.ODataService} oODataService
93
- * facade to any OData service, keeping track of CSRF token and default error handler
94
- * (see {@link #getODataService})
95
- *
96
- * @class
97
- * @public
98
- * @since 1.19.0
99
- */
100
- // OLD API: function (sBaseUrl, oODataService, bSupportsChangeSets)
101
- sap.ui2.srvc.ODataWrapper = function (oSettings, oODataService) {
102
- var S_STICKY_SESSION_HEADER = "saplb", // header to check during sticky session
103
- aBatchQueue, // corresponds to data.__batchRequests
104
- bChangeSetOpen, // whether a change set is currently open at the end of our batch queue
105
- aDeferredQueue, // jQuery.Deferred() objects corresponding to each request
106
- that = this,
107
- sBaseUrl,
108
- bSupportsChangeSets;
109
-
110
- // BEWARE: constructor code below!
111
-
112
- // "private" methods -------------------------------------------------------
113
-
114
- /**
115
- * Converts old api calls of the ODataWrapper constructor to the current one.
116
- * @param {object} oArgs
117
- * An arguments object containing the parameters of the constructor function. See Constructor for details.
118
- * @returns {object}
119
- * Settings for ODataWrapper in an object instead of arguments
120
- * @private
121
- */
122
- function handleOldConstructorApi(oArgs) {
123
- var oSettings = {};
124
-
125
- oSettings.baseUrl = oArgs[0];
126
- // This is not a mandatory parameter and might be undefined
127
- if (typeof oArgs[2] === 'boolean') {
128
- oSettings.supportsChangeSets = oArgs[2];
129
- }
130
-
131
- return oSettings;
132
- }
133
-
134
- /* eslint-disable no-unused-vars */
135
- /**
136
- * Getter mainly used for testing.
137
- *
138
- * @returns {object}
139
- */
140
- sap.ui2.srvc.testPublishAt(that);
141
- function getBatchQueue() {
142
- return aBatchQueue;
143
- }
144
- /* eslint-enable no-unused-vars */
145
-
146
- /**
147
- * Iterates over the given headers map and returns the first value for the requested key (case
148
- * insensitive). If no such key is found, <code>undefined</code> is returned.
149
- *
150
- * @param {string} sKey
151
- * the requested key
152
- * @param {object} [mHeaders={}]
153
- * an object treated as a <code>map&lt;string, object&gt;</code>
154
- * @returns {string}
155
- * the header value or <code>undefined</code> if the header was not found
156
- */
157
- sap.ui2.srvc.testPublishAt(that);
158
- function headerValue(sKey, mHeaders) {
159
- sKey = sKey.toLowerCase();
160
- for (var sCurrentKey in mHeaders) {
161
- if (Object.prototype.hasOwnProperty.call(mHeaders, sCurrentKey)
162
- && sCurrentKey.toLowerCase() === sKey) {
163
- return mHeaders[sCurrentKey];
164
- }
165
- }
166
- return undefined;
167
- }
168
-
169
- /**
170
- * Gets an object supposed to be the headers object used for OData requests.
171
- * Adds headers set as static properties of sap.ui2.srvc.ODataWrapper or as part of Constructor settings.
172
- * The following headers may be added:
173
- * - sap-language
174
- * - sap-statistics
175
- * - sap-client
176
- *
177
- * @param {object} [oHeaders={}]
178
- * optional object supposed to be the headers object used for OData requests
179
- * @returns {object}
180
- * returns oHeader (if not given, a new object is created) additional headers may be added as
181
- * properties to the object.
182
- */
183
- function addGlobalSapHeaders(oHeaders) {
184
- var sSapLanguage = oSettings["sap-language"] || sap.ui2.srvc.ODataWrapper["sap-language"],
185
- sSapStatistics = oSettings["sap-statistics"] || sap.ui2.srvc.ODataWrapper["sap-statistics"],
186
- sSapClient = oSettings["sap-client"] || sap.ui2.srvc.ODataWrapper["sap-client"];
187
-
188
- oHeaders = oHeaders || {};
189
- if (sSapLanguage) {
190
- oHeaders["sap-language"] = sSapLanguage;
191
- }
192
- if (sSapStatistics || (sSapStatistics === "false")) {
193
- // If sSapStatistics is set, all requests done via ODataWrapper, will contain the
194
- // sap-statistics header. See
195
- // http://help.sap.com/saphelp_nw74/helpdata/de/40/93b81292194d6a926e105c10d5048d/content.htm
196
- oHeaders["sap-statistics"] = "" + sSapStatistics;
197
- }
198
- if (sSapClient) {
199
- oHeaders["sap-client"] = sSapClient;
200
- }
201
- return oHeaders;
202
- }
203
-
204
- /**
205
- * Adds previously detected sticky session headers to the given object
206
- * (which is supposed to be the headers object used for OData requests).
207
- *
208
- * @param {object} [oHeaders={}]
209
- * optional object supposed to be the headers object used for OData requests
210
- * @returns {object}
211
- * returns oHeader (if not given, a new object is created) additional
212
- * headers may be added as properties to the object.
213
- */
214
- function addStickySessionHeader(oHeaders) {
215
- oHeaders = oHeaders || {};
216
-
217
- var oStickySessionConfiguration =
218
- sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl];
219
-
220
- /*
221
- * NOTE: not assuming oStickySessionConfiguration is defined (see comment
222
- * on assigment of sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration)
223
- * down in this file.
224
- */
225
- if (typeof oStickySessionConfiguration === "undefined") {
226
- return oHeaders;
227
- }
228
-
229
- if (oStickySessionConfiguration && oStickySessionConfiguration.enabled &&
230
- typeof oStickySessionConfiguration.value !== "undefined") {
231
-
232
- oHeaders[S_STICKY_SESSION_HEADER] = oStickySessionConfiguration.value;
233
- }
234
-
235
- return oHeaders;
236
- }
237
-
238
- /**
239
- * Detects sticky session based on the given response headers and, in case
240
- * of success, updates the static
241
- * sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration with the sticky
242
- * session value found in the header.
243
- *
244
- * @param {object} oResponseHeaders
245
- * the response headers
246
- */
247
- sap.ui2.srvc.testPublishAt(that);
248
- function detectStickySession(oResponseHeaders) {
249
- var sHeaderValue,
250
- oStickySessionConfiguration =
251
- sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl];
252
-
253
- if (!oStickySessionConfiguration || !oStickySessionConfiguration.enabled) {
254
- return;
255
- }
256
-
257
- // add sticky session header value if found
258
- sHeaderValue = headerValue(S_STICKY_SESSION_HEADER, oResponseHeaders);
259
- if (typeof sHeaderValue === "undefined") {
260
- return;
261
- }
262
-
263
- if (oStickySessionConfiguration.value !== sHeaderValue) { // take the last value from server
264
- oStickySessionConfiguration.value = sHeaderValue;
265
- }
266
- }
267
-
268
- /**
269
- * Iterates over the given headers map and returns the first value for the well-known
270
- * "X-CSRF-Token" key (case insensitive). If no such key is found, <code>""</code> is
271
- * returned.
272
- *
273
- * @param {object} [mHeaders={}]
274
- * an object treated as a <code>map&lt;string, object&gt;</code>
275
- * @returns {string}
276
- */
277
- sap.ui2.srvc.testPublishAt(that);
278
- function csrfTokenValue(mHeaders) {
279
- return headerValue("x-csrf-token", mHeaders) || "";
280
- }
281
-
282
- /**
283
- * Wrapper around
284
- * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.request">
285
- * <code>OData.request</code></a> which is able to automatically fetch a CSRF token if
286
- * required.
287
- *
288
- * @param {string} sRequestUrl
289
- * a string containing the <b>absolute</b> URL
290
- * @param {string} sMethod
291
- * the HTTP method to use, e.g. "POST"
292
- * @param {object} oPayload
293
- * payload of the request (in intermediate format)
294
- * @param {function(object)} [fnSuccess]
295
- * callback function that is executed if the request succeeds, taking the processed data
296
- * @param {function (string, object=)} [fnFailure]
297
- * error handler taking an error message and, since version 1.28.6, an
298
- * optional object containing the complete error information as delivered
299
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
300
- * for more details.
301
- * Defaults to the OData service facade's default error handler
302
- * @param {object} [oHandler]
303
- * (OData/datajs) handler for the response data
304
- * @param {boolean} [bIsRepeatedRequest]
305
- * This function recursively calls itself to re-execute it in case the CSRF token was invalid and has been fetched.
306
- * To prevent endless loops when the server returns an invalid token, this flag is used.
307
- */
308
- sap.ui2.srvc.testPublishAt(that);
309
- function doRequest(sRequestUrl, sMethod, oPayload, fnSuccess, fnFailure, oHandler, bIsRepeatedRequest) {
310
- var oHeaders;
311
- fnSuccess = fnSuccess || nop;
312
- fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
313
- that.check(fnSuccess, fnFailure);
314
- oHeaders = {
315
- 'Accept': "application/json",
316
- 'Accept-Language': (sap.ui && sap.ui.getCore().getConfiguration().getLanguage()) || "",
317
- 'X-CSRF-Token': oODataService.getCsrfToken()
318
- };
319
-
320
- addGlobalSapHeaders(oHeaders);
321
- addStickySessionHeader(oHeaders);
322
-
323
- OData.request({
324
- requestUri: sRequestUrl,
325
- method: sMethod,
326
- data: oPayload,
327
- headers : oHeaders
328
- },
329
- function (oData, oResponse) {
330
- detectStickySession((oResponse || {}).headers);
331
- jQuery.sap.log.debug("Received OData response for " + sMethod + ' "' + sRequestUrl + '"',
332
- null,
333
- "sap.ui2.srvc.ODataWrapper");
334
- // Note: drop excess parameters; try/catch
335
- sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
336
- },
337
- function (oError) {
338
- // wrappers for success & failure handlers to reset flag
339
- function failure() {
340
- fnFailure.apply(null, arguments);
341
- }
342
- function success() {
343
- fnSuccess.apply(null, arguments);
344
- }
345
-
346
- if (!bIsRepeatedRequest
347
- && oError.response.statusCode === 403
348
- && csrfTokenValue(oError.response.headers).toLowerCase() === "required") {
349
- // refresh CSRF token and repeat original request
350
- jQuery.sap.log.debug("CSRF token required for " + sMethod + ' "' + sRequestUrl
351
- + '", refreshing it', JSON.stringify(oError.response),
352
- "sap.ui2.srvc.ODataWrapper");
353
-
354
- oODataService.refreshCsrfToken(
355
- doRequest.bind(that, sRequestUrl, sMethod, oPayload, success, failure, oHandler, /*bIsRepeatedRequest*/ true),
356
- failure
357
- );
358
- } else {
359
- that.onError(sMethod, sRequestUrl, fnFailure, /*oDeferred*/null, oError);
360
- }
361
- },
362
- oHandler);
363
- jQuery.sap.log.debug("Sent OData request for " + sMethod + ' "' + sRequestUrl + '"', null,
364
- "sap.ui2.srvc.ODataWrapper");
365
- }
366
-
367
- /**
368
- * Transforms the given absolute URL into a relative URL w.r.t. this OData wrapper's base URL,
369
- * removing protocol, host, port, and base URL, but preserving query parameters and a fragment
370
- * part.
371
- *
372
- * @param {string} sRequestUrl
373
- * a string containing an <b>absolute</b> URL, e.g.
374
- * "http://acme.corp/OData/OData.svc/Products(1)?$foo=bar#abc"
375
- * @returns {string}
376
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
377
- * "Products(1)?$foo=bar#abc"
378
- */
379
- sap.ui2.srvc.testPublishAt(that);
380
- function toRelativeUrl(sRequestUrl) {
381
- var i = sRequestUrl.indexOf(sBaseUrl);
382
- if (i < 0) {
383
- throw new sap.ui2.srvc.Error('Not relative to base URL "' + sBaseUrl + '": ' + sRequestUrl,
384
- "sap.ui2.srvc.ODataWrapper");
385
- }
386
- return sRequestUrl.slice(i + sBaseUrl.length);
387
- }
388
-
389
- /**
390
- * Transforms the given relative URL into an absolute URL w.r.t. this OData wrapper's base URL,
391
- * making sure there is exactly one slash in between.
392
- *
393
- * @param {string} sRelativeUrl
394
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
395
- * "Products(1)"
396
- * @returns {string}
397
- * a string containing the <b>absolute</b> URL, e.g. "/OData/OData.svc/Products(1)"
398
- */
399
- sap.ui2.srvc.testPublishAt(that);
400
- function toRequestUrl(sRelativeUrl) {
401
- if (/^\//.test(sRelativeUrl)) {
402
- throw new sap.ui2.srvc.Error("Not a relative URL: " + sRelativeUrl,
403
- "sap.ui2.srvc.ODataWrapper");
404
- }
405
- return sBaseUrl + sRelativeUrl;
406
- }
407
-
408
- /**
409
- * Wrapper around
410
- * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.read">
411
- * <code>OData.read</code></a> which supports queuing up for "$batch" requests.
412
- *
413
- * @param {string} sRelativeUrl
414
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
415
- * "Products(1)"
416
- * @param {function(object,object)} fnRawSuccess
417
- * a callback function that is executed if the request succeeds, taking the processed data
418
- * and the response object
419
- * @param {function (object)} fnRawFailure
420
- * a callback function that is executed if the request fails, taking an error object
421
- */
422
- sap.ui2.srvc.testPublishAt(that);
423
- function readOrBatch(sRelativeUrl, fnRawSuccess, fnRawFailure) {
424
- var oDeferred,
425
- sRequestUrl = toRequestUrl(sRelativeUrl),
426
- oHeaders = addStickySessionHeader(addGlobalSapHeaders()); // may return an empty object
427
-
428
- if (aBatchQueue) {
429
- jQuery.sap.log.debug('Queued OData request for GET "' + sRelativeUrl + '"', null,
430
- "sap.ui2.srvc.ODataWrapper");
431
- aBatchQueue.push({
432
- method: "GET",
433
- requestUri: sRelativeUrl,
434
- headers: oHeaders
435
- });
436
- oDeferred = (new jQuery.Deferred()).done(fnRawSuccess).fail(fnRawFailure);
437
- aDeferredQueue.push(oDeferred);
438
- bChangeSetOpen = false;
439
- return;
440
- }
441
-
442
- // add specifc headers for read request
443
- oHeaders["Accept"] = "application/json";
444
- oHeaders["Accept-Language"] = (sap.ui && sap.ui.getCore().getConfiguration().getLanguage()) || "";
445
- // always fetch a new token with GET requests;
446
- // this avoids using old tokens from responses served from browser cache
447
- // see internal BCP incident 1570753380
448
- oHeaders["X-CSRF-Token"] = "Fetch";
449
-
450
- OData.read({
451
- requestUri: sRequestUrl,
452
- headers: oHeaders
453
- }, fnRawSuccess, fnRawFailure);
454
- jQuery.sap.log.debug('Sent OData request for GET "' + sRequestUrl + '"', null,
455
- "sap.ui2.srvc.ODataWrapper");
456
- }
457
-
458
- /**
459
- * Wrapper around {@link #doRequest} which supports queuing up for "$batch" requests.
460
- *
461
- * @param {string} sRelativeUrl
462
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
463
- * "Products"
464
- * @param {string} sMethod
465
- * the HTTP method to use, either "POST", "PUT" or "DELETE" (retrieve requests using "GET"
466
- * must be made via {@link #readOrBatch} instead!)
467
- * @param {object} oPayload
468
- * payload of the request (in intermediate format)
469
- * @param {function(object)} [fnSuccess]
470
- * callback function that is executed if the request succeeds, taking the processed data
471
- * @param {function (string, object=)} [fnFailure]
472
- * error handler taking an error message and, since version 1.28.6, an
473
- * optional object containing the complete error information as delivered
474
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
475
- * for more details.
476
- * Defaults to the OData service facade's default error handler
477
- */
478
- sap.ui2.srvc.testPublishAt(that);
479
- function requestOrBatch(sRelativeUrl, sMethod, oPayload, fnSuccess, fnFailure) {
480
- /*jslint nomen:true */
481
- var oDeferred,
482
- oChangeRequest = {
483
- data: oPayload,
484
- method: sMethod,
485
- requestUri: sRelativeUrl,
486
- headers: addStickySessionHeader(addGlobalSapHeaders()) // may return an empty object
487
- },
488
- sRequestUrl = toRequestUrl(sRelativeUrl);
489
-
490
- if (aBatchQueue) {
491
- fnSuccess = fnSuccess || nop;
492
- fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
493
- that.check(fnSuccess, fnFailure);
494
- jQuery.sap.log.debug("Queued OData request for " + sMethod + ' "' + sRelativeUrl + '"',
495
- null, "sap.ui2.srvc.ODataWrapper");
496
- if (!bChangeSetOpen) {
497
- aBatchQueue.push({__changeRequests: []});
498
- aDeferredQueue.push([]);
499
- bChangeSetOpen = bSupportsChangeSets;
500
- }
501
- aBatchQueue[aBatchQueue.length - 1].__changeRequests.push(oChangeRequest);
502
- oDeferred = (new jQuery.Deferred())
503
- .done(function (oData, oResponse) {
504
- jQuery.sap.log.debug("Received OData response for "
505
- + sMethod + ' "' + sRequestUrl + '"', null, "sap.ui2.srvc.ODataWrapper");
506
- // Note: drop excess parameters; try/catch
507
- sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
508
- })
509
- .fail(that.onError.bind(that, sMethod, sRequestUrl, fnFailure, /*oDeferred*/null));
510
- aDeferredQueue[aDeferredQueue.length - 1].push(oDeferred);
511
- return;
512
- }
513
-
514
- doRequest(sRequestUrl, sMethod, oPayload, fnSuccess, fnFailure);
515
- }
516
-
517
- // "public" methods --------------------------------------------------------
518
-
519
- /**
520
- * Checks whether session stickyness is configured and enabled
521
- *
522
- * @returns {boolean}
523
- * whether the sticky session header is configured and active
524
- *
525
- * @public
526
- *
527
- * @since 1.30.0
528
- */
529
- this.isStickySessionEnabled = function() {
530
- return (sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] || {}).enabled || false;
531
- };
532
-
533
- /**
534
- * <p>
535
- * Configures and activates session stickiness.
536
- * </p>
537
- * <p>
538
- * Session stickiness allows the client to execute OData requests to the
539
- * same application server. This is achieved by copying a certain sticky
540
- * session header
541
- * (found in the OData response from the server) to the OData request made
542
- * by ODataWrapper, indicating the load balancer that requests should be
543
- * made against a certain application server. Once detected, the sticky
544
- * header is automatically shared by all instances of
545
- * <code>sap.ui2.srvc.ODataWrapper</code> connected to exactly the same
546
- * base URL.
547
- * </p>
548
- * <p>
549
- * NOTE: The sticky session header sent from the server always overrides
550
- * the last sticky session header requested. This is to avoid that the
551
- * feature is disabled if the load balancer returns another value for the
552
- * sticky session header for some reason (e.g. the application server goes
553
- * offline).
554
- * </p>
555
- * <p>
556
- * NOTE: In line with {@link sap.ui2.srvc.PageBuildingService}, session
557
- * stickiness is only supported in scopes different from PERS.
558
- * </p>
559
- * <p>
560
- * Currently, session stickiness is affected by the following limitations:
561
- * <ul>
562
- * <li>If the client caches the response headers for certain requests, these
563
- * cached headers will be used instead of the headers sent from the server
564
- * (as in if cache was disabled).</li>
565
- * <li>If initial requests to different URLs that share the same base URL are
566
- * performed asynchronously (e.g. through multiple instances of
567
- * ODataWrapper), sticky session may be disabled for a part or all of these
568
- * initial requests. This is because the requests are made before the first
569
- * response with a sticky session header is obtained.</li>
570
- * <li>This mechanism only guarantees that requests are forwarded to the
571
- * specified application server. For example, if the application server in
572
- * turn contacts further load balanced servers, the session may not be
573
- * maintain and load balancing may still occur.</li>
574
- * </ul>
575
- * </p>
576
- * @public
577
- *
578
- * @since 1.30.0
579
- */
580
- this.enableStickySession = function() {
581
- sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl].enabled = true;
582
- };
583
-
584
- /**
585
- * Checks that the given callback functions are really functions. This check
586
- * is useful to "fail fast" because these callbacks are not called immediately.
587
- *
588
- * @param {function} fnSuccess
589
- * success callback
590
- * @param {function} fnFailure
591
- * error callback
592
- *
593
- * @public
594
- * @since 1.19.0
595
- */
596
- this.check = function (fnSuccess, fnFailure) {
597
- if (!fnSuccess) {
598
- throw new sap.ui2.srvc.Error("Missing success callback",
599
- "sap.ui2.srvc.ODataWrapper");
600
- }
601
- if (typeof fnSuccess !== "function") {
602
- throw new sap.ui2.srvc.Error("Success callback is not a function",
603
- "sap.ui2.srvc.ODataWrapper");
604
- }
605
- if (!fnFailure) {
606
- throw new sap.ui2.srvc.Error("Missing error callback",
607
- "sap.ui2.srvc.ODataWrapper");
608
- }
609
- if (typeof fnFailure !== "function") {
610
- throw new sap.ui2.srvc.Error("Error callback is not a function",
611
- "sap.ui2.srvc.ODataWrapper");
612
- }
613
- };
614
-
615
- /**
616
- * Wrapper around <code>OData.request</code> which is able to automatically fetch a CSRF token
617
- * if required. It uses POST as a method.
618
- *
619
- * @param {string} sRelativeUrl
620
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
621
- * "Products"
622
- * @param {object} oPayload
623
- * payload of the request (in intermediate format)
624
- * @param {function(object)} [fnSuccess]
625
- * callback function that is executed if the request succeeds, taking the processed data
626
- * @param {function (string, object=)} [fnFailure]
627
- * error handler taking an error message and, since version 1.28.6, an
628
- * optional object containing the complete error information as delivered
629
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
630
- * for more details.
631
- * Defaults to the OData service facade's default error handler
632
- *
633
- * @public
634
- * @since 1.19.0
635
- */
636
- this.create = function (sRelativeUrl, oPayload, fnSuccess, fnFailure) {
637
- requestOrBatch(sRelativeUrl, "POST", oPayload, fnSuccess, fnFailure);
638
- };
639
-
640
- /**
641
- * Wrapper around <code>OData.request</code> which is able to automatically fetch a CSRF token
642
- * if required. It uses DELETE as a method.
643
- *
644
- * @param {string|object} vEntity
645
- * either a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL,
646
- * e.g. "Products(1)", or the datajs representation of the entity
647
- * @param {function()} [fnSuccess]
648
- * callback function that is executed if the request succeeds, taking no data
649
- * @param {function (string, object=)} [fnFailure]
650
- * error handler taking an error message and, since version 1.28.6, an
651
- * optional object containing the complete error information as delivered
652
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
653
- * for more details.
654
- * Defaults to the OData service facade's default error handler
655
- *
656
- * @public
657
- * @since 1.19.0
658
- */
659
- this.del = function (vEntity, fnSuccess, fnFailure) {
660
- /*jslint nomen:true */
661
- var sRelativeUrl = vEntity;
662
-
663
- if (typeof sRelativeUrl !== "string") {
664
- sRelativeUrl = toRelativeUrl(vEntity.__metadata.uri);
665
- }
666
-
667
- requestOrBatch(sRelativeUrl, "DELETE", null, function (oData) {
668
- if (fnSuccess) {
669
- // Note: try/catch already done by doRequest()
670
- fnSuccess(); // drop excess parameters (oData === undefined)
671
- }
672
- }, fnFailure);
673
- };
674
-
675
- /**
676
- * Returns the wrapper's base URL.
677
- *
678
- * @returns {string}
679
- * base URL of the OData service, e.g. "/OData/OData.svc/"
680
- *
681
- * @public
682
- * @since 1.19.0
683
- */
684
- this.getBaseUrl = function () {
685
- return sBaseUrl;
686
- };
687
-
688
- /**
689
- * Returns this wrapper's facade to an OData service (which was passed to the constructor
690
- * {@link sap.ui2.srvc.ODataWrapper}).
691
- *
692
- * @returns {sap.ui2.srvc.ODataService}
693
- * this wrapper's facade to an OData service.
694
- *
695
- * @public
696
- * @since 1.19.1
697
- */
698
- this.getODataService = function () {
699
- return oODataService;
700
- };
701
-
702
- /**
703
- * Wraps the given generic OData failure handler. It processes the raw
704
- * OData error response object, calls the given failure handler with an
705
- * error message and, since version 1.28.6, an object containing additional
706
- * technical details. If a Deferred object is given, it is rejected with
707
- * the same arguments passed to the failure handler call.
708
- *
709
- * This method logs technical information to the console if this is
710
- * available at the time the error occurs.
711
- *
712
- * @param {string} sMethod
713
- * the HTTP method used in the OData request, e.g. "POST"
714
- * @param {string} sRequestUrl
715
- * the <b>absolute</b> URL the request is sent to
716
- * @param {function (string, object=)} fnFailure
717
- * the wrapped failure handler that will be called synchronously. The
718
- * first parameter is a human-readable error message containing technical
719
- * information, including sMethod and sRequestUrl; the second parameter
720
- * is an <b>optional</b> object containing the complete error information
721
- * returned in the <code>error</code> value contained in the body of the
722
- * OData error response object, plus the HTTP response status code.
723
- *
724
- * For example, the returned error object has the following structure:
725
- * <pre>
726
- * {
727
- * httpStatus: 404,
728
- * // ... other keys and values from oError.response.body.error
729
- * }
730
- * </pre>
731
- *
732
- * Please refer to the documentation of the specific OData service used
733
- * for details about keys and values returned in
734
- * <code>oError.response.body.error</code>.<br />
735
- *
736
- * <b>IMPORTANT:</b> the second parameter may be undefined if the error
737
- * cannot be parsed or is not returned in the OData error response.<br />
738
- *
739
- * <b>NOTE:</b> the second parameter is returned since version 1.28.6.
740
- * @param {jQuery.Deferred} [oDeferred]
741
- * a <code>jQuery.Deferred</code> object that will be rejected with the
742
- * same arguments fnFailure is called
743
- * @param {object} oError
744
- * error object provided by datajs, should contain the response
745
- *
746
- * @public
747
- * @since 1.19.0
748
- */
749
- this.onError = function (sMethod, sRequestUrl, fnFailure, oDeferred, oError) {
750
- var oParsedErrorInformation,
751
- sMessage = "Error ";
752
-
753
- if (oError.response && oError.response.statusCode) {
754
- sMessage += "(" + oError.response.statusCode + ", " + oError.response.statusText + ") ";
755
- }
756
- sMessage += "in OData response for " + sMethod + ' "' + sRequestUrl + '": ' + oError.message;
757
-
758
- if (oError.response && oError.response.body) {
759
-
760
- try {
761
- oParsedErrorInformation = JSON.parse(oError.response.body).error;
762
-
763
- if (oParsedErrorInformation) {
764
-
765
- if (oParsedErrorInformation.hasOwnProperty("message") &&
766
- oParsedErrorInformation.message.hasOwnProperty("value")) {
767
- sMessage += "\nDetails: " + oParsedErrorInformation.message.value;
768
- }
769
-
770
- // NOTE: there was an agreement to not alter the data in
771
- // oError.response.body.error (except from adding the status if
772
- // it's not there already)
773
- if (oError.response.statusCode && !oParsedErrorInformation.httpStatus) {
774
- oParsedErrorInformation.httpStatus = oError.response.statusCode;
775
- }
776
- }
777
- } catch (ex) {
778
- // do not rely on subtleties of error response, treat error details as optional
779
- }
780
- }
781
-
782
- jQuery.sap.log.error(sMessage, JSON.stringify(oError.response),
783
- "sap.ui2.srvc.ODataWrapper");
784
-
785
- if (oDeferred) {
786
- oDeferred.reject(sMessage, oParsedErrorInformation);
787
- }
788
- fnFailure(sMessage, oParsedErrorInformation);
789
- };
790
-
791
- /**
792
- * Opens a new queue where all requests are parked until a call to {@link #submitBatchQueue}.
793
- *
794
- * @public
795
- * @since 1.19.0
796
- *
797
- * @see #isBatchQueueOpen
798
- * @see #submitBatchQueue
799
- */
800
- this.openBatchQueue = function () {
801
- if (aBatchQueue) {
802
- throw new sap.ui2.srvc.Error("Batch queue already open", "sap.ui2.srvc.ODataWrapper");
803
- }
804
-
805
- aBatchQueue = [];
806
- aDeferredQueue = [];
807
- bChangeSetOpen = false;
808
- };
809
-
810
- /**
811
- * Checks whether the queue of requests is already open or not
812
- *
813
- * @returns {boolean}
814
- * true if batchQueue is already open
815
- *
816
- * @public
817
- * @since 1.34.0
818
- *
819
- * @see #openBatchQueue
820
- * @see #submitBatchQueue
821
- */
822
- this.isBatchQueueOpen = function() {
823
- return !!aBatchQueue;
824
- };
825
-
826
- /**
827
- * Wrapper around
828
- * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.read">
829
- * <code>OData.read</code></a> which supports caching.
830
- *
831
- * @param {string} sRelativeUrl
832
- * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
833
- * "Products(1)"
834
- * @param {function(object)} fnSuccess
835
- * a callback function that is executed if the request succeeds, taking the processed data
836
- * @param {function (string, object=)} [fnFailure]
837
- * error handler taking an error message and, since version 1.28.6, an
838
- * optional object containing the complete error information as delivered
839
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
840
- * for more details.
841
- * Defaults to the OData service facade's default error handler
842
- * ({@link sap.ui2.srvc.ODataService#getDefaultErrorHandler})
843
- * @param {boolean} [bCache=false]
844
- * whether the response is cached for further calls (since 1.11.0) in
845
- * <code>OData.read.$cache</code>, a <code>sap.ui2.srvc.Map</code> from
846
- * <code>sRequestUrl</code> to a <code>jQuery.Deferred</code> object created on demand;
847
- * without this flag, the cache is neither written nor read!
848
- *
849
- * @public
850
- * @since 1.19.0
851
- */
852
- this.read = function (sRelativeUrl, fnSuccess, fnFailure, bCache) {
853
- var oDeferred,
854
- sRequestUrl = toRequestUrl(sRelativeUrl),
855
- sSapMessage;
856
-
857
- /*
858
- * Success handler for <code>OData.read</code>.
859
- */
860
- function success(oData, oResponse) {
861
- detectStickySession(oResponse.headers);
862
- oODataService.setCsrfToken(
863
- csrfTokenValue(oResponse.headers) || oODataService.getCsrfToken() // prefer a new token
864
- );
865
- jQuery.sap.log.debug('Received OData response for GET "' + sRequestUrl + '"', null,
866
- "sap.ui2.srvc.ODataWrapper");
867
- // The sap-message header is a Gateway feature used e.g. by the INTEROP service
868
- // It returns structured information (as XML). We don't evaluate it, we only log it.
869
- // Severity is also part of the structure, but we simply assume warning.
870
- sSapMessage = headerValue("sap-message", oResponse.headers);
871
- if (sSapMessage) {
872
- jQuery.sap.log.warning("SAP message for GET " + sRequestUrl, sSapMessage,
873
- "sap.ui2.srvc.ODataWrapper");
874
- }
875
- if (oDeferred) {
876
- // put arguments for fnSuccess into cache; clone oData first
877
- oDeferred.resolve(JSON.parse(JSON.stringify(oData)), oODataService.getCsrfToken());
878
- }
879
- // Note: drop excess parameters; try/catch
880
- sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
881
- }
882
-
883
- fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
884
- this.check(fnSuccess, fnFailure);
885
-
886
- if (bCache) {
887
- OData.read.$cache = OData.read.$cache || new sap.ui2.srvc.Map();
888
- oDeferred = OData.read.$cache.get(sRequestUrl); // the promise is cached
889
- if (oDeferred) {
890
- jQuery.sap.log.debug('Using cached response for GET "' + sRequestUrl + '"', null,
891
- "sap.ui2.srvc.ODataWrapper");
892
- oDeferred.done(function (oData, sCachedCsrfToken) {
893
- // prefer our own token
894
- oODataService.setCsrfToken(oODataService.getCsrfToken() || sCachedCsrfToken);
895
- // clone cached oData before passing to success handler
896
- // Note: try/catch
897
- sap.ui2.srvc.call(fnSuccess.bind(null, JSON.parse(JSON.stringify(oData))), fnFailure);
898
- }).fail(fnFailure);
899
- return;
900
- }
901
- oDeferred = new jQuery.Deferred();
902
- OData.read.$cache.put(sRequestUrl, oDeferred.promise());
903
- }
904
-
905
- readOrBatch(
906
- sRelativeUrl,
907
- success,
908
- this.onError.bind(this, "GET", sRequestUrl, fnFailure, oDeferred)
909
- );
910
- };
911
-
912
- /**
913
- * Performs a batch request with the given payload.
914
- *
915
- * <b>Warning:</b> This bypasses the batch queue.
916
- *
917
- * @param {object} oPayload
918
- * payload of the request (in intermediate format)
919
- * @param {function(object)} [fnSuccess]
920
- * callback function that is executed if the request succeeds, taking the processed data
921
- * @param {function (string, object=)} [fnFailure]
922
- * error handler taking an error message and, since version 1.28.6, an
923
- * optional object containing the complete error information as delivered
924
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
925
- * for more details.
926
- * Defaults to the OData service facade's default error handler
927
- * @private
928
- */
929
- this.batch = function (oPayload, fnSuccess, fnFailure) {
930
- var sRequestUrl = toRequestUrl("$batch");
931
-
932
- doRequest(sRequestUrl, "POST", oPayload, fnSuccess, fnFailure, OData.batchHandler);
933
- };
934
-
935
- /**
936
- * Submits the current batch queue opened by {@link #openBatchQueue} by sending a single
937
- * "$batch" request to the OData service and deletes the current batch queue immediately.
938
- *
939
- * @param {function()} [fnBatchAccepted]
940
- * A callback function that is executed if the batch request is accepted by the
941
- * server, no matter whether individual operations fail. It will be called <b>after</b> all
942
- * success or failure handlers of individual operations.
943
- * @param {function(string, object=)} [fnBatchItselfFailed]
944
- * A callback function that is executed if the batch request itself
945
- * fails, error handler taking an error message and, since version
946
- * 1.28.6, an optional object containing the complete error information
947
- * as delivered by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
948
- * for more details.<br />
949
- *
950
- * Defaults to the OData service facade's default
951
- * error handler ({@link sap.ui2.srvc.ODataService#getDefaultErrorHandler}).<br />
952
- *
953
- * This is called <b>instead</b> of individual failure handlers in this case!
954
- *
955
- * @public
956
- * @since 1.19.0
957
- *
958
- * @see #openBatchQueue
959
- * @see #isBatchQueueOpen
960
- */
961
- this.submitBatchQueue = function (fnBatchAccepted, fnBatchItselfFailed) {
962
- /*jslint nomen:true */
963
- var aMyDeferredQueue = aDeferredQueue; // used in closure below
964
-
965
- /*
966
- * Success handler for $batch request.
967
- *
968
- * @param {object} oData
969
- */
970
- function onBatchSuccess(oData) {
971
- var iActual = oData.__batchResponses.length,
972
- iExpected = aMyDeferredQueue.length;
973
-
974
- if (iExpected !== iActual) {
975
- that.onError("POST", toRequestUrl("$batch"), fnBatchItselfFailed, /*oDeferred*/null, {
976
- message: "Protocol error! Expected " + iExpected
977
- + " responses, but received " + iActual
978
- });
979
- return;
980
- }
981
-
982
- oData.__batchResponses.forEach(function (oResponse, i) {
983
- // Note: "raw" success/failure signatures for all requests!
984
- var oDeferred = aMyDeferredQueue[i];
985
- if (oResponse.response) {
986
- // if it contains a nested response, it must be a failure
987
- reject(oDeferred, oResponse);
988
- } else if (oResponse.__changeResponses) {
989
- // successful change set
990
- oResponse.__changeResponses.forEach(function (oChangeResponse, j) {
991
- oDeferred[j].resolve(oChangeResponse.data, oChangeResponse);
992
- });
993
- } else {
994
- // successful GET request
995
- oDeferred.resolve(oResponse.data, oResponse);
996
- }
997
- });
998
-
999
- if (fnBatchAccepted) {
1000
- sap.ui2.srvc.call(fnBatchAccepted, oODataService.getDefaultErrorHandler());
1001
- }
1002
- }
1003
-
1004
- if (!aBatchQueue) {
1005
- throw new sap.ui2.srvc.Error("No open batch queue to submit", "sap.ui2.srvc.ODataWrapper");
1006
- }
1007
-
1008
- if (aBatchQueue.length > 0) {
1009
- this.batch({__batchRequests: aBatchQueue}, onBatchSuccess, fnBatchItselfFailed);
1010
- } else if (fnBatchAccepted) {
1011
- // call success handler (if given) directly (async) if batchQueue is empty
1012
- sap.ui2.srvc.call(fnBatchAccepted, oODataService.getDefaultErrorHandler(), /*async=*/true);
1013
- }
1014
-
1015
- aBatchQueue = undefined;
1016
- aDeferredQueue = undefined; // be nice to the garbage collector
1017
- };
1018
-
1019
- /**
1020
- * Returns this wrapper's string representation.
1021
- *
1022
- * @param {boolean} [bVerbose=false]
1023
- * flag whether to show all properties
1024
- * @returns {string}
1025
- * this wrapper's string representation
1026
- *
1027
- * @public
1028
- * @since 1.19.0
1029
- */
1030
- this.toString = function (bVerbose) {
1031
- var aResult = ['sap.ui2.srvc.ODataWrapper({sBaseUrl:"', sBaseUrl, '"'];
1032
- // if (bVerbose) {
1033
- // }
1034
- aResult.push('})');
1035
- return aResult.join('');
1036
- };
1037
-
1038
- /** executes a put request
1039
- *
1040
- * @param {string} sRelativeUrl the relative URL to use
1041
- * @param {object} oPayload the payload
1042
- * @param {function ()} [fnSuccess]
1043
- * callback function that is executed if the request succeeds, taking no data
1044
- * @param {function (string, object=)} [fnFailure]
1045
- * error handler taking an error message and, since version 1.28.6, an
1046
- * optional object containing the complete error information as delivered
1047
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1048
- * for more details.
1049
- * Defaults to the OData service facade's default error handler
1050
- */
1051
- this.put = function (sRelativeUrl, oPayload, fnSuccess, fnFailure) {
1052
- requestOrBatch(sRelativeUrl, "PUT", oPayload, function (oData) {
1053
- if (fnSuccess) {
1054
- // Note: try/catch already done by doRequest()
1055
- fnSuccess(); // drop excess parameters (oData === undefined)
1056
- }
1057
- }, fnFailure);
1058
- };
1059
- /**
1060
- * Generic entity update method.
1061
- *
1062
- * @param {object} oEntity
1063
- * the datajs representation of the entity
1064
- * @param {function ()} [fnSuccess]
1065
- * callback function that is executed if the request succeeds, taking no data
1066
- * @param {function (string, object=)} [fnFailure]
1067
- * error handler taking an error message and, since version 1.28.6, an
1068
- * optional object containing the complete error information as delivered
1069
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1070
- * for more details.
1071
- * Defaults to the OData service facade's default error handler
1072
- *
1073
- * @public
1074
- * @since 1.19.0
1075
- */
1076
- this.update = function (oEntity, fnSuccess, fnFailure) {
1077
- /*jslint nomen:true */
1078
- var oPayload = {
1079
- "__metadata": {
1080
- type: oEntity.__metadata && oEntity.__metadata.type
1081
- }
1082
- },
1083
- sPropertyName,
1084
- sRelativeUrl = toRelativeUrl(oEntity.__metadata.uri);
1085
-
1086
- // copy all original property values of entity into request data
1087
- for (sPropertyName in oEntity) {
1088
- if (Object.prototype.hasOwnProperty.call(oEntity, sPropertyName)
1089
- && sPropertyName.indexOf('$') !== 0
1090
- && typeof oEntity[sPropertyName] !== "object") {
1091
- oPayload[sPropertyName] = oEntity[sPropertyName];
1092
- }
1093
- }
1094
- this.put(sRelativeUrl, oPayload, fnSuccess, fnFailure);
1095
- };
1096
-
1097
- // constructor code -------------------------------------------------------
1098
- if (!sap.ui2.srvc.Map) {
1099
- fnRequire("sap.ui2.srvc.utils");
1100
- }
1101
-
1102
- // Detect old API and transform it to the current one
1103
- if (typeof oSettings === 'string') {
1104
- oSettings = handleOldConstructorApi(arguments);
1105
- } else if (typeof oSettings === 'object') {
1106
- // clone to ensure it is not changed afterwards
1107
- oSettings = cloneObject(oSettings);
1108
- }
1109
-
1110
- if (!oSettings || !oSettings.baseUrl || typeof oSettings.baseUrl !== "string") {
1111
- throw new sap.ui2.srvc.Error("Missing base URL", "sap.ui2.srvc.ODataWrapper");
1112
- }
1113
- if (!oODataService || typeof oODataService !== "object") {
1114
- throw new sap.ui2.srvc.Error("Missing OData service facade", "sap.ui2.srvc.ODataWrapper");
1115
- }
1116
-
1117
- // ensure that base URL has a trailing /
1118
- oSettings.baseUrl = oSettings.baseUrl.replace(/\/$/, "") + "/";
1119
-
1120
- sBaseUrl = oSettings.baseUrl;
1121
- bSupportsChangeSets = oSettings.supportsChangeSets;
1122
-
1123
- if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration === "undefined") {
1124
- jQuery.sap.log.error("sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration is not defined!",
1125
- "the sap.ui2.srvc.ODataWrapper constructor was called before the static property was defined",
1126
- "sap.ui2.srvc.ODataWrapper");
1127
- } else if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] === "undefined") {
1128
-
1129
- // Define the sticky session configuration for the base URL managed by
1130
- // this ODataWrapper instance.
1131
- sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] = {
1132
- enabled: false, // sticky session disabled by default
1133
- value: undefined // the value detected if enabled = true
1134
- };
1135
- }
1136
- };
1137
-
1138
- /**
1139
- * Detects and, if needed, converts old api calls of the createODataWrapper function to the current one.
1140
- * @param {object} oArgs
1141
- * An arguments object containing the parameters of the createODataWrapper function. See createODataWrapper for details
1142
- * @returns {object}
1143
- * Settings for ODataWrapper and default failure function
1144
- * @private
1145
- */
1146
- function handleOldCreateODataWrapperApi (oArgs) {
1147
- var oSettings = {};
1148
- var oTransformedApi = {};
1149
-
1150
- oSettings.baseUrl = oArgs[0];
1151
- // These are non mandatory parameters and might be undefined
1152
- if (typeof oArgs[1] === 'boolean') {
1153
- oSettings.supportsChangeSets = oArgs[1];
1154
- }
1155
- if (typeof oArgs[2] === 'function') {
1156
- oTransformedApi.defaultFailure = oArgs[2];
1157
- }
1158
- oTransformedApi.settings = oSettings;
1159
-
1160
- return oTransformedApi;
1161
- }
1162
-
1163
- /**
1164
- * Clones an Object to avoid unwanted changes
1165
- * @param {object} oInputObject
1166
- * The object that needs to be cloned
1167
- * @returns {object}
1168
- * The cloned object
1169
- * @private
1170
- */
1171
- function cloneObject (oInputObject) {
1172
- if (oInputObject === undefined) {
1173
- return undefined;
1174
- }
1175
- try {
1176
- return JSON.parse(JSON.stringify(oInputObject));
1177
- } catch (e) {
1178
- return undefined;
1179
- }
1180
- }
1181
-
1182
- /*
1183
- * Configuration for sticky session is done here as it is a static property
1184
- * that should not rely on the instance initialization. Also, it may happen
1185
- * that an already defined sap.ui2.srvc.ODataWrapper name is re-assigned
1186
- * because this file is re-executed or re-loaded. Such a scenario may be
1187
- * possible if this file is required twice (after being registered to two
1188
- * different module paths).
1189
- */
1190
- if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration === "undefined") {
1191
- sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration = {};
1192
- }
1193
-
1194
- // public factory function ***************************************************
1195
-
1196
- /**
1197
- * Checks the sap-statistics setting form UI5's configuration and set it on the ODataWrapper.
1198
- * If UI5 is not available, it is checked if the sap-statistics query parameter is set.
1199
- * Note: this function is directly executed
1200
- * @param {string} getWindowLocationSearch
1201
- * value of window.location.search; to be able to test the behavior of this method with
1202
- * different search strings.
1203
- * @private
1204
- */
1205
- sap.ui2.srvc.testPublishAt(sap.ui2.srvc.ODataWrapper);
1206
- function checkSapStatisticsSetting(sWindowLocationSearch) {
1207
- try {
1208
- // read the value from UI5 as it may be set via [CTRL-SHIFT-ALT-P]
1209
- sap.ui2.srvc.ODataWrapper["sap-statistics"] =
1210
- sap.ui.getCore().getConfiguration().getStatistics();
1211
- } catch (e) {
1212
- // Read sap-statistics directly form query parameter in scenarios without UI5
1213
- sap.ui2.srvc.ODataWrapper["sap-statistics"] =
1214
- /sap-statistics=(true|x|X)/.test(sWindowLocationSearch);
1215
- }
1216
- }
1217
- // call function directly (immediate function pattern will break testPublishAt)
1218
- checkSapStatisticsSetting(window.location.search);
1219
-
1220
- /**
1221
- * Constructs and returns a {@link sap.ui2.srvc.ODataWrapper} which knows its
1222
- * {@link sap.ui2.srvc.ODataService} twin, see {@link sap.ui2.srvc.ODataWrapper#getODataService}.
1223
- *
1224
- * @param {object} oSettings
1225
- * An object containing various properties:
1226
- * <pre>
1227
- * {
1228
- * baseUrl: "/OData/OData.svc", // Mandatory base URL of the OData service
1229
- * supportsChangeSets: false, // Type: boolean, Default: false
1230
- * // Whether the OData service supports change sets with <b>multiple</b>
1231
- * // operations bundled into a single logical unit of work. Otherwise
1232
- * // each modifying operation is isolated in a change set of its own.
1233
- * "sap-language": "EN", // header which is set for all requests sent
1234
- * "sap-client": 120, // header which is set for all requests sent
1235
- * "sap-statistics": true // header which is set for all requests sent; in order to receive
1236
- * // some performance statistics
1237
- * }
1238
- * </pre>
1239
- * @param {function (string, object=)} [fnDefaultFailure]
1240
- * error handler taking an error message and, since version 1.28.6, an
1241
- * optional object containing the complete error information as delivered
1242
- * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1243
- * for more details.
1244
- * @returns {sap.ui2.srvc.ODataWrapper}
1245
- * new instance of ODataWapper/ODataService
1246
- *
1247
- * @public
1248
- * @since 1.19.1
1249
- */
1250
- // OLD API: function (sBaseUrl, bSupportsChangeSets, fnDefaultFailure)
1251
- sap.ui2.srvc.createODataWrapper = function (oSettings, fnDefaultFailure) {
1252
- // If old API is used we need to transform it to the current one.
1253
- if (typeof arguments[0] === 'string') {
1254
- var oTransformedApi = handleOldCreateODataWrapperApi(arguments);
1255
- oSettings = oTransformedApi.settings;
1256
- if (oTransformedApi.defaultFailure) {
1257
- fnDefaultFailure = oTransformedApi.defaultFailure;
1258
- }
1259
- } else if (typeof arguments[0] === 'object') {
1260
- // clone to ensure it is not changed afterwards
1261
- oSettings = cloneObject(oSettings);
1262
- }
1263
-
1264
- function Service() {
1265
- var oWrapper = new sap.ui2.srvc.ODataWrapper(oSettings, this);
1266
- fnRequire("sap.ui2.srvc.ODataService");
1267
- sap.ui2.srvc.ODataService.call(this, oWrapper, fnDefaultFailure);
1268
- return oWrapper;
1269
- }
1270
-
1271
- return new Service(); // BEWARE: this actually _returns_ the wrapper, not the service!
1272
- };
1273
- /// @end
1274
- }());
1
+ // Copyright (c) 2009-2020 SAP SE, All Rights Reserved
2
+ /**
3
+ * @fileOverview A wrapper around <code>OData</code>, providing CSRF token handling, caching and
4
+ * generic batch support.
5
+ */
6
+
7
+ (function () {
8
+ "use strict";
9
+ /*global OData */
10
+ jQuery.sap.declare("sap.ui2.srvc.ODataWrapper");
11
+
12
+ // Note: Migration from jquery.sap.log via
13
+ // var Log = sap.ui.require("sap/base/Log");
14
+ // does not work out of the box and leads to an error in the unit tests.
15
+
16
+ // Note: Only the section between @begin and @end is included in pbs-template.js.
17
+ // In pbs-template fnRequire is differently initialized (in case UI5 is not available)!
18
+ // Thus this variable is used in the coding below and not directly jQuery.sap.require.
19
+ // avoid fnRequire = jQuery.sap.require as require cannot be spied on afterwards
20
+ var fnRequire = function () {
21
+ jQuery.sap.require.apply(this, arguments);
22
+ };
23
+ function nop () { /* null object pattern */ }
24
+
25
+ sap.ui2.srvc.testPublishAt = sap.ui2.srvc.testPublishAt || function () {
26
+ // intentionally left blank
27
+ };
28
+
29
+ /// @begin
30
+ // if OData is missing, automatically require SAPUI5's datajs implementation
31
+ if (typeof OData !== "object") {
32
+ fnRequire("sap.ui.thirdparty.datajs");
33
+ }
34
+
35
+ /**
36
+ * Reject the given <code>jQuery.Deferred</code> object(s) (array or single one) with the
37
+ * given error response.
38
+ *
39
+ * @param {jQuery.Deferred|jQuery.Deferred[]} vDeferred
40
+ * deferred or array of deferreds to be rejected
41
+ * @param {object} oResponse
42
+ * parameter of (each) reject call
43
+ */
44
+ function reject (vDeferred, oResponse) {
45
+ if (sap.ui2.srvc.isArray(vDeferred)) {
46
+ // a failed change set: reject each single jQuery.Deferred in the same way
47
+ vDeferred.forEach(function (oDeferred) {
48
+ oDeferred.reject(oResponse);
49
+ });
50
+ } else {
51
+ // a failed GET request
52
+ vDeferred.reject(oResponse);
53
+ }
54
+ }
55
+
56
+ // "public class" ************************************************************
57
+
58
+ /**
59
+ * Constructs a wrapper around <code>OData</code>, providing CSRF token handling, caching and
60
+ * generic batch support. The sap-statistics header is automatically added to all requests if
61
+ * the URL query parameter <code>sap-statistics=true</code> is set (see
62
+ * <a href="http://help.sap.com/saphelp_nw74/helpdata/de/40/93b81292194d6a926e105c10d5048d/content.htm">
63
+ * SAP Performance Statistics</a>).
64
+ * If <code>OData</code> is missing, "sap.ui.thirdparty.datajs" is required automatically.
65
+ * <p>
66
+ * The preferred way to call the constructor is from a "sub-class" of
67
+ * {@link sap.ui2.srvc.ODataService}:
68
+ * <pre>
69
+ * function Service() {
70
+ * var oWrapper = new sap.ui2.srvc.ODataWrapper(oSettings, this);
71
+ * sap.ui2.srvc.ODataService.call(this, oWrapper, fnDefaultFailure);
72
+ * }
73
+ * var myService = new Service();
74
+ * </pre>
75
+ * This provides public inheritance of <code>sap.ui2.srvc.ODataService</code> methods and
76
+ * private inheritance of <code>sap.ui2.srvc.ODataWrapper</code> methods. In case you are not
77
+ * providing a public "sub-class" of <code>sap.ui2.srvc.ODataService</code> but only want to
78
+ * use methods from <code>sap.ui2.srvc.ODataWrapper</code>,
79
+ * {@link sap.ui2.srvc.createODataWrapper} is the preferred way to construct an instance.
80
+ *
81
+ * @param {object} oSettings
82
+ * An object containing various properties:
83
+ * <pre>
84
+ * {
85
+ * baseUrl: "/OData/OData.svc", // Mandatory base URL of the OData service
86
+ * supportsChangeSets: false, // Type: boolean, Default: false
87
+ * // Whether the OData service supports change sets with <b>multiple</b>
88
+ * // operations bundled into a single logical unit of work. Otherwise
89
+ * // each modifying operation is isolated in a change set of its own.
90
+ * "sap-language": "EN", // header which is set for all requests sent
91
+ * "sap-client": 120, // header which is set for all requests sent
92
+ * "sap-statistics": true // header which is set for all requests sent; in order to receive
93
+ * // some performance statistics
94
+ * }
95
+ * </pre>
96
+ * @param {sap.ui2.srvc.ODataService} oODataService
97
+ * facade to any OData service, keeping track of CSRF token and default error handler
98
+ * (see {@link #getODataService})
99
+ *
100
+ * @class
101
+ * @public
102
+ * @since 1.19.0
103
+ */
104
+ // OLD API: function (sBaseUrl, oODataService, bSupportsChangeSets)
105
+ sap.ui2.srvc.ODataWrapper = function (oSettings, oODataService) {
106
+ var S_STICKY_SESSION_HEADER = "saplb", // header to check during sticky session
107
+ aBatchQueue, // corresponds to data.__batchRequests
108
+ bChangeSetOpen, // whether a change set is currently open at the end of our batch queue
109
+ aDeferredQueue, // jQuery.Deferred() objects corresponding to each request
110
+ that = this,
111
+ sBaseUrl,
112
+ bSupportsChangeSets;
113
+
114
+ // BEWARE: constructor code below!
115
+
116
+ // "private" methods -------------------------------------------------------
117
+
118
+ /**
119
+ * Converts old api calls of the ODataWrapper constructor to the current one.
120
+ * @param {object} oArgs
121
+ * An arguments object containing the parameters of the constructor function. See Constructor for details.
122
+ * @returns {object}
123
+ * Settings for ODataWrapper in an object instead of arguments
124
+ * @private
125
+ */
126
+ function handleOldConstructorApi (oArgs) {
127
+ var oSettingsODataWrapper = {};
128
+
129
+ oSettingsODataWrapper.baseUrl = oArgs[0];
130
+ // This is not a mandatory parameter and might be undefined
131
+ if (typeof oArgs[2] === "boolean") {
132
+ oSettingsODataWrapper.supportsChangeSets = oArgs[2];
133
+ }
134
+
135
+ return oSettingsODataWrapper;
136
+ }
137
+
138
+ /* eslint-disable no-unused-vars */
139
+ /**
140
+ * Getter mainly used for testing.
141
+ *
142
+ * @returns {object}
143
+ */
144
+ sap.ui2.srvc.testPublishAt(that);
145
+ function getBatchQueue () {
146
+ return aBatchQueue;
147
+ }
148
+ /* eslint-enable no-unused-vars */
149
+
150
+ /**
151
+ * Iterates over the given headers map and returns the first value for the requested key (case
152
+ * insensitive). If no such key is found, <code>undefined</code> is returned.
153
+ *
154
+ * @param {string} sKey
155
+ * the requested key
156
+ * @param {object} [mHeaders={}]
157
+ * an object treated as a <code>map&lt;string, object&gt;</code>
158
+ * @returns {string}
159
+ * the header value or <code>undefined</code> if the header was not found
160
+ */
161
+ sap.ui2.srvc.testPublishAt(that);
162
+ function headerValue (sKey, mHeaders) {
163
+ sKey = sKey.toLowerCase();
164
+ for (var sCurrentKey in mHeaders) {
165
+ if (Object.prototype.hasOwnProperty.call(mHeaders, sCurrentKey)
166
+ && sCurrentKey.toLowerCase() === sKey) {
167
+ return mHeaders[sCurrentKey];
168
+ }
169
+ }
170
+ return undefined;
171
+ }
172
+
173
+ /**
174
+ * Gets an object supposed to be the headers object used for OData requests.
175
+ * Adds headers set as static properties of sap.ui2.srvc.ODataWrapper or as part of Constructor settings.
176
+ * The following headers may be added:
177
+ * - sap-language
178
+ * - sap-statistics
179
+ * - sap-client
180
+ *
181
+ * @param {object} [oHeaders={}]
182
+ * optional object supposed to be the headers object used for OData requests
183
+ * @returns {object}
184
+ * returns oHeader (if not given, a new object is created) additional headers may be added as
185
+ * properties to the object.
186
+ */
187
+ function addGlobalSapHeaders (oHeaders) {
188
+ var sSapLanguage = oSettings["sap-language"] || sap.ui2.srvc.ODataWrapper["sap-language"],
189
+ sSapStatistics = oSettings["sap-statistics"] || sap.ui2.srvc.ODataWrapper["sap-statistics"],
190
+ sSapClient = oSettings["sap-client"] || sap.ui2.srvc.ODataWrapper["sap-client"];
191
+
192
+ oHeaders = oHeaders || {};
193
+ if (sSapLanguage) {
194
+ oHeaders["sap-language"] = sSapLanguage;
195
+ }
196
+ if (sSapStatistics || (sSapStatistics === "false")) {
197
+ // If sSapStatistics is set, all requests done via ODataWrapper, will contain the
198
+ // sap-statistics header. See
199
+ // http://help.sap.com/saphelp_nw74/helpdata/de/40/93b81292194d6a926e105c10d5048d/content.htm
200
+ oHeaders["sap-statistics"] = "" + sSapStatistics;
201
+ }
202
+ if (sSapClient) {
203
+ oHeaders["sap-client"] = sSapClient;
204
+ }
205
+ return oHeaders;
206
+ }
207
+
208
+ /**
209
+ * Adds previously detected sticky session headers to the given object
210
+ * (which is supposed to be the headers object used for OData requests).
211
+ *
212
+ * @param {object} [oHeaders={}]
213
+ * optional object supposed to be the headers object used for OData requests
214
+ * @returns {object}
215
+ * returns oHeader (if not given, a new object is created) additional
216
+ * headers may be added as properties to the object.
217
+ */
218
+ function addStickySessionHeader (oHeaders) {
219
+ oHeaders = oHeaders || {};
220
+
221
+ var oStickySessionConfiguration =
222
+ sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl];
223
+
224
+ /*
225
+ * NOTE: not assuming oStickySessionConfiguration is defined (see comment
226
+ * on assigment of sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration)
227
+ * down in this file.
228
+ */
229
+ if (typeof oStickySessionConfiguration === "undefined") {
230
+ return oHeaders;
231
+ }
232
+
233
+ if (oStickySessionConfiguration && oStickySessionConfiguration.enabled &&
234
+ typeof oStickySessionConfiguration.value !== "undefined") {
235
+
236
+ oHeaders[S_STICKY_SESSION_HEADER] = oStickySessionConfiguration.value;
237
+ }
238
+
239
+ return oHeaders;
240
+ }
241
+
242
+ /**
243
+ * Detects sticky session based on the given response headers and, in case
244
+ * of success, updates the static
245
+ * sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration with the sticky
246
+ * session value found in the header.
247
+ *
248
+ * @param {object} oResponseHeaders
249
+ * the response headers
250
+ */
251
+ sap.ui2.srvc.testPublishAt(that);
252
+ function detectStickySession (oResponseHeaders) {
253
+ var sHeaderValue,
254
+ oStickySessionConfiguration =
255
+ sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl];
256
+
257
+ if (!oStickySessionConfiguration || !oStickySessionConfiguration.enabled) {
258
+ return;
259
+ }
260
+
261
+ // add sticky session header value if found
262
+ sHeaderValue = headerValue(S_STICKY_SESSION_HEADER, oResponseHeaders);
263
+ if (typeof sHeaderValue === "undefined") {
264
+ return;
265
+ }
266
+
267
+ if (oStickySessionConfiguration.value !== sHeaderValue) { // take the last value from server
268
+ oStickySessionConfiguration.value = sHeaderValue;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Iterates over the given headers map and returns the first value for the well-known
274
+ * "X-CSRF-Token" key (case insensitive). If no such key is found, or if the request
275
+ * was cached, <code>""</code> is returned.
276
+ *
277
+ * @param {object} [mHeaders={}]
278
+ * an object treated as a <code>map&lt;string, object&gt;</code>
279
+ * @returns {string}
280
+ */
281
+ sap.ui2.srvc.testPublishAt(that);
282
+ function csrfTokenValue (mHeaders) {
283
+
284
+ // Access CSRF token value
285
+ var sCsrfTokenHeaderValue = headerValue("x-csrf-token", mHeaders);
286
+
287
+ // Return always if value was "Required"
288
+ // ... Happens when server reported http status "403 Forbidden" and token was invalid or expired
289
+ if (sCsrfTokenHeaderValue === "Required") {
290
+ return sCsrfTokenHeaderValue;
291
+ }
292
+
293
+ // Do not return if no value or request was cached
294
+ // ... as indicated by cache-control header, Expires header not evaluated here
295
+ var sCacheControlHeaderValue = headerValue("cache-control", mHeaders);
296
+ if (sCacheControlHeaderValue === undefined
297
+ || sCacheControlHeaderValue.indexOf("max-age=0") > -1
298
+ || sCacheControlHeaderValue.indexOf("no-cache") > -1) {
299
+ return sCsrfTokenHeaderValue || "";
300
+ }
301
+
302
+ return "";
303
+ }
304
+
305
+ /**
306
+ * Wrapper around
307
+ * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.request">
308
+ * <code>OData.request</code></a> which is able to automatically fetch a CSRF token if
309
+ * required.
310
+ *
311
+ * @param {string} sRequestUrl
312
+ * a string containing the <b>absolute</b> URL
313
+ * @param {string} sMethod
314
+ * the HTTP method to use, e.g. "POST"
315
+ * @param {object} oPayload
316
+ * payload of the request (in intermediate format)
317
+ * @param {function(object)} [fnSuccess]
318
+ * callback function that is executed if the request succeeds, taking the processed data
319
+ * @param {function (string, object=)} [fnFailure]
320
+ * error handler taking an error message and, since version 1.28.6, an
321
+ * optional object containing the complete error information as delivered
322
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
323
+ * for more details.
324
+ * Defaults to the OData service facade's default error handler
325
+ * @param {object} [oHandler]
326
+ * (OData/datajs) handler for the response data
327
+ * @param {boolean} [bIsRepeatedRequest]
328
+ * This function recursively calls itself to re-execute it in case the CSRF token was invalid and has been fetched.
329
+ * To prevent endless loops when the server returns an invalid token, this flag is used.
330
+ */
331
+ sap.ui2.srvc.testPublishAt(that);
332
+ function doRequest (sRequestUrl, sMethod, oPayload, fnSuccess, fnFailure, oHandler, bIsRepeatedRequest) {
333
+ var oHeaders;
334
+ fnSuccess = fnSuccess || nop;
335
+ fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
336
+ that.check(fnSuccess, fnFailure);
337
+ oHeaders = {
338
+ Accept: "application/json",
339
+ "Accept-Language": (sap.ui && sap.ui.getCore().getConfiguration().getLanguage()) || "",
340
+ "X-CSRF-Token": oODataService.getCsrfToken()
341
+ };
342
+
343
+ addGlobalSapHeaders(oHeaders);
344
+ addStickySessionHeader(oHeaders);
345
+
346
+ OData.request({
347
+ requestUri: sRequestUrl,
348
+ method: sMethod,
349
+ data: oPayload,
350
+ headers: oHeaders
351
+ },
352
+ function (oData, oResponse) {
353
+ detectStickySession((oResponse || {}).headers);
354
+ jQuery.sap.log.debug("Received OData response for " + sMethod + ' "' + sRequestUrl + '"',
355
+ null,
356
+ "sap.ui2.srvc.ODataWrapper");
357
+ // Note: drop excess parameters; try/catch
358
+ sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
359
+ },
360
+ function (oError) {
361
+ // wrappers for success & failure handlers to reset flag
362
+ function failure () {
363
+ fnFailure.apply(null, arguments);
364
+ }
365
+ function success () {
366
+ fnSuccess.apply(null, arguments);
367
+ }
368
+
369
+ if (!bIsRepeatedRequest
370
+ && oError.response.statusCode === 403
371
+ && csrfTokenValue(oError.response.headers).toLowerCase() === "required") {
372
+ // refresh CSRF token and repeat original request
373
+ jQuery.sap.log.debug("CSRF token required for " + sMethod + ' "' + sRequestUrl
374
+ + '", refreshing it', JSON.stringify(oError.response),
375
+ "sap.ui2.srvc.ODataWrapper");
376
+
377
+ oODataService.refreshCsrfToken(
378
+ doRequest.bind(that, sRequestUrl, sMethod, oPayload, success, failure, oHandler, /*bIsRepeatedRequest*/ true),
379
+ failure
380
+ );
381
+ } else {
382
+ that.onError(sMethod, sRequestUrl, fnFailure, /*oDeferred*/null, oError);
383
+ }
384
+ },
385
+ oHandler);
386
+ jQuery.sap.log.debug("Sent OData request for " + sMethod + ' "' + sRequestUrl + '"', null,
387
+ "sap.ui2.srvc.ODataWrapper");
388
+ }
389
+
390
+ /**
391
+ * Transforms the given absolute URL into a relative URL w.r.t. this OData wrapper's base URL,
392
+ * removing protocol, host, port, and base URL, but preserving query parameters and a fragment
393
+ * part.
394
+ *
395
+ * @param {string} sRequestUrl
396
+ * a string containing an <b>absolute</b> URL, e.g.
397
+ * "http://acme.corp/OData/OData.svc/Products(1)?$foo=bar#abc"
398
+ * @returns {string}
399
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
400
+ * "Products(1)?$foo=bar#abc"
401
+ */
402
+ sap.ui2.srvc.testPublishAt(that);
403
+ function toRelativeUrl (sRequestUrl) {
404
+ var i = sRequestUrl.indexOf(sBaseUrl);
405
+ if (i < 0) {
406
+ throw new sap.ui2.srvc.Error('Not relative to base URL "' + sBaseUrl + '": ' + sRequestUrl,
407
+ "sap.ui2.srvc.ODataWrapper");
408
+ }
409
+ return sRequestUrl.slice(i + sBaseUrl.length);
410
+ }
411
+
412
+ /**
413
+ * Transforms the given relative URL into an absolute URL w.r.t. this OData wrapper's base URL,
414
+ * making sure there is exactly one slash in between.
415
+ *
416
+ * @param {string} sRelativeUrl
417
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
418
+ * "Products(1)"
419
+ * @returns {string}
420
+ * a string containing the <b>absolute</b> URL, e.g. "/OData/OData.svc/Products(1)"
421
+ */
422
+ sap.ui2.srvc.testPublishAt(that);
423
+ function toRequestUrl (sRelativeUrl) {
424
+ if (/^\//.test(sRelativeUrl)) {
425
+ throw new sap.ui2.srvc.Error("Not a relative URL: " + sRelativeUrl,
426
+ "sap.ui2.srvc.ODataWrapper");
427
+ }
428
+ return sBaseUrl + sRelativeUrl;
429
+ }
430
+
431
+ /**
432
+ * Wrapper around
433
+ * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.read">
434
+ * <code>OData.read</code></a> which supports queuing up for "$batch" requests.
435
+ *
436
+ * @param {string} sRelativeUrl
437
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
438
+ * "Products(1)"
439
+ * @param {function(object,object)} fnRawSuccess
440
+ * a callback function that is executed if the request succeeds, taking the processed data
441
+ * and the response object
442
+ * @param {function (object)} fnRawFailure
443
+ * a callback function that is executed if the request fails, taking an error object
444
+ */
445
+ sap.ui2.srvc.testPublishAt(that);
446
+ function readOrBatch (sRelativeUrl, fnRawSuccess, fnRawFailure) {
447
+ var oDeferred,
448
+ sRequestUrl = toRequestUrl(sRelativeUrl),
449
+ oHeaders = addStickySessionHeader(addGlobalSapHeaders()); // may return an empty object
450
+
451
+ if (aBatchQueue) {
452
+ jQuery.sap.log.debug('Queued OData request for GET "' + sRelativeUrl + '"', null,
453
+ "sap.ui2.srvc.ODataWrapper");
454
+ aBatchQueue.push({
455
+ method: "GET",
456
+ requestUri: sRelativeUrl,
457
+ headers: oHeaders
458
+ });
459
+ oDeferred = (new jQuery.Deferred()).done(fnRawSuccess).fail(fnRawFailure);
460
+ aDeferredQueue.push(oDeferred);
461
+ bChangeSetOpen = false;
462
+ return;
463
+ }
464
+
465
+ // add specifc headers for read request
466
+ oHeaders.Accept = "application/json";
467
+ oHeaders["Accept-Language"] = (sap.ui && sap.ui.getCore().getConfiguration().getLanguage()) || "";
468
+ // always fetch a new token with GET requests;
469
+ // this avoids using old tokens from responses served from browser cache
470
+ // see internal BCP incident 1570753380
471
+ oHeaders["X-CSRF-Token"] = "Fetch";
472
+
473
+ OData.read({
474
+ requestUri: sRequestUrl,
475
+ headers: oHeaders
476
+ }, fnRawSuccess, fnRawFailure);
477
+ jQuery.sap.log.debug('Sent OData request for GET "' + sRequestUrl + '"', null,
478
+ "sap.ui2.srvc.ODataWrapper");
479
+ }
480
+
481
+ /**
482
+ * Wrapper around {@link #doRequest} which supports queuing up for "$batch" requests.
483
+ *
484
+ * @param {string} sRelativeUrl
485
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
486
+ * "Products"
487
+ * @param {string} sMethod
488
+ * the HTTP method to use, either "POST", "PUT" or "DELETE" (retrieve requests using "GET"
489
+ * must be made via {@link #readOrBatch} instead!)
490
+ * @param {object} oPayload
491
+ * payload of the request (in intermediate format)
492
+ * @param {function(object)} [fnSuccess]
493
+ * callback function that is executed if the request succeeds, taking the processed data
494
+ * @param {function (string, object=)} [fnFailure]
495
+ * error handler taking an error message and, since version 1.28.6, an
496
+ * optional object containing the complete error information as delivered
497
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
498
+ * for more details.
499
+ * Defaults to the OData service facade's default error handler
500
+ */
501
+ sap.ui2.srvc.testPublishAt(that);
502
+ function requestOrBatch (sRelativeUrl, sMethod, oPayload, fnSuccess, fnFailure) {
503
+ /*jslint nomen:true */
504
+ var oDeferred,
505
+ oChangeRequest = {
506
+ data: oPayload,
507
+ method: sMethod,
508
+ requestUri: sRelativeUrl,
509
+ headers: addStickySessionHeader(addGlobalSapHeaders()) // may return an empty object
510
+ },
511
+ sRequestUrl = toRequestUrl(sRelativeUrl);
512
+
513
+ if (aBatchQueue) {
514
+ fnSuccess = fnSuccess || nop;
515
+ fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
516
+ that.check(fnSuccess, fnFailure);
517
+ jQuery.sap.log.debug("Queued OData request for " + sMethod + ' "' + sRelativeUrl + '"',
518
+ null, "sap.ui2.srvc.ODataWrapper");
519
+ if (!bChangeSetOpen) {
520
+ aBatchQueue.push({__changeRequests: []});
521
+ aDeferredQueue.push([]);
522
+ bChangeSetOpen = bSupportsChangeSets;
523
+ }
524
+ aBatchQueue[aBatchQueue.length - 1].__changeRequests.push(oChangeRequest);
525
+ oDeferred = (new jQuery.Deferred())
526
+ .done(function (oData, oResponse) {
527
+ jQuery.sap.log.debug("Received OData response for "
528
+ + sMethod + ' "' + sRequestUrl + '"', null, "sap.ui2.srvc.ODataWrapper");
529
+ // Note: drop excess parameters; try/catch
530
+ sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
531
+ })
532
+ .fail(that.onError.bind(that, sMethod, sRequestUrl, fnFailure, /*oDeferred*/null));
533
+ aDeferredQueue[aDeferredQueue.length - 1].push(oDeferred);
534
+ return;
535
+ }
536
+
537
+ doRequest(sRequestUrl, sMethod, oPayload, fnSuccess, fnFailure);
538
+ }
539
+
540
+ // "public" methods --------------------------------------------------------
541
+
542
+ /**
543
+ * Checks whether session stickyness is configured and enabled
544
+ *
545
+ * @returns {boolean}
546
+ * whether the sticky session header is configured and active
547
+ *
548
+ * @public
549
+ *
550
+ * @since 1.30.0
551
+ */
552
+ this.isStickySessionEnabled = function () {
553
+ return (sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] || {}).enabled || false;
554
+ };
555
+
556
+ /**
557
+ * <p>
558
+ * Configures and activates session stickiness.
559
+ * </p>
560
+ * <p>
561
+ * Session stickiness allows the client to execute OData requests to the
562
+ * same application server. This is achieved by copying a certain sticky
563
+ * session header
564
+ * (found in the OData response from the server) to the OData request made
565
+ * by ODataWrapper, indicating the load balancer that requests should be
566
+ * made against a certain application server. Once detected, the sticky
567
+ * header is automatically shared by all instances of
568
+ * <code>sap.ui2.srvc.ODataWrapper</code> connected to exactly the same
569
+ * base URL.
570
+ * </p>
571
+ * <p>
572
+ * NOTE: The sticky session header sent from the server always overrides
573
+ * the last sticky session header requested. This is to avoid that the
574
+ * feature is disabled if the load balancer returns another value for the
575
+ * sticky session header for some reason (e.g. the application server goes
576
+ * offline).
577
+ * </p>
578
+ * <p>
579
+ * NOTE: In line with {@link sap.ui2.srvc.PageBuildingService}, session
580
+ * stickiness is only supported in scopes different from PERS.
581
+ * </p>
582
+ * <p>
583
+ * Currently, session stickiness is affected by the following limitations:
584
+ * <ul>
585
+ * <li>If the client caches the response headers for certain requests, these
586
+ * cached headers will be used instead of the headers sent from the server
587
+ * (as in if cache was disabled).</li>
588
+ * <li>If initial requests to different URLs that share the same base URL are
589
+ * performed asynchronously (e.g. through multiple instances of
590
+ * ODataWrapper), sticky session may be disabled for a part or all of these
591
+ * initial requests. This is because the requests are made before the first
592
+ * response with a sticky session header is obtained.</li>
593
+ * <li>This mechanism only guarantees that requests are forwarded to the
594
+ * specified application server. For example, if the application server in
595
+ * turn contacts further load balanced servers, the session may not be
596
+ * maintain and load balancing may still occur.</li>
597
+ * </ul>
598
+ * </p>
599
+ * @public
600
+ *
601
+ * @since 1.30.0
602
+ */
603
+ this.enableStickySession = function () {
604
+ sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl].enabled = true;
605
+ };
606
+
607
+ /**
608
+ * Checks that the given callback functions are really functions. This check
609
+ * is useful to "fail fast" because these callbacks are not called immediately.
610
+ *
611
+ * @param {function} fnSuccess
612
+ * success callback
613
+ * @param {function} fnFailure
614
+ * error callback
615
+ *
616
+ * @public
617
+ * @since 1.19.0
618
+ */
619
+ this.check = function (fnSuccess, fnFailure) {
620
+ if (!fnSuccess) {
621
+ throw new sap.ui2.srvc.Error("Missing success callback",
622
+ "sap.ui2.srvc.ODataWrapper");
623
+ }
624
+ if (typeof fnSuccess !== "function") {
625
+ throw new sap.ui2.srvc.Error("Success callback is not a function",
626
+ "sap.ui2.srvc.ODataWrapper");
627
+ }
628
+ if (!fnFailure) {
629
+ throw new sap.ui2.srvc.Error("Missing error callback",
630
+ "sap.ui2.srvc.ODataWrapper");
631
+ }
632
+ if (typeof fnFailure !== "function") {
633
+ throw new sap.ui2.srvc.Error("Error callback is not a function",
634
+ "sap.ui2.srvc.ODataWrapper");
635
+ }
636
+ };
637
+
638
+ /**
639
+ * Wrapper around <code>OData.request</code> which is able to automatically fetch a CSRF token
640
+ * if required. It uses POST as a method.
641
+ *
642
+ * @param {string} sRelativeUrl
643
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
644
+ * "Products"
645
+ * @param {object} oPayload
646
+ * payload of the request (in intermediate format)
647
+ * @param {function(object)} [fnSuccess]
648
+ * callback function that is executed if the request succeeds, taking the processed data
649
+ * @param {function (string, object=)} [fnFailure]
650
+ * error handler taking an error message and, since version 1.28.6, an
651
+ * optional object containing the complete error information as delivered
652
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
653
+ * for more details.
654
+ * Defaults to the OData service facade's default error handler
655
+ *
656
+ * @public
657
+ * @since 1.19.0
658
+ */
659
+ this.create = function (sRelativeUrl, oPayload, fnSuccess, fnFailure) {
660
+ requestOrBatch(sRelativeUrl, "POST", oPayload, fnSuccess, fnFailure);
661
+ };
662
+
663
+ /**
664
+ * Wrapper around <code>OData.request</code> which is able to automatically fetch a CSRF token
665
+ * if required. It uses DELETE as a method.
666
+ *
667
+ * @param {string|object} vEntity
668
+ * either a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL,
669
+ * e.g. "Products(1)", or the datajs representation of the entity
670
+ * @param {function()} [fnSuccess]
671
+ * callback function that is executed if the request succeeds, taking no data
672
+ * @param {function (string, object=)} [fnFailure]
673
+ * error handler taking an error message and, since version 1.28.6, an
674
+ * optional object containing the complete error information as delivered
675
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
676
+ * for more details.
677
+ * Defaults to the OData service facade's default error handler
678
+ *
679
+ * @public
680
+ * @since 1.19.0
681
+ */
682
+ this.del = function (vEntity, fnSuccess, fnFailure) {
683
+ /*jslint nomen:true */
684
+ var sRelativeUrl = vEntity;
685
+
686
+ if (typeof sRelativeUrl !== "string") {
687
+ sRelativeUrl = toRelativeUrl(vEntity.__metadata.uri);
688
+ }
689
+
690
+ requestOrBatch(sRelativeUrl, "DELETE", null, function (oData) {
691
+ if (fnSuccess) {
692
+ // Note: try/catch already done by doRequest()
693
+ fnSuccess(); // drop excess parameters (oData === undefined)
694
+ }
695
+ }, fnFailure);
696
+ };
697
+
698
+ /**
699
+ * Returns the wrapper's base URL.
700
+ *
701
+ * @returns {string}
702
+ * base URL of the OData service, e.g. "/OData/OData.svc/"
703
+ *
704
+ * @public
705
+ * @since 1.19.0
706
+ */
707
+ this.getBaseUrl = function () {
708
+ return sBaseUrl;
709
+ };
710
+
711
+ /**
712
+ * Returns this wrapper's facade to an OData service (which was passed to the constructor
713
+ * {@link sap.ui2.srvc.ODataWrapper}).
714
+ *
715
+ * @returns {sap.ui2.srvc.ODataService}
716
+ * this wrapper's facade to an OData service.
717
+ *
718
+ * @public
719
+ * @since 1.19.1
720
+ */
721
+ this.getODataService = function () {
722
+ return oODataService;
723
+ };
724
+
725
+ /**
726
+ * Wraps the given generic OData failure handler. It processes the raw
727
+ * OData error response object, calls the given failure handler with an
728
+ * error message and, since version 1.28.6, an object containing additional
729
+ * technical details. If a Deferred object is given, it is rejected with
730
+ * the same arguments passed to the failure handler call.
731
+ *
732
+ * This method logs technical information to the console if this is
733
+ * available at the time the error occurs.
734
+ *
735
+ * @param {string} sMethod
736
+ * the HTTP method used in the OData request, e.g. "POST"
737
+ * @param {string} sRequestUrl
738
+ * the <b>absolute</b> URL the request is sent to
739
+ * @param {function (string, object=)} fnFailure
740
+ * the wrapped failure handler that will be called synchronously. The
741
+ * first parameter is a human-readable error message containing technical
742
+ * information, including sMethod and sRequestUrl; the second parameter
743
+ * is an <b>optional</b> object containing the complete error information
744
+ * returned in the <code>error</code> value contained in the body of the
745
+ * OData error response object, plus the HTTP response status code.
746
+ *
747
+ * For example, the returned error object has the following structure:
748
+ * <pre>
749
+ * {
750
+ * httpStatus: 404,
751
+ * // ... other keys and values from oError.response.body.error
752
+ * }
753
+ * </pre>
754
+ *
755
+ * Please refer to the documentation of the specific OData service used
756
+ * for details about keys and values returned in
757
+ * <code>oError.response.body.error</code>.<br />
758
+ *
759
+ * <b>IMPORTANT:</b> the second parameter may be undefined if the error
760
+ * cannot be parsed or is not returned in the OData error response.<br />
761
+ *
762
+ * <b>NOTE:</b> the second parameter is returned since version 1.28.6.
763
+ * @param {jQuery.Deferred} [oDeferred]
764
+ * a <code>jQuery.Deferred</code> object that will be rejected with the
765
+ * same arguments fnFailure is called
766
+ * @param {object} oError
767
+ * error object provided by datajs, should contain the response
768
+ *
769
+ * @public
770
+ * @since 1.19.0
771
+ */
772
+ this.onError = function (sMethod, sRequestUrl, fnFailure, oDeferred, oError) {
773
+ var oParsedErrorInformation,
774
+ sMessage = "Error ";
775
+
776
+ if (oError.response && oError.response.statusCode) {
777
+ sMessage += "(" + oError.response.statusCode + ", " + oError.response.statusText + ") ";
778
+ }
779
+ sMessage += "in OData response for " + sMethod + ' "' + sRequestUrl + '": ' + oError.message;
780
+
781
+ if (oError.response && oError.response.body) {
782
+
783
+ try {
784
+ oParsedErrorInformation = JSON.parse(oError.response.body).error;
785
+
786
+ if (oParsedErrorInformation) {
787
+
788
+ if (oParsedErrorInformation.hasOwnProperty("message") &&
789
+ oParsedErrorInformation.message.hasOwnProperty("value")) {
790
+ sMessage += "\nDetails: " + oParsedErrorInformation.message.value;
791
+ }
792
+
793
+ // NOTE: there was an agreement to not alter the data in
794
+ // oError.response.body.error (except from adding the status if
795
+ // it's not there already)
796
+ if (oError.response.statusCode && !oParsedErrorInformation.httpStatus) {
797
+ oParsedErrorInformation.httpStatus = oError.response.statusCode;
798
+ }
799
+ }
800
+ } catch (ex) {
801
+ // do not rely on subtleties of error response, treat error details as optional
802
+ }
803
+ }
804
+
805
+ jQuery.sap.log.error(sMessage, JSON.stringify(oError.response),
806
+ "sap.ui2.srvc.ODataWrapper");
807
+
808
+ if (oDeferred) {
809
+ oDeferred.reject(sMessage, oParsedErrorInformation);
810
+ }
811
+ fnFailure(sMessage, oParsedErrorInformation);
812
+ };
813
+
814
+ /**
815
+ * Opens a new queue where all requests are parked until a call to {@link #submitBatchQueue}.
816
+ *
817
+ * @public
818
+ * @since 1.19.0
819
+ *
820
+ * @see #isBatchQueueOpen
821
+ * @see #submitBatchQueue
822
+ */
823
+ this.openBatchQueue = function () {
824
+ if (aBatchQueue) {
825
+ throw new sap.ui2.srvc.Error("Batch queue already open", "sap.ui2.srvc.ODataWrapper");
826
+ }
827
+
828
+ aBatchQueue = [];
829
+ aDeferredQueue = [];
830
+ bChangeSetOpen = false;
831
+ };
832
+
833
+ /**
834
+ * Checks whether the queue of requests is already open or not
835
+ *
836
+ * @returns {boolean}
837
+ * true if batchQueue is already open
838
+ *
839
+ * @public
840
+ * @since 1.34.0
841
+ *
842
+ * @see #openBatchQueue
843
+ * @see #submitBatchQueue
844
+ */
845
+ this.isBatchQueueOpen = function () {
846
+ return !!aBatchQueue;
847
+ };
848
+
849
+ /**
850
+ * Wrapper around
851
+ * <a href="http://datajs.codeplex.com/wikipage?title=datajs%20OData%20API#OData.read">
852
+ * <code>OData.read</code></a> which supports caching.
853
+ *
854
+ * @param {string} sRelativeUrl
855
+ * a string containing the <b>relative</b> URL w.r.t. this OData wrapper's base URL, e.g.
856
+ * "Products(1)"
857
+ * @param {function(object)} fnSuccess
858
+ * a callback function that is executed if the request succeeds, taking the processed data
859
+ * @param {function (string, object=)} [fnFailure]
860
+ * error handler taking an error message and, since version 1.28.6, an
861
+ * optional object containing the complete error information as delivered
862
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
863
+ * for more details.
864
+ * Defaults to the OData service facade's default error handler
865
+ * ({@link sap.ui2.srvc.ODataService#getDefaultErrorHandler})
866
+ * @param {boolean} [bCache=false]
867
+ * whether the response is cached for further calls (since 1.11.0) in
868
+ * <code>OData.read.$cache</code>, a <code>sap.ui2.srvc.Map</code> from
869
+ * <code>sRequestUrl</code> to a <code>jQuery.Deferred</code> object created on demand;
870
+ * without this flag, the cache is neither written nor read!
871
+ *
872
+ * @public
873
+ * @since 1.19.0
874
+ */
875
+ this.read = function (sRelativeUrl, fnSuccess, fnFailure, bCache) {
876
+ var oDeferred,
877
+ sRequestUrl = toRequestUrl(sRelativeUrl),
878
+ sSapMessage;
879
+
880
+ /*
881
+ * Success handler for <code>OData.read</code>.
882
+ */
883
+ function success (oData, oResponse) {
884
+ detectStickySession(oResponse.headers);
885
+ oODataService.setCsrfToken(
886
+ csrfTokenValue(oResponse.headers) || oODataService.getCsrfToken() // prefer a new token
887
+ );
888
+ jQuery.sap.log.debug('Received OData response for GET "' + sRequestUrl + '"', null,
889
+ "sap.ui2.srvc.ODataWrapper");
890
+ // The sap-message header is a Gateway feature used e.g. by the INTEROP service
891
+ // It returns structured information (as XML). We don't evaluate it, we only log it.
892
+ // Severity is also part of the structure, but we simply assume warning.
893
+ sSapMessage = headerValue("sap-message", oResponse.headers);
894
+ if (sSapMessage) {
895
+ jQuery.sap.log.warning("SAP message for GET " + sRequestUrl, sSapMessage,
896
+ "sap.ui2.srvc.ODataWrapper");
897
+ }
898
+ if (oDeferred) {
899
+ // put arguments for fnSuccess into cache; clone oData first
900
+ oDeferred.resolve(JSON.parse(JSON.stringify(oData)), oODataService.getCsrfToken());
901
+ }
902
+ // Note: drop excess parameters; try/catch
903
+ sap.ui2.srvc.call(fnSuccess.bind(null, oData), fnFailure);
904
+ }
905
+
906
+ fnFailure = fnFailure || oODataService.getDefaultErrorHandler();
907
+ this.check(fnSuccess, fnFailure);
908
+
909
+ if (bCache) {
910
+ OData.read.$cache = OData.read.$cache || new sap.ui2.srvc.Map();
911
+ oDeferred = OData.read.$cache.get(sRequestUrl); // the promise is cached
912
+ if (oDeferred) {
913
+ jQuery.sap.log.debug('Using cached response for GET "' + sRequestUrl + '"', null,
914
+ "sap.ui2.srvc.ODataWrapper");
915
+ oDeferred.done(function (oData, sCachedCsrfToken) {
916
+ // prefer our own token
917
+ oODataService.setCsrfToken(oODataService.getCsrfToken() || sCachedCsrfToken);
918
+ // clone cached oData before passing to success handler
919
+ // Note: try/catch
920
+ sap.ui2.srvc.call(fnSuccess.bind(null, JSON.parse(JSON.stringify(oData))), fnFailure);
921
+ }).fail(fnFailure);
922
+ return;
923
+ }
924
+ oDeferred = new jQuery.Deferred();
925
+ OData.read.$cache.put(sRequestUrl, oDeferred.promise());
926
+ }
927
+
928
+ readOrBatch(
929
+ sRelativeUrl,
930
+ success,
931
+ this.onError.bind(this, "GET", sRequestUrl, fnFailure, oDeferred)
932
+ );
933
+ };
934
+
935
+ /**
936
+ * Performs a batch request with the given payload.
937
+ *
938
+ * <b>Warning:</b> This bypasses the batch queue.
939
+ *
940
+ * @param {object} oPayload
941
+ * payload of the request (in intermediate format)
942
+ * @param {function(object)} [fnSuccess]
943
+ * callback function that is executed if the request succeeds, taking the processed data
944
+ * @param {function (string, object=)} [fnFailure]
945
+ * error handler taking an error message and, since version 1.28.6, an
946
+ * optional object containing the complete error information as delivered
947
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
948
+ * for more details.
949
+ * Defaults to the OData service facade's default error handler
950
+ * @private
951
+ */
952
+ this.batch = function (oPayload, fnSuccess, fnFailure) {
953
+ var sRequestUrl = toRequestUrl("$batch");
954
+
955
+ doRequest(sRequestUrl, "POST", oPayload, fnSuccess, fnFailure, OData.batchHandler);
956
+ };
957
+
958
+ /**
959
+ * Submits the current batch queue opened by {@link #openBatchQueue} by sending a single
960
+ * "$batch" request to the OData service and deletes the current batch queue immediately.
961
+ *
962
+ * @param {function()} [fnBatchAccepted]
963
+ * A callback function that is executed if the batch request is accepted by the
964
+ * server, no matter whether individual operations fail. It will be called <b>after</b> all
965
+ * success or failure handlers of individual operations.
966
+ * @param {function(string, object=)} [fnBatchItselfFailed]
967
+ * A callback function that is executed if the batch request itself
968
+ * fails, error handler taking an error message and, since version
969
+ * 1.28.6, an optional object containing the complete error information
970
+ * as delivered by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
971
+ * for more details.<br />
972
+ *
973
+ * Defaults to the OData service facade's default
974
+ * error handler ({@link sap.ui2.srvc.ODataService#getDefaultErrorHandler}).<br />
975
+ *
976
+ * This is called <b>instead</b> of individual failure handlers in this case!
977
+ *
978
+ * @public
979
+ * @since 1.19.0
980
+ *
981
+ * @see #openBatchQueue
982
+ * @see #isBatchQueueOpen
983
+ */
984
+ this.submitBatchQueue = function (fnBatchAccepted, fnBatchItselfFailed) {
985
+ /*jslint nomen:true */
986
+ var aMyDeferredQueue = aDeferredQueue; // used in closure below
987
+
988
+ /*
989
+ * Success handler for $batch request.
990
+ *
991
+ * @param {object} oData
992
+ */
993
+ function onBatchSuccess (oData) {
994
+ var iActual = oData.__batchResponses.length,
995
+ iExpected = aMyDeferredQueue.length;
996
+
997
+ if (iExpected !== iActual) {
998
+ that.onError("POST", toRequestUrl("$batch"), fnBatchItselfFailed, /*oDeferred*/null, {
999
+ message: "Protocol error! Expected " + iExpected
1000
+ + " responses, but received " + iActual
1001
+ });
1002
+ return;
1003
+ }
1004
+
1005
+ oData.__batchResponses.forEach(function (oResponse, i) {
1006
+ // Note: "raw" success/failure signatures for all requests!
1007
+ var oDeferred = aMyDeferredQueue[i];
1008
+ if (oResponse.response) {
1009
+ // if it contains a nested response, it must be a failure
1010
+ reject(oDeferred, oResponse);
1011
+ } else if (oResponse.__changeResponses) {
1012
+ // successful change set
1013
+ oResponse.__changeResponses.forEach(function (oChangeResponse, j) {
1014
+ oDeferred[j].resolve(oChangeResponse.data, oChangeResponse);
1015
+ });
1016
+ } else {
1017
+ // successful GET request
1018
+ oDeferred.resolve(oResponse.data, oResponse);
1019
+ }
1020
+ });
1021
+
1022
+ if (fnBatchAccepted) {
1023
+ sap.ui2.srvc.call(fnBatchAccepted, oODataService.getDefaultErrorHandler());
1024
+ }
1025
+ }
1026
+
1027
+ if (!aBatchQueue) {
1028
+ throw new sap.ui2.srvc.Error("No open batch queue to submit", "sap.ui2.srvc.ODataWrapper");
1029
+ }
1030
+
1031
+ if (aBatchQueue.length > 0) {
1032
+ this.batch({__batchRequests: aBatchQueue}, onBatchSuccess, fnBatchItselfFailed);
1033
+ } else if (fnBatchAccepted) {
1034
+ // call success handler (if given) directly (async) if batchQueue is empty
1035
+ sap.ui2.srvc.call(fnBatchAccepted, oODataService.getDefaultErrorHandler(), /*async=*/true);
1036
+ }
1037
+
1038
+ aBatchQueue = undefined;
1039
+ aDeferredQueue = undefined; // be nice to the garbage collector
1040
+ };
1041
+
1042
+ /**
1043
+ * Returns this wrapper's string representation.
1044
+ *
1045
+ * @param {boolean} [bVerbose=false]
1046
+ * flag whether to show all properties
1047
+ * @returns {string}
1048
+ * this wrapper's string representation
1049
+ *
1050
+ * @public
1051
+ * @since 1.19.0
1052
+ */
1053
+ this.toString = function (bVerbose) {
1054
+ var aResult = ['sap.ui2.srvc.ODataWrapper({sBaseUrl:"', sBaseUrl, '"'];
1055
+ // if (bVerbose) {
1056
+ // }
1057
+ aResult.push("})");
1058
+ return aResult.join("");
1059
+ };
1060
+
1061
+ /** executes a put request
1062
+ *
1063
+ * @param {string} sRelativeUrl the relative URL to use
1064
+ * @param {object} oPayload the payload
1065
+ * @param {function ()} [fnSuccess]
1066
+ * callback function that is executed if the request succeeds, taking no data
1067
+ * @param {function (string, object=)} [fnFailure]
1068
+ * error handler taking an error message and, since version 1.28.6, an
1069
+ * optional object containing the complete error information as delivered
1070
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1071
+ * for more details.
1072
+ * Defaults to the OData service facade's default error handler
1073
+ */
1074
+ this.put = function (sRelativeUrl, oPayload, fnSuccess, fnFailure) {
1075
+ requestOrBatch(sRelativeUrl, "PUT", oPayload, function (oData) {
1076
+ if (fnSuccess) {
1077
+ // Note: try/catch already done by doRequest()
1078
+ fnSuccess(); // drop excess parameters (oData === undefined)
1079
+ }
1080
+ }, fnFailure);
1081
+ };
1082
+ /**
1083
+ * Generic entity update method.
1084
+ *
1085
+ * @param {object} oEntity
1086
+ * the datajs representation of the entity
1087
+ * @param {function ()} [fnSuccess]
1088
+ * callback function that is executed if the request succeeds, taking no data
1089
+ * @param {function (string, object=)} [fnFailure]
1090
+ * error handler taking an error message and, since version 1.28.6, an
1091
+ * optional object containing the complete error information as delivered
1092
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1093
+ * for more details.
1094
+ * Defaults to the OData service facade's default error handler
1095
+ *
1096
+ * @public
1097
+ * @since 1.19.0
1098
+ */
1099
+ this.update = function (oEntity, fnSuccess, fnFailure) {
1100
+ /*jslint nomen:true */
1101
+ var oPayload = {
1102
+ __metadata: {
1103
+ type: oEntity.__metadata && oEntity.__metadata.type
1104
+ }
1105
+ },
1106
+ sPropertyName,
1107
+ sRelativeUrl = toRelativeUrl(oEntity.__metadata.uri);
1108
+
1109
+ // copy all original property values of entity into request data
1110
+ for (sPropertyName in oEntity) {
1111
+ if (Object.prototype.hasOwnProperty.call(oEntity, sPropertyName)
1112
+ && sPropertyName.indexOf("$") !== 0
1113
+ && typeof oEntity[sPropertyName] !== "object") {
1114
+ oPayload[sPropertyName] = oEntity[sPropertyName];
1115
+ }
1116
+ }
1117
+ this.put(sRelativeUrl, oPayload, fnSuccess, fnFailure);
1118
+ };
1119
+
1120
+ // constructor code -------------------------------------------------------
1121
+ if (!sap.ui2.srvc.Map) {
1122
+ fnRequire("sap.ui2.srvc.utils");
1123
+ }
1124
+
1125
+ // Detect old API and transform it to the current one
1126
+ if (typeof oSettings === "string") {
1127
+ oSettings = handleOldConstructorApi(arguments);
1128
+ } else if (typeof oSettings === "object") {
1129
+ // clone to ensure it is not changed afterwards
1130
+ oSettings = cloneObject(oSettings);
1131
+ }
1132
+
1133
+ if (!oSettings || !oSettings.baseUrl || typeof oSettings.baseUrl !== "string") {
1134
+ throw new sap.ui2.srvc.Error("Missing base URL", "sap.ui2.srvc.ODataWrapper");
1135
+ }
1136
+ if (!oODataService || typeof oODataService !== "object") {
1137
+ throw new sap.ui2.srvc.Error("Missing OData service facade", "sap.ui2.srvc.ODataWrapper");
1138
+ }
1139
+
1140
+ // ensure that base URL has a trailing /
1141
+ oSettings.baseUrl = oSettings.baseUrl.replace(/\/$/, "") + "/";
1142
+
1143
+ sBaseUrl = oSettings.baseUrl;
1144
+ bSupportsChangeSets = oSettings.supportsChangeSets;
1145
+
1146
+ if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration === "undefined") {
1147
+ jQuery.sap.log.error("sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration is not defined!",
1148
+ "the sap.ui2.srvc.ODataWrapper constructor was called before the static property was defined",
1149
+ "sap.ui2.srvc.ODataWrapper");
1150
+ } else if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] === "undefined") {
1151
+
1152
+ // Define the sticky session configuration for the base URL managed by
1153
+ // this ODataWrapper instance.
1154
+ sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration[sBaseUrl] = {
1155
+ enabled: false, // sticky session disabled by default
1156
+ value: undefined // the value detected if enabled = true
1157
+ };
1158
+ }
1159
+ };
1160
+
1161
+ /**
1162
+ * Detects and, if needed, converts old api calls of the createODataWrapper function to the current one.
1163
+ * @param {object} oArgs
1164
+ * An arguments object containing the parameters of the createODataWrapper function. See createODataWrapper for details
1165
+ * @returns {object}
1166
+ * Settings for ODataWrapper and default failure function
1167
+ * @private
1168
+ */
1169
+ function handleOldCreateODataWrapperApi (oArgs) {
1170
+ var oSettings = {};
1171
+ var oTransformedApi = {};
1172
+
1173
+ oSettings.baseUrl = oArgs[0];
1174
+ // These are non mandatory parameters and might be undefined
1175
+ if (typeof oArgs[1] === "boolean") {
1176
+ oSettings.supportsChangeSets = oArgs[1];
1177
+ }
1178
+ if (typeof oArgs[2] === "function") {
1179
+ oTransformedApi.defaultFailure = oArgs[2];
1180
+ }
1181
+ oTransformedApi.settings = oSettings;
1182
+
1183
+ return oTransformedApi;
1184
+ }
1185
+
1186
+ /**
1187
+ * Clones an Object to avoid unwanted changes
1188
+ * @param {object} oInputObject
1189
+ * The object that needs to be cloned
1190
+ * @returns {object}
1191
+ * The cloned object
1192
+ * @private
1193
+ */
1194
+ function cloneObject (oInputObject) {
1195
+ if (oInputObject === undefined) {
1196
+ return undefined;
1197
+ }
1198
+ try {
1199
+ return JSON.parse(JSON.stringify(oInputObject));
1200
+ } catch (e) {
1201
+ return undefined;
1202
+ }
1203
+ }
1204
+
1205
+ /*
1206
+ * Configuration for sticky session is done here as it is a static property
1207
+ * that should not rely on the instance initialization. Also, it may happen
1208
+ * that an already defined sap.ui2.srvc.ODataWrapper name is re-assigned
1209
+ * because this file is re-executed or re-loaded. Such a scenario may be
1210
+ * possible if this file is required twice (after being registered to two
1211
+ * different module paths).
1212
+ */
1213
+ if (typeof sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration === "undefined") {
1214
+ sap.ui2.srvc.ODataWrapper.oStickySessionConfiguration = {};
1215
+ }
1216
+
1217
+ // public factory function ***************************************************
1218
+
1219
+ /**
1220
+ * Checks the sap-statistics setting form UI5's configuration and set it on the ODataWrapper.
1221
+ * If UI5 is not available, it is checked if the sap-statistics query parameter is set.
1222
+ * Note: this function is directly executed
1223
+ * @param {string} getWindowLocationSearch
1224
+ * value of window.location.search; to be able to test the behavior of this method with
1225
+ * different search strings.
1226
+ * @private
1227
+ */
1228
+ sap.ui2.srvc.testPublishAt(sap.ui2.srvc.ODataWrapper);
1229
+ function checkSapStatisticsSetting (sWindowLocationSearch) {
1230
+ try {
1231
+ // read the value from UI5 as it may be set via [CTRL-SHIFT-ALT-P]
1232
+ sap.ui2.srvc.ODataWrapper["sap-statistics"] =
1233
+ sap.ui.getCore().getConfiguration().getStatistics();
1234
+ } catch (e) {
1235
+ // Read sap-statistics directly form query parameter in scenarios without UI5
1236
+ sap.ui2.srvc.ODataWrapper["sap-statistics"] =
1237
+ /sap-statistics=(true|x|X)/.test(sWindowLocationSearch);
1238
+ }
1239
+ }
1240
+ // call function directly (immediate function pattern will break testPublishAt)
1241
+ checkSapStatisticsSetting(window.location.search);
1242
+
1243
+ /**
1244
+ * Constructs and returns a {@link sap.ui2.srvc.ODataWrapper} which knows its
1245
+ * {@link sap.ui2.srvc.ODataService} twin, see {@link sap.ui2.srvc.ODataWrapper#getODataService}.
1246
+ *
1247
+ * @param {object} oSettings
1248
+ * An object containing various properties:
1249
+ * <pre>
1250
+ * {
1251
+ * baseUrl: "/OData/OData.svc", // Mandatory base URL of the OData service
1252
+ * supportsChangeSets: false, // Type: boolean, Default: false
1253
+ * // Whether the OData service supports change sets with <b>multiple</b>
1254
+ * // operations bundled into a single logical unit of work. Otherwise
1255
+ * // each modifying operation is isolated in a change set of its own.
1256
+ * "sap-language": "EN", // header which is set for all requests sent
1257
+ * "sap-client": 120, // header which is set for all requests sent
1258
+ * "sap-statistics": true // header which is set for all requests sent; in order to receive
1259
+ * // some performance statistics
1260
+ * }
1261
+ * </pre>
1262
+ * @param {function (string, object=)} [fnDefaultFailure]
1263
+ * error handler taking an error message and, since version 1.28.6, an
1264
+ * optional object containing the complete error information as delivered
1265
+ * by the ODataService. See fnFailure parameter of {@link sap.ui2.srvc.ODataWrapper#onError}
1266
+ * for more details.
1267
+ * @returns {sap.ui2.srvc.ODataWrapper}
1268
+ * new instance of ODataWapper/ODataService
1269
+ *
1270
+ * @public
1271
+ * @since 1.19.1
1272
+ */
1273
+ // OLD API: function (sBaseUrl, bSupportsChangeSets, fnDefaultFailure)
1274
+ sap.ui2.srvc.createODataWrapper = function (oSettings, fnDefaultFailure) {
1275
+ // If old API is used we need to transform it to the current one.
1276
+ if (typeof arguments[0] === "string") {
1277
+ var oTransformedApi = handleOldCreateODataWrapperApi(arguments);
1278
+ oSettings = oTransformedApi.settings;
1279
+ if (oTransformedApi.defaultFailure) {
1280
+ fnDefaultFailure = oTransformedApi.defaultFailure;
1281
+ }
1282
+ } else if (typeof arguments[0] === "object") {
1283
+ // clone to ensure it is not changed afterwards
1284
+ oSettings = cloneObject(oSettings);
1285
+ }
1286
+
1287
+ function Service () {
1288
+ var oWrapper = new sap.ui2.srvc.ODataWrapper(oSettings, this);
1289
+ fnRequire("sap.ui2.srvc.ODataService");
1290
+ sap.ui2.srvc.ODataService.call(this, oWrapper, fnDefaultFailure);
1291
+ return oWrapper;
1292
+ }
1293
+
1294
+ return new Service(); // BEWARE: this actually _returns_ the wrapper, not the service!
1295
+ };
1296
+ /// @end
1297
+ }());