cds-caching 0.3.1 → 0.3.3

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.
package/README.md CHANGED
@@ -82,7 +82,7 @@ For more control, you can specify additional options:
82
82
  "host": "localhost",
83
83
  "port": 6379,
84
84
  "password": "optional",
85
- "url": "redis://..." // Alternative: Redis connection URI
85
+ "uri": "redis://..." // Alternative: Redis connection URI
86
86
 
87
87
  // SQLite specific
88
88
  "url": "sqlite://./cache.sqlite"
@@ -460,12 +460,21 @@ Templates support the following properties:
460
460
 
461
461
 
462
462
  ```javascript
463
-
464
- // Store with tags
463
+ // Store with static tag
465
464
  await cache.set("key", "value", {
466
465
  tags: [{ value: "user-123" }]
467
466
  })
468
467
 
468
+ // Store with template tag (will generate a tag like "tenant-global-user-anonymous")
469
+ await cache.set("key", "value", {
470
+ tags: [{ template: "tenant-{tenant}-user-{user}" }]
471
+ })
472
+
473
+ // Store with data-based tag
474
+ await cache.set("key", { id: 123, name: "Product" }, {
475
+ tags: [{ data: "id", prefix: "product-" }]
476
+ })
477
+
469
478
  // Invalidate by tag
470
479
  await cache.deleteByTag('user-123')
471
480
  ```
@@ -796,4 +805,4 @@ Contributions are welcome! Please read our contributing guidelines and submit pu
796
805
 
797
806
  ### License
798
807
 
799
- This project is licensed under the MIT License - see the LICENSE file for details.
808
+ This project is licensed under the MIT License - see the LICENSE file for details.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cds-caching",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "A caching plugin for SAP CAP applications supporting Redis",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,25 +32,25 @@
32
32
  "release": "release-it"
33
33
  },
34
34
  "peerDependencies": {
35
- "@sap/cds": ">=8"
35
+ "@sap/cds": ">=9"
36
36
  },
37
37
  "workspaces": [
38
38
  ".",
39
39
  "test/**"
40
40
  ],
41
41
  "dependencies": {
42
- "@keyv/compress-gzip": "^2.0.2",
42
+ "@keyv/compress-gzip": "^2.0.3",
43
43
  "@keyv/compress-lz4": "^1.0.0",
44
- "@keyv/redis": "^4.3.1",
45
- "@keyv/sqlite": "^4.0.1",
46
- "keyv": "^5.3.1"
44
+ "@keyv/redis": "^4.5.0",
45
+ "@keyv/sqlite": "^4.0.5",
46
+ "keyv": "^5.3.4"
47
47
  },
48
48
  "devDependencies": {
49
- "eslint": "^9.21.0",
49
+ "eslint": "^9.30.1",
50
50
  "husky": "^9.1.7",
51
- "jest": "^29.7.0",
52
- "release-it": "^18.1.2",
53
- "@cap-js/cds-test": "^0.2.0"
51
+ "jest": "^30.0.4",
52
+ "release-it": "^19.0.3",
53
+ "@cap-js/cds-test": "^0.4.0"
54
54
  },
55
55
  "engines": {
56
56
  "node": ">=16"
@@ -21,7 +21,7 @@ class CachingService extends cds.Service {
21
21
  this.options = this.options || {
22
22
  store: null,
23
23
  compression: null,
24
- credentials: { }
24
+ credentials: {}
25
25
  };
26
26
 
27
27
  let store;
@@ -224,6 +224,7 @@ class CachingService extends cds.Service {
224
224
  switch (arg1.constructor.name) {
225
225
  case "Request":
226
226
  case "NoaRequest":
227
+ case "ODataRequest":
227
228
  const req = arg1;
228
229
  const next = arguments[1];
229
230
 
@@ -239,7 +240,7 @@ class CachingService extends cds.Service {
239
240
  return cachedValue;
240
241
  }
241
242
  const response = await next();
242
- req.cacheOptions.tags = this.resolveTags(req.cacheOptions.tags, response, req.params);
243
+ req.cacheOptions.tags = this.resolveTags(req.cacheOptions.tags, response, { ...req.params, user: req.user.id, tenant: req.tenant, locale: req.locale, hash: this.createKey(req, { template: '{hash}' }) });
243
244
  await this.set(req.cacheKey, response, req.cacheOptions);
244
245
  return response;
245
246
  case "cds.ql":
@@ -259,7 +260,7 @@ class CachingService extends cds.Service {
259
260
  return this.get(query.cacheKey);
260
261
  }
261
262
  const data = await srv.run(query);
262
- options.tags = this.resolveTags(options.tags, data, query.params);
263
+ options.tags = this.resolveTags(options.tags, data, { ...query.params, hash: this.createKey(query, { template: '{hash}' }) });
263
264
  await this.set(query.cacheKey, data, options);
264
265
  return data;
265
266
  } else {
@@ -393,6 +394,36 @@ class CachingService extends cds.Service {
393
394
  return [tag];
394
395
  }
395
396
 
397
+ // Handle template-based tags
398
+ if (config.template) {
399
+ const createHash = (data) => crypto.createHash('md5').update(JSON.stringify(data)).digest('hex');
400
+
401
+ const hashParts = [
402
+ ...(data ? [data] : []),
403
+ ...(params ? [params] : [])
404
+ ];
405
+
406
+ const contextVars = {
407
+ tenant: params.tenant || 'global',
408
+ user: params.user || 'anonymous',
409
+ locale: params.locale || 'en',
410
+ hash: createHash(hashParts)
411
+ };
412
+
413
+ const value = config.template.replace(
414
+ /\{(tenant|user|locale|hash)\}/g,
415
+ (match, variable) => contextVars[variable]
416
+ );
417
+
418
+ const tag = [
419
+ config.prefix,
420
+ value,
421
+ config.suffix
422
+ ].filter(Boolean).join('');
423
+
424
+ return [tag];
425
+ }
426
+
396
427
  // Handle data-based tags
397
428
  if (config.data && dataArray.length) {
398
429
  return dataArray.flatMap(item => {
@@ -459,6 +490,7 @@ class CachingService extends cds.Service {
459
490
  switch (keyOrObject.constructor.name) {
460
491
  case "Request":
461
492
  case "NoaRequest":
493
+ case "ODataRequest":
462
494
 
463
495
  return this.createCacheKey((!options.value && !options.template) ? { template: '{tenant}:{user}:{locale}:{hash}' } : options, {
464
496
  req: keyOrObject,
package/srv/util.js CHANGED
@@ -24,7 +24,7 @@ const bindFunction = async (service, action, isBound = false) => {
24
24
  const cache = await cds.connect.to(action['@cache.service'] || "caching");
25
25
  const data = await cache.run(req, next, {
26
26
  ttl: action['@cache.ttl'],
27
- tags: extractCacheProperties(action, 'tags'),
27
+ tags: action['@cache.tags'],
28
28
  key: extractCacheProperties(action, 'key')
29
29
  });
30
30
  return data;
@@ -37,9 +37,11 @@ const bindEntity = async (service, entity) => {
37
37
  service.on('READ', entity.name, async (req, next) => {
38
38
  const cache = await cds.connect.to(entity['@cache.service'] || "caching");
39
39
 
40
+ console.log(entity);
41
+
40
42
  const data = await cache.run(req, next, {
41
43
  ttl: entity['@cache.ttl'],
42
- tags: extractCacheProperties(entity, 'tags'),
44
+ tags: entity['@cache.tags'],
43
45
  key: extractCacheProperties(entity, 'key')
44
46
  });
45
47
  return data;