@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
344 lines (329 loc) • 12.7 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2026 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
//Provides class sap.ui.model.odata.v4.ODataUtils
sap.ui.define([
"sap/base/i18n/date/CalendarType",
"sap/ui/core/format/DateFormat",
"sap/ui/model/odata/ODataUtils",
"sap/ui/model/odata/v4/lib/_Batch",
"sap/ui/model/odata/v4/lib/_Helper"
], function (CalendarType, DateFormat, BaseODataUtils, _Batch, _Helper) {
"use strict";
// see https://docs.oasis-open.org/odata/odata/v4.01/os/abnf/
var oDateFormatter,
oDateTimeOffsetFormatter,
sDateValue = "\\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\\d|3[01])",
oTimeFormatter,
sTimeOfDayValue = "(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(\\.\\d{1,12})?)?",
rDate = new RegExp("^" + sDateValue + "$"),
rDateTimeOffset = new RegExp("^" + sDateValue + "T" + sTimeOfDayValue
+ "(?:Z|[-+](?:0\\d|1[0-3]):[0-5]\\d|[-+]14:00)$", "i"),
rTimeOfDay = new RegExp("^" + sTimeOfDayValue + "$"),
/**
* @classdesc
* A collection of methods which help to consume OData V4 services.
*
* @public
* @since 1.43.0
* @namespace
* @alias sap.ui.model.odata.v4.ODataUtils
*/
ODataUtils = {
/**
* Sets the static date and time formatter instances.
*
* @private
*/
_setDateTimeFormatter : function () {
oDateFormatter = DateFormat.getDateInstance({
calendarType : CalendarType.Gregorian,
pattern : "yyyy-MM-dd",
strictParsing : true,
UTC : true
});
oDateTimeOffsetFormatter = DateFormat.getDateTimeInstance({
calendarType : CalendarType.Gregorian,
pattern : "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
strictParsing : true
});
oTimeFormatter = DateFormat.getTimeInstance({
calendarType : CalendarType.Gregorian,
pattern : "HH:mm:ss.SSS",
strictParsing : true,
UTC : true
});
},
/**
* Compares the given OData values.
*
* @param {any} vValue1
* The first value to compare
* @param {any} vValue2
* The second value to compare
* @param {boolean|string} [vEdmType]
* If <code>true</code> or "Decimal", the string values <code>vValue1</code> and
* <code>vValue2</code> are assumed to be valid "Edm.Decimal" or "Edm.Int64" values
* and are compared as a decimal number (only sign, integer and fraction digits; no
* exponential format).
* If "DateTime", the string values <code>vValue1</code> and <code>vValue2</code>
* are assumed to be valid "Edm.DateTimeOffset" values and are compared based on the
* corresponding number of milliseconds since 1 January, 1970 UTC.
* Otherwise the values are compared with the JavaScript operators <code>===</code>
* and <code>></code>.
* @returns {number}
* The result of the comparison: <code>0</code> if the values are equal,
* <code>1</code> if the first value is larger, <code>-1</code> if the second value
* is larger, <code>NaN</code> if they cannot be compared
*
* @public
* @since 1.43.0
*/
compare : function (vValue1, vValue2, vEdmType) {
if (vEdmType === true || vEdmType === "Decimal") {
return BaseODataUtils.compare(vValue1, vValue2, true);
}
if (vEdmType === "DateTime") {
return BaseODataUtils.compare(
ODataUtils.parseDateTimeOffset(vValue1),
ODataUtils.parseDateTimeOffset(vValue2));
}
return BaseODataUtils.compare(vValue1, vValue2);
},
/**
* Deserializes a batch response body using the batch boundary from the given value of
* the "Content-Type" header.
*
* @param {string} sContentType
* The value of the "Content-Type" header from the batch response, for example
* "multipart/mixed; boundary=batch_123456"
* @param {string} sResponseBody
* A batch response body
* @returns {object[]}
* An array containing responses from the batch response body, each with the following
* structure:
* <ul>
* <li> <code>status</code>: {number} HTTP status code
* <li> <code>statusText</code>: {string} (optional) HTTP status text
* <li> <code>headers</code>: {object} Map of response headers
* <li> <code>responseText</code>: {string} Response body
* </ul>
* If the specified <code>sResponseBody</code> contains responses for change sets,
* then the corresponding response objects will be returned in a nested array.
* @throws {Error} If
* <ul>
* <li> the <code>sContentType</code> parameter does not represent a
* "multipart/mixed" media type with "boundary" parameter
* <li> the "charset" parameter of the "Content-Type" header of a nested response
* has a value other than "UTF-8"
* <li> there is no "Content-ID" header for a change set response or its value is
* not a number
* </ul>
*
* @private
* @since 1.90.0
* @ui5-restricted sap.ui.integration
*/
deserializeBatchResponse : function (sContentType, sResponseBody) {
return _Batch.deserializeBatchResponse(sContentType, sResponseBody);
},
/**
* Formats the given OData value into a literal suitable for usage in data binding paths
* and URLs.
*
* @param {any} vValue
* The value according to <a href=
* "https://docs.oasis-open.org/odata/odata-json-format/v4.01/odata-json-format-v4.01.html#sec_PrimitiveValue"
* >"OData JSON Format Version 4.01" section "7.1 Primitive Value"</a>
* @param {string} sType
* The OData primitive type, for example "Edm.String"
* @returns {string}
* The literal according to <a href=
* "https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31361028"
* >"OData Version 4.01. Part 2: URL Conventions"</a> section
* "5.1.1.14.1 Primitive Literals"
* @throws {Error}
* If the value is undefined or the type is not supported
*
* @example <caption>Use <code>formatLiteral</code> together with
* <code>encodeURIComponent</code> to create a properly encoded data binding path for
* {@link sap.ui.model.odata.v4.ODataModel}.</caption>
* var sSalesOrderId = ODataUtils.formatLiteral("A/B&C", "Edm.String"),
* // expected result: "'A/B&C'"
* sPath = "/" + encodeURIComponent("SalesOrderList(" + sSalesOrderId + ")");
* // expected result: "/SalesOrderList('A%2FB%26C')"
*
* @public
* @since 1.64.0
*/
formatLiteral : function (vValue, sType) {
return _Helper.formatLiteral(vValue, sType);
},
/**
* Parses an "Edm.Date" value and returns the corresponding JavaScript <code>Date</code>
* value (UTC with a time value of "00:00:00").
*
* @param {string} sDate
* The "Edm.Date" value to parse
* @returns {Date}
* The JavaScript <code>Date</code> value
* @throws {Error}
* If the input cannot be parsed
*
* @public
* @since 1.43.0
*/
parseDate : function (sDate) {
var oDate = rDate.test(sDate) && oDateFormatter.parse(sDate);
if (!oDate) {
throw new Error("Not a valid Edm.Date value: " + sDate);
}
return oDate;
},
/**
* Parses an "Edm.DateTimeOffset" value and returns the corresponding JavaScript
* <code>Date</code> value.
*
* @param {string} sDateTimeOffset
* The "Edm.DateTimeOffset" value to parse
* @returns {Date}
* The JavaScript <code>Date</code> value
* @throws {Error}
* If the input cannot be parsed
*
* @public
* @since 1.43.0
*/
parseDateTimeOffset : function (sDateTimeOffset) {
var oDateTimeOffset,
aMatches = rDateTimeOffset.exec(sDateTimeOffset);
if (aMatches) {
if (aMatches[1] && aMatches[1].length > 4) {
// "round" to millis, BEWARE of the dot!
sDateTimeOffset
= sDateTimeOffset.replace(aMatches[1], aMatches[1].slice(0, 4));
}
oDateTimeOffset = oDateTimeOffsetFormatter.parse(sDateTimeOffset.toUpperCase());
}
if (!oDateTimeOffset) {
throw new Error("Not a valid Edm.DateTimeOffset value: " + sDateTimeOffset);
}
return oDateTimeOffset;
},
/**
* Parses an "Edm.TimeOfDay" value and returns the corresponding JavaScript
* <code>Date</code> value (UTC with a date value of "1970-01-01").
*
* @param {string} sTimeOfDay
* The "Edm.TimeOfDay" value to parse
* @returns {Date}
* The JavaScript <code>Date</code> value
* @throws {Error}
* If the input cannot be parsed
*
* @public
* @since 1.43.0
*/
parseTimeOfDay : function (sTimeOfDay) {
var oTimeOfDay;
if (rTimeOfDay.test(sTimeOfDay)) {
if (sTimeOfDay.length > 12) {
// "round" to millis: "HH:mm:ss.SSS"
sTimeOfDay = sTimeOfDay.slice(0, 12);
}
oTimeOfDay = oTimeFormatter.parse(sTimeOfDay);
}
if (!oTimeOfDay) {
throw new Error("Not a valid Edm.TimeOfDay value: " + sTimeOfDay);
}
return oTimeOfDay;
},
/**
* Serializes an array of requests to an object containing the batch request body and
* mandatory headers for the batch request.
*
* @param {object[]} aRequests
* An array consisting of request objects or arrays of request objects, in case
* requests need to be sent in scope of a change set. See example below. Change set
* requests are annotated with a property <code>$ContentID</code> containing the
* corresponding "Content-ID" header from the serialized batch request body.
* @param {string} aRequests[].method
* The HTTP method; only "GET", "POST", "PUT", "PATCH", or "DELETE" are allowed
* @param {string} aRequests[].url
* An absolute or relative URL. If the URL contains a "Content-ID" reference, then the
* reference has to be specified as the zero-based index of the referenced request
* inside the change set. See example below.
* @param {object} aRequests[].headers
* A map of request headers. RFC-2047 encoding rules are not supported. Nevertheless
* non-US-ASCII values can be used. If the value of an "If-Match" header is an object,
* that object's ETag ("@odata.etag") is used instead.
* @param {object} [aRequests[].body]
* The request body. If specified, the <code>oRequest.headers</code> map must contain
* a "Content-Type" header either without "charset" parameter or with "charset"
* parameter having value "UTF-8".
* @param {string} [sEpilogue]
* A string that will be included in the epilogue (which acts like a comment)
* @returns {object}
* An object containing the following properties:
* <ul>
* <li> <code>body</code>: {string} Batch request body
* <li> <code>headers</code>: {object} Map of batch-specific request headers:
* <ul>
* <li> <code>Content-Type</code>: Value for the "Content-Type" header
* <li> <code>MIME-Version</code>: Value for the "MIME-Version" header
* </ul>
* </ul>
* @throws {Error}
* If change sets are nested or an invalid HTTP method is used
*
* @example
* var oBatchRequest = ODataUtils.serializeBatchRequest([
* {
* method : "GET",
* url : "/sap/opu/odata4/IWBEP/TEA_BUSI/0001/Employees('1')",
* headers : {
* Accept : "application/json"
* }
* },
* [{
* method : "POST",
* url : "TEAMS",
* headers : {
* "Content-Type" : "application/json"
* },
* body : {"TEAM_ID" : "TEAM_03"}
* }, {
* method : "POST",
* url : "$0/TEAM_2_Employees",
* headers : {
* "Content-Type" : "application/json",
* "If-Match" : "etag0"
* },
* body : {"Name" : "John Smith"}
* }],
* {
* method : "PATCH",
* url : "/sap/opu/odata4/IWBEP/TEA_BUSI/0001/Employees('3')",
* headers : {
* "Content-Type" : "application/json",
* "If-Match" : {
* "@odata.etag" : "etag1"
* }
* },
* body : {"TEAM_ID" : "TEAM_01"}
* }
* ]);
*
* @private
* @since 1.90.0
* @ui5-restricted sap.ui.integration
*/
serializeBatchRequest : function (aRequests, sEpilogue) {
return _Batch.serializeBatchRequest(aRequests, sEpilogue);
}
};
ODataUtils._setDateTimeFormatter();
return ODataUtils;
}, /* bExport= */ true);