cds-caching 1.3.0 → 1.3.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.
|
@@ -1655,7 +1655,7 @@ sap.ui.predefine("sap/tnt/library", ["./IllustratedMessageType","sap/ui/base/Dat
|
|
|
1655
1655
|
* (c) Copyright 2025 SAP SE or an SAP affiliate company.
|
|
1656
1656
|
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
|
|
1657
1657
|
*/
|
|
1658
|
-
sap.ui.predefine("sap/ui/Global", ["sap/ui/VersionInfo","sap/base/Log","sap/base/assert","sap/base/util/ObjectPath"],function(e,a,r,t){"use strict";if(globalThis.OpenAjax&&globalThis.OpenAjax.hub){OpenAjax.hub.registerLibrary("sap","http://www.sap.com/","0.1",{})}var n;let s={version:"1.136.0",buildinfo:{lastchange:"",buildtime:"
|
|
1658
|
+
sap.ui.predefine("sap/ui/Global", ["sap/ui/VersionInfo","sap/base/Log","sap/base/assert","sap/base/util/ObjectPath"],function(e,a,r,t){"use strict";if(globalThis.OpenAjax&&globalThis.OpenAjax.hub){OpenAjax.hub.registerLibrary("sap","http://www.sap.com/","0.1",{})}var n;let s={version:"1.136.0",buildinfo:{lastchange:"",buildtime:"20260415-1158"}};s=Object.assign(sap.ui,s);var o=sap.ui.loader._.getSyncCallBehavior();sap.ui.getVersionInfo=function(r){if(r&&r.async){a.info("Do not use deprecated function 'sap.ui.getVersionInfo'. Use"+" 'sap/ui/VersionInfo' module's asynchronous .load function instead")}else{a.warning("Do not use deprecated function 'sap.ui.getVersionInfo' synchronously! Use"+" 'sap/ui/VersionInfo' module's asynchronous .load function instead","Deprecation",null,function(){return{type:"sap.ui.getVersionInfo",name:"Global"}})}return e._load(r)};sap.ui.namespace=function(e){r(false,"sap.ui.namespace is long time deprecated and shouldn't be used");return t.create(e)};sap.ui.lazyRequire=function(e,s,i){r(typeof e==="string"&&e,"lazyRequire: sClassName must be a non-empty string");r(!s||typeof s==="string","lazyRequire: sMethods must be empty or a string");if(o===2){a.error("[nosync] lazy stub creation ignored for '"+e+"'");return}var u=e.replace(/\//gi,"."),l=u.lastIndexOf("."),c=u.substr(0,l),p=u.substr(l+1),f=t.create(c),y=f[p],d=(s||"new").split(" "),b=d.indexOf("new");i=i||u;if(!y){if(b>=0){y=function(){if(o){if(o===1){a.error("[nosync] lazy stub for constructor '"+u+"' called")}}else{a.debug("lazy stub for constructor '"+u+"' called.")}sap.ui.requireSync(i.replace(/\./g,"/"));var t=f[p];r(typeof t==="function","lazyRequire: oRealClass must be a function after loading");if(t._sapUiLazyLoader){throw new Error("lazyRequire: stub '"+u+"'has not been replaced by module '"+i+"'")}var s=Object.create(t.prototype);if(!(this instanceof y)){n=n||sap.ui.require("sap/ui/base/Object");if(n&&s instanceof n){a.error("Constructor "+e+' has been called without "new" operator!',null,null,function(){try{throw new Error}catch(e){return e}})}}var l=t.apply(s,arguments);if(l&&(typeof l==="function"||typeof l==="object")){s=l}return s};y._sapUiLazyLoader=true;d.splice(b,1)}else{y={}}f[p]=y}d.forEach(function(e){if(!y[e]){y[e]=function(){if(o){if(o===1){a.error("[no-sync] lazy stub for method '"+u+"."+e+"' called")}}else{a.debug("lazy stub for method '"+u+"."+e+"' called.")}sap.ui.requireSync(i.replace(/\./g,"/"));var t=f[p];r(typeof t==="function"||typeof t==="object","lazyRequire: oRealClass must be a function or object after loading");r(typeof t[e]==="function","lazyRequire: method must be a function");if(t[e]._sapUiLazyLoader){throw new Error("lazyRequire: stub '"+u+"."+e+"' has not been replaced by loaded module '"+i+"'")}return t[e].apply(t,arguments)};y[e]._sapUiLazyLoader=true}})};sap.ui.lazyRequire._isStub=function(e){r(typeof e==="string"&&e,"lazyRequire._isStub: sClassName must be a non-empty string");var a=e.lastIndexOf("."),n=e.slice(0,a),s=e.slice(a+1),o=t.get(n||"");return!!(o&&typeof o[s]==="function"&&o[s]._sapUiLazyLoader)};sap.ui.resource=function(e,a){r(typeof e==="string","sLibraryName must be a string");r(typeof a==="string","sResourcePath must be a string");return sap.ui.require.toUrl((String(e).replace(/\./g,"/")+"/"+a).replace(/^\/*/,""))};sap.ui.localResources=function(e){r(e,"sNamespace must not be empty");var a={};a[e.replace(/\./g,"/")]="./"+e.replace(/\./g,"/");sap.ui.loader.config({paths:a})};return s});
|
|
1659
1659
|
/*!
|
|
1660
1660
|
* OpenUI5
|
|
1661
1661
|
* (c) Copyright 2025 SAP SE or an SAP affiliate company.
|
package/lib/CachingService.js
CHANGED
|
@@ -87,15 +87,16 @@ class CachingService extends cds.Service {
|
|
|
87
87
|
this.log.debug(`CLEAR`);
|
|
88
88
|
await this.cache.clear();
|
|
89
89
|
|
|
90
|
-
// Also clear statistics
|
|
91
90
|
if (this.statistics) {
|
|
92
91
|
await this.statistics.resetCurrentStats();
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
if (event.data.clearStatistics) {
|
|
94
|
+
try {
|
|
95
|
+
await this.statistics.deleteMetrics();
|
|
96
|
+
await this.statistics.deleteKeyMetrics();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.log.warn(`Failed to clear statistics for cache ${this.name}:`, error);
|
|
99
|
+
}
|
|
99
100
|
}
|
|
100
101
|
}
|
|
101
102
|
}
|
|
@@ -173,10 +174,10 @@ class CachingService extends cds.Service {
|
|
|
173
174
|
return this.basicOperations.delete(key);
|
|
174
175
|
}
|
|
175
176
|
|
|
176
|
-
async clear(tx = null) {
|
|
177
|
-
if (tx) return this.basicOperations.clear(tx);
|
|
178
|
-
if (this.options.transactionalOperations) return this.basicOperations.clearInTx();
|
|
179
|
-
return this.basicOperations.clear();
|
|
177
|
+
async clear(options = {}, tx = null) {
|
|
178
|
+
if (tx) return this.basicOperations.clear(options, tx);
|
|
179
|
+
if (this.options.transactionalOperations) return this.basicOperations.clearInTx(options);
|
|
180
|
+
return this.basicOperations.clear(options);
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
async deleteByTag(tag, tx = null) {
|
|
@@ -156,12 +156,15 @@ class BasicOperations {
|
|
|
156
156
|
|
|
157
157
|
/**
|
|
158
158
|
* Clear all cache entries
|
|
159
|
+
* @param {object} options - clear options
|
|
160
|
+
* @param {boolean} [options.clearStatistics=false] - also delete persisted metrics and key metrics
|
|
159
161
|
* @returns {Promise<void>}
|
|
160
162
|
*/
|
|
161
|
-
async clear(tx = null) {
|
|
163
|
+
async clear(options = {}, tx = null) {
|
|
162
164
|
const srv = tx || this.cache;
|
|
163
165
|
await srv.send('CLEAR', {
|
|
164
|
-
deleteAll: true
|
|
166
|
+
deleteAll: true,
|
|
167
|
+
clearStatistics: options.clearStatistics || false
|
|
165
168
|
});
|
|
166
169
|
|
|
167
170
|
// Record the native clear operation
|
|
@@ -318,10 +321,10 @@ class BasicOperations {
|
|
|
318
321
|
}
|
|
319
322
|
}
|
|
320
323
|
|
|
321
|
-
async clearInTx() {
|
|
324
|
+
async clearInTx(options = {}) {
|
|
322
325
|
const tx = await this.cache.tx();
|
|
323
326
|
try {
|
|
324
|
-
await this.clear(tx);
|
|
327
|
+
await this.clear(options, tx);
|
|
325
328
|
await tx.commit();
|
|
326
329
|
return;
|
|
327
330
|
} catch (error) {
|
|
@@ -78,13 +78,34 @@ class KeyManager {
|
|
|
78
78
|
case "Request":
|
|
79
79
|
case "NoaRequest":
|
|
80
80
|
case "ODataRequest":
|
|
81
|
-
//
|
|
81
|
+
// Hash request information including the HTTP URL which
|
|
82
|
+
// contains $filter/$select/$orderby query parameters.
|
|
83
|
+
//
|
|
84
|
+
// Why req.query (CQN) is unreliable for hashing:
|
|
85
|
+
// NoaRequest ("New OData Adapter Request", defined in
|
|
86
|
+
// @sap/cds/libx/odata/ODataAdapter.js) carries cds.ql
|
|
87
|
+
// query objects where SELECT.where lives on the prototype
|
|
88
|
+
// chain, not as an own property. JSON.stringify only
|
|
89
|
+
// serializes own enumerable properties, so the where clause
|
|
90
|
+
// from $filter is silently dropped — two requests that
|
|
91
|
+
// differ only by $filter produce the same hash.
|
|
92
|
+
// This does NOT affect programmatic CQL (e.g.
|
|
93
|
+
// SELECT.from(Entity).where(...)) where `where` is an own
|
|
94
|
+
// property. It only affects CQN built from OData URL
|
|
95
|
+
// parsing in the adapter pipeline.
|
|
96
|
+
//
|
|
97
|
+
// The HTTP URL (req._.req.url / req.http.req.url) always
|
|
98
|
+
// contains the full query string as a plain string and is
|
|
99
|
+
// the reliable source for all OData query options. When a
|
|
100
|
+
// URL is available it supersedes req.query.
|
|
101
|
+
const url = this.normalizeUrl(keyOrObject._?.req?.url || keyOrObject.http?.req?.url);
|
|
82
102
|
return this.createHash({
|
|
83
103
|
method: keyOrObject.method,
|
|
84
104
|
path: keyOrObject.path || keyOrObject.http?.req?.path,
|
|
105
|
+
url,
|
|
85
106
|
data: keyOrObject.data,
|
|
86
107
|
params: keyOrObject.params,
|
|
87
|
-
query: keyOrObject.query,
|
|
108
|
+
query: url ? undefined : keyOrObject.query,
|
|
88
109
|
event: keyOrObject.event,
|
|
89
110
|
target: keyOrObject.target?.name
|
|
90
111
|
});
|
|
@@ -105,6 +126,23 @@ class KeyManager {
|
|
|
105
126
|
return this.createHash({ content: keyOrObject });
|
|
106
127
|
}
|
|
107
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Normalize a URL by sorting its query parameters alphabetically by key.
|
|
131
|
+
* Ensures the same parameters in different order produce the same cache key.
|
|
132
|
+
* @param {string} url - URL string to normalize
|
|
133
|
+
* @returns {string|undefined} - Normalized URL or undefined if input is falsy
|
|
134
|
+
*/
|
|
135
|
+
normalizeUrl(url) {
|
|
136
|
+
if (!url) return undefined;
|
|
137
|
+
try {
|
|
138
|
+
const urlObj = new URL(url, 'http://localhost');
|
|
139
|
+
urlObj.searchParams.sort();
|
|
140
|
+
return urlObj.pathname + (urlObj.searchParams.size ? '?' + urlObj.searchParams.toString() : '');
|
|
141
|
+
} catch {
|
|
142
|
+
return url;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
108
146
|
/**
|
|
109
147
|
* Serialize function arguments for cache key generation
|
|
110
148
|
* @param {any} arg - argument to serialize
|