@sapui5/sap.ushell_abap 1.94.0 → 1.96.1

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