cds-caching 1.3.1 → 1.3.2

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 (32) hide show
  1. package/README.md +16 -0
  2. package/app/dashboard/controller/App-dbg.controller.js +14 -14
  3. package/app/dashboard/controller/App-dbg.controller.js.map +1 -1
  4. package/app/dashboard/controller/App.controller.js +1 -1
  5. package/app/dashboard/controller/App.controller.js.map +1 -1
  6. package/app/dashboard/controller/App.controller.ts +14 -14
  7. package/app/dashboard/controller/BaseController-dbg.js +7 -0
  8. package/app/dashboard/controller/BaseController-dbg.js.map +1 -1
  9. package/app/dashboard/controller/BaseController.js +1 -1
  10. package/app/dashboard/controller/BaseController.js.map +1 -1
  11. package/app/dashboard/controller/BaseController.ts +10 -0
  12. package/app/dashboard/controller/Cache-dbg.controller.js +34 -25
  13. package/app/dashboard/controller/Cache-dbg.controller.js.map +1 -1
  14. package/app/dashboard/controller/Cache.controller.js +1 -1
  15. package/app/dashboard/controller/Cache.controller.js.map +1 -1
  16. package/app/dashboard/controller/Cache.controller.ts +36 -25
  17. package/app/dashboard/controller/SingleMetric-dbg.controller.js +56 -50
  18. package/app/dashboard/controller/SingleMetric-dbg.controller.js.map +1 -1
  19. package/app/dashboard/controller/SingleMetric.controller.js +1 -1
  20. package/app/dashboard/controller/SingleMetric.controller.js.map +1 -1
  21. package/app/dashboard/controller/SingleMetric.controller.ts +29 -22
  22. package/app/dashboard/i18n/i18n.properties +235 -0
  23. package/app/dashboard/i18n/i18n_de.properties +271 -3
  24. package/app/dashboard/i18n/i18n_en.properties +295 -2
  25. package/app/dashboard/resources/sap-ui-custom.js +11 -11
  26. package/app/dashboard/resources/sap-ui-custom.js.map +1 -1
  27. package/app/dashboard/view/Cache.view.xml +149 -149
  28. package/app/dashboard/view/Main.view.xml +7 -7
  29. package/app/dashboard/view/SingleMetric.view.xml +30 -30
  30. package/cds-plugin.js +31 -8
  31. package/lib/util.js +28 -2
  32. package/package.json +1 -1
@@ -13,30 +13,30 @@
13
13
  formatter: 'cds/plugin/caching/dashboard/model/formatter'
14
14
  }">
15
15
 
16
- <f:DynamicPage title="cds-caching – Dashboard" headerExpanded="true" headerPinned="true">
16
+ <f:DynamicPage title="{i18n>homePageTitle}" headerExpanded="true" headerPinned="true">
17
17
  <f:title>
18
18
  <f:DynamicPageTitle>
19
19
  <f:heading>
20
- <Title text="cds-caching – Dashboard" />
20
+ <Title text="{i18n>homePageTitle}" />
21
21
  </f:heading>
22
22
  <f:actions>
23
- <Button type="Transparent" icon="sap-icon://refresh" text="Refresh" press="onRefresh" />
23
+ <Button type="Transparent" icon="sap-icon://refresh" text="{i18n>refreshButton}" press="onRefresh" />
24
24
  </f:actions>
25
25
  </f:DynamicPageTitle>
26
26
  </f:title>
27
27
 
28
28
  <f:content>
29
29
 
30
- <Table id="cachesList" headerText="Caching Services" mode="SingleSelectMaster" items="{path: '/Caches'}" itemPress="onCacheSelect" noDataText="No caches available">
30
+ <Table id="cachesList" headerText="{i18n>cachingServicesHeader}" mode="SingleSelectMaster" items="{path: '/Caches'}" itemPress="onCacheSelect" noDataText="{i18n>noCachesAvailable}">
31
31
  <columns>
32
32
  <Column>
33
- <Text text="Name" />
33
+ <Text text="{i18n>colName}" />
34
34
  </Column>
35
35
  <Column>
36
- <Text text="Statistic Tracking Enabled" />
36
+ <Text text="{i18n>colStatisticTrackingEnabled}" />
37
37
  </Column>
38
38
  <Column>
39
- <Text text="Key Tracking Enabled" />
39
+ <Text text="{i18n>colKeyTrackingEnabled}" />
40
40
  </Column>
41
41
  </columns>
42
42
  <items>
@@ -14,19 +14,19 @@
14
14
  formatter: 'cds/plugin/caching/dashboard/model/formatter'
15
15
  }">
16
16
 
17
- <f:DynamicPage title="Metric Details" showFooter="false">
17
+ <f:DynamicPage title="{i18n>metricDetailsPageTitle}" showFooter="false">
18
18
  <f:title>
19
19
  <f:DynamicPageTitle>
20
20
  <f:heading>
21
- <Title text="Metric: {ui>/metricId}" />
21
+ <Title text="{= ${i18n>metricPagePrefix} + ' ' + ${ui>/metricId} }" />
22
22
  </f:heading>
23
23
  <f:actions>
24
- <Button icon="sap-icon://refresh" text="Refresh" type="Transparent" press="onRefresh" />
24
+ <Button icon="sap-icon://refresh" text="{i18n>refreshButton}" type="Transparent" press="onRefresh" />
25
25
  </f:actions>
26
26
  <f:navigationActions>
27
- <OverflowToolbarButton type="Transparent" icon="sap-icon://full-screen" press="handleFullScreen" tooltip="Enter Full Screen Mode" visible="{= ${app>/layout} !== 'EndColumnFullScreen' }"/>
28
- <OverflowToolbarButton type="Transparent" icon="sap-icon://exit-full-screen" press="handleExitFullScreen" tooltip="Exit Full Screen Mode" visible="{= ${app>/layout} === 'EndColumnFullScreen' }"/>
29
- <OverflowToolbarButton type="Transparent" icon="sap-icon://decline" press="handleClose" tooltip="Close end column"/>
27
+ <OverflowToolbarButton type="Transparent" icon="sap-icon://full-screen" press="handleFullScreen" tooltip="{i18n>ttFullScreenEnter}" visible="{= ${app>/layout} !== 'EndColumnFullScreen' }"/>
28
+ <OverflowToolbarButton type="Transparent" icon="sap-icon://exit-full-screen" press="handleExitFullScreen" tooltip="{i18n>ttFullScreenExit}" visible="{= ${app>/layout} === 'EndColumnFullScreen' }"/>
29
+ <OverflowToolbarButton type="Transparent" icon="sap-icon://decline" press="handleClose" tooltip="{i18n>ttCloseEndColumn}"/>
30
30
  </f:navigationActions>
31
31
  </f:DynamicPageTitle>
32
32
  </f:title>
@@ -43,10 +43,10 @@
43
43
  <cssgrid:ResponsiveColumnItemLayoutData columns="24"/>
44
44
  </layoutData>
45
45
 
46
- <Title text="Read-Through Performance" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
46
+ <Title text="{i18n>sectionReadThroughPerformance}" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
47
47
 
48
48
  <f:GridContainer id="metricCacheThroughGrid" columnsChange=".onColumnsChange">
49
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Hit Ratio" subheader="Cache Hit Rate">
49
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>hitRatio}" subheader="{i18n>tileSubCacheHitRate}">
50
50
  <TileContent unit="%">
51
51
  <NumericContent valueColor="{formatter: 'formatter.formatHitRatioWithColor', path: 'ui>/hitRatio'}" value="{ path: 'ui>/hitRatio', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
52
52
  </TileContent>
@@ -55,7 +55,7 @@
55
55
  </layoutData>
56
56
  </GenericTile>
57
57
 
58
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Efficiency" subheader="Performance Factor">
58
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerCacheEfficiency}" subheader="{i18n>tileSubPerformanceFactor}">
59
59
  <TileContent unit="x">
60
60
  <NumericContent valueColor="{formatter: 'formatter.formatCacheEfficiencyWithColor', path: 'ui>/cacheEfficiency'}" value="{path: 'ui>/cacheEfficiency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
61
61
  </TileContent>
@@ -64,7 +64,7 @@
64
64
  </layoutData>
65
65
  </GenericTile>
66
66
 
67
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Throughput" subheader="Requests per Second">
67
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>throughput}" subheader="{i18n>tileSubReqPerSec}">
68
68
  <TileContent unit="req/s">
69
69
  <NumericContent value="{ path: 'ui>/throughput', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" withMargin="false" />
70
70
  </TileContent>
@@ -73,7 +73,7 @@
73
73
  </layoutData>
74
74
  </GenericTile>
75
75
 
76
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Error Rate" subheader="Cache Errors">
76
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerErrorRate}" subheader="{i18n>tileSubCacheErrors}">
77
77
  <TileContent unit="%">
78
78
  <NumericContent valueColor="{formatter: 'formatter.formatErrorRateWithColor', path: 'ui>/errorRate'}" value="{ path: 'ui>/errorRate', formatter: 'formatter.formatPercentageValue' }" />
79
79
  </TileContent>
@@ -82,7 +82,7 @@
82
82
  </layoutData>
83
83
  </GenericTile>
84
84
 
85
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Total Requests" subheader="Cache Operations">
85
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>totalRequests}" subheader="{i18n>tileSubCacheOperations}">
86
86
  <TileContent unit="req">
87
87
  <NumericContent value="{= ${ui>/hits} + ${ui>/misses}}" withMargin="false" />
88
88
  </TileContent>
@@ -91,7 +91,7 @@
91
91
  </layoutData>
92
92
  </GenericTile>
93
93
 
94
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Hits" subheader="Successful Retrievals">
94
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerCacheHits}" subheader="{i18n>tileSubSuccessfulRetrievals}">
95
95
  <TileContent unit="hits">
96
96
  <NumericContent value="{ui>/hits}" withMargin="false" />
97
97
  </TileContent>
@@ -100,7 +100,7 @@
100
100
  </layoutData>
101
101
  </GenericTile>
102
102
 
103
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Misses" subheader="Failed Retrievals">
103
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerCacheMisses}" subheader="{i18n>tileSubFailedRetrievals}">
104
104
  <TileContent unit="misses">
105
105
  <NumericContent value="{ui>/misses}" withMargin="false" />
106
106
  </TileContent>
@@ -110,7 +110,7 @@
110
110
  </GenericTile>
111
111
 
112
112
 
113
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Cache Errors" subheader="Error Count">
113
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerCacheErrors}" subheader="{i18n>tileSubErrorCount}">
114
114
  <TileContent unit="errors">
115
115
  <NumericContent value="{ui>/errors}" withMargin="false" />
116
116
  </TileContent>
@@ -120,7 +120,7 @@
120
120
  </GenericTile>
121
121
 
122
122
 
123
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Hit Latency" subheader="Cache Hit Response">
123
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerHitLatency}" subheader="{i18n>tileSubCacheHitResponse}">
124
124
  <TileContent unit="ms">
125
125
  <NumericContent valueColor="{formatter: 'formatter.formatLatencyWithColor', path: 'ui>/avgHitLatency'}" value="{ path: 'ui>/avgHitLatency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
126
126
  </TileContent>
@@ -129,7 +129,7 @@
129
129
  </layoutData>
130
130
  </GenericTile>
131
131
 
132
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Miss Latency" subheader="Cache Miss Response">
132
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerMissLatency}" subheader="{i18n>tileSubCacheMissResponse}">
133
133
  <TileContent unit="ms">
134
134
  <NumericContent valueColor="{formatter: 'formatter.formatLatencyWithColor', path: 'ui>/avgMissLatency'}" value="{ path: 'ui>/avgMissLatency', type: 'sap.ui.model.type.Float', formatOptions: {style: 'short', decimals: 2}}" />
135
135
  </TileContent>
@@ -141,7 +141,7 @@
141
141
  <!-- Read-Through Distribution Chart -->
142
142
  <f:Card class="sapUiTinyMargin">
143
143
  <f:header>
144
- <card:Header title="Read-Through Operations" subtitle="Hit/Miss/Error Distribution" />
144
+ <card:Header title="{i18n>cardReadThroughOps}" subtitle="{i18n>cardReadThroughOpsSub}" />
145
145
  </f:header>
146
146
  <f:content>
147
147
  <l:VerticalLayout width="100%">
@@ -175,7 +175,7 @@
175
175
  <!-- Latency Chart -->
176
176
  <f:Card class="sapUiTinyMargin">
177
177
  <f:header>
178
- <card:Header title="Latency Comparison" subtitle="Read-Through Latency Metrics" />
178
+ <card:Header title="{i18n>cardLatencyCompare}" subtitle="{i18n>cardLatencyCompareSub}" />
179
179
  </f:header>
180
180
  <f:content>
181
181
  <l:VerticalLayout width="100%">
@@ -214,10 +214,10 @@
214
214
  <cssgrid:ResponsiveColumnItemLayoutData columns="24"/>
215
215
  </layoutData>
216
216
 
217
- <Title text="Native Function Performance" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
217
+ <Title text="{i18n>sectionNativeFunctionPerformance}" titleStyle="H5" class="sapUiMediumMarginTop sapUiTinyMarginBottom" />
218
218
 
219
219
  <f:GridContainer id="metricNativeGrid" columnsChange=".onColumnsChange">
220
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Throughput" subheader="Operations per Second">
220
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeThroughput}" subheader="{i18n>tileSubNativeOpsPerSec}">
221
221
  <TileContent unit="ops/s">
222
222
  <NumericContent value="{ path: 'ui>/nativeThroughput', formatter: 'formatter.formatNumberValue' }" withMargin="false" />
223
223
  </TileContent>
@@ -226,7 +226,7 @@
226
226
  </layoutData>
227
227
  </GenericTile>
228
228
 
229
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Error Rate" subheader="Native Function Errors">
229
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeErrorRate}" subheader="{i18n>tileSubCacheErrors}">
230
230
  <TileContent unit="%">
231
231
  <NumericContent valueColor="{formatter: 'formatter.formatErrorRateWithColor', path: 'ui>/nativeErrorRate'}" value="{ path: 'ui>/nativeErrorRate', formatter: 'formatter.formatPercentageValue' }" />
232
232
  </TileContent>
@@ -235,7 +235,7 @@
235
235
  </layoutData>
236
236
  </GenericTile>
237
237
 
238
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Sets" subheader="Write Operations">
238
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeSets}" subheader="{i18n>tileSubWriteOps}">
239
239
  <TileContent unit="sets">
240
240
  <NumericContent value="{ui>/nativeSets}" withMargin="false" />
241
241
  </TileContent>
@@ -244,7 +244,7 @@
244
244
  </layoutData>
245
245
  </GenericTile>
246
246
 
247
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Gets" subheader="Read Operations">
247
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeGets}" subheader="{i18n>tileSubReadOps}">
248
248
  <TileContent unit="gets">
249
249
  <NumericContent value="{ui>/nativeGets}" withMargin="false" />
250
250
  </TileContent>
@@ -253,7 +253,7 @@
253
253
  </layoutData>
254
254
  </GenericTile>
255
255
 
256
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Deletes" subheader="Delete Operations">
256
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeDeletes}" subheader="{i18n>tileSubDeleteOps}">
257
257
  <TileContent unit="deletes">
258
258
  <NumericContent value="{ui>/nativeDeletes}" withMargin="false" />
259
259
  </TileContent>
@@ -262,7 +262,7 @@
262
262
  </layoutData>
263
263
  </GenericTile>
264
264
 
265
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Clears" subheader="Clear Operations">
265
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeClears}" subheader="{i18n>tileSubClearOps}">
266
266
  <TileContent unit="clears">
267
267
  <NumericContent value="{ui>/nativeClears}" withMargin="false" />
268
268
  </TileContent>
@@ -271,7 +271,7 @@
271
271
  </layoutData>
272
272
  </GenericTile>
273
273
 
274
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Delete By Tags" subheader="Tag-based Deletes">
274
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerDeleteByTags}" subheader="{i18n>tileSubTagDeletes}">
275
275
  <TileContent unit="deletes">
276
276
  <NumericContent value="{ui>/nativeDeleteByTags}" withMargin="false" />
277
277
  </TileContent>
@@ -280,7 +280,7 @@
280
280
  </layoutData>
281
281
  </GenericTile>
282
282
 
283
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Native Errors" subheader="Error Count">
283
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerNativeErrors}" subheader="{i18n>tileSubErrorCount}">
284
284
  <TileContent unit="errors">
285
285
  <NumericContent value="{ui>/nativeErrors}" withMargin="false" />
286
286
  </TileContent>
@@ -289,7 +289,7 @@
289
289
  </layoutData>
290
290
  </GenericTile>
291
291
 
292
- <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Total Native Ops" subheader="All Operations">
292
+ <GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="{i18n>headerTotalNativeOps}" subheader="{i18n>tileSubAllOperations}">
293
293
  <TileContent unit="ops">
294
294
  <NumericContent value="{ui>/totalNativeOperations}" withMargin="false" />
295
295
  </TileContent>
@@ -300,7 +300,7 @@
300
300
  <!-- Native Function Distribution Chart -->
301
301
  <f:Card class="sapUiTinyMargin">
302
302
  <f:header>
303
- <card:Header title="Native Function Operations" subtitle="Hit/Miss/Error Distribution" />
303
+ <card:Header title="{i18n>cardNativeFunctionOps}" subtitle="{i18n>cardReadThroughOpsSub}" />
304
304
  </f:header>
305
305
  <f:content>
306
306
  <l:VerticalLayout width="100%">
package/cds-plugin.js CHANGED
@@ -14,8 +14,6 @@ if (cachingConfigs.some(c => c.statistics)) {
14
14
  cds.env.roots.push(path.join(__dirname, 'db', 'statistics'));
15
15
  }
16
16
 
17
- cds.add?.register?.('caching-dashboard', require('./lib/add'))
18
-
19
17
  cds.on('served', scanCachingAnnotations)
20
18
  const LOG = cds.log("cds-caching");
21
19
 
@@ -27,14 +25,20 @@ cds.build?.register?.('cds-caching', class CachingBuildPlugin extends cds.build.
27
25
  clean() { }
28
26
 
29
27
  static hasTask() {
28
+ cds.log('cds-caching').info('hasTask', cds.env.requires);
30
29
  const requires = cds.env.requires || {};
30
+ const dbKind = requires.db?.kind || '';
31
+ const isHanaDB = dbKind === 'hana' || dbKind === 'sql';
31
32
  return Object.values(requires).some(
32
- r => r.impl === 'cds-caching' && r.store === 'hana'
33
+ r => r.impl === 'cds-caching' && (r.store === 'hana' || (r.store === 'cds' && isHanaDB))
33
34
  );
34
35
  }
35
36
 
36
37
  async build() {
37
38
  const requires = cds.env.requires || {};
39
+ const compileOpts = { ...this.options(), sql_mapping: cds.env.sql.names };
40
+ let wroteCacheStore = false;
41
+
38
42
  for (const [, config] of Object.entries(requires)) {
39
43
  if (config.impl === 'cds-caching' && config.store === 'hana') {
40
44
  const table = config.credentials?.table || 'KEYV';
@@ -48,12 +52,31 @@ cds.build?.register?.('cds-caching', class CachingBuildPlugin extends cds.build.
48
52
  await this.write(content).to(`src/gen/${table}.hdbtable`);
49
53
  LOG.info('Building cds-caching hana table', { table, keySize });
50
54
  }
51
- // For store: 'cds' with HANA, the CacheStore entity from db/cache-store.cds
52
- // is loaded via cds.env.roots and deployed automatically by the HDI deployer —
53
- // no manual .hdbtable needed. For SQLite/Postgres, CAP's cds deploy handles
54
- // table creation from the CDS model.
55
+
56
+ if (config.store === 'cds' && !wroteCacheStore) {
57
+ await this._buildCacheStoreHdbtables(compileOpts);
58
+ wroteCacheStore = true;
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * The HANA build task compiles only the app `db/` folder, so `plugin.cds_caching.CacheStore`
65
+ * from env.roots is not part of that CSN. Emit matching .hdbtable files here.
66
+ * Non-HANA DBs still get the table via normal CDS deploy / DDL.
67
+ */
68
+ async _buildCacheStoreHdbtables(compileOpts) {
69
+ const modelPath = path.join(__dirname, 'db', 'cache-store');
70
+ const model = await cds.load(modelPath, { ...compileOpts, cwd: cds.root });
71
+ const artifacts = cds.compile.to.hdbtable(model, compileOpts);
72
+ for (const [content, key] of artifacts) {
73
+ const file = key.file || `${key.name}${key.suffix || ''}`;
74
+ await this.write(content).to(path.join('src/gen', file));
55
75
  }
76
+ LOG.info('Built cds-caching CacheStore HANA artifacts from cache-store model');
56
77
  }
57
78
  });
58
79
 
59
- module.exports = cds.service.impl(CachingService)
80
+ // CAP resolves `impl: 'cds-caching'` to this file; the main export must be the service class
81
+ // so `cds.connect.to('<cache>')` returns CachingService (e.g. addCachableFunction).
82
+ module.exports = CachingService
package/lib/util.js CHANGED
@@ -85,17 +85,42 @@ const bindFunction = async (service, action, isBound = false) => {
85
85
  })
86
86
  }
87
87
 
88
+ const ENTITY_TAG_PREFIX = 'entity:';
89
+
90
+ const getEntityTag = (entity) => `${ENTITY_TAG_PREFIX}${entity.name}`;
91
+
88
92
  const bindEntity = async (service, entity) => {
93
+ const invalidateOnWrite = entity['@cache.invalidateOnWrite'];
94
+ const entityTag = invalidateOnWrite ? getEntityTag(entity) : null;
95
+
89
96
  service.prepend(function () {
90
97
  service.on('READ', entity.name, async (req, next) => {
91
98
  const cache = await cds.connect.to(entity['@cache.service'] || "caching");
99
+
100
+ const tags = [...(entity['@cache.tags'] || [])];
101
+ if (entityTag) tags.push({ value: entityTag });
102
+
92
103
  const { result, cacheKey } = await cache.rt.run(req, next, {
93
104
  ttl: entity['@cache.ttl'],
94
- tags: entity['@cache.tags'],
105
+ tags,
95
106
  key: extractCacheProperties(entity, 'key')
96
107
  });
97
108
  return result;
98
109
  })
110
+
111
+ if (invalidateOnWrite) {
112
+ const cudHandler = async (data, req) => {
113
+ try {
114
+ const cache = await cds.connect.to(entity['@cache.service'] || "caching");
115
+ await cache.deleteByTag(entityTag);
116
+ } catch (error) {
117
+ cds.log('cds-caching').warn(`Cache invalidation failed for ${entity.name}:`, error);
118
+ }
119
+ };
120
+ service.after('CREATE', entity.name, cudHandler);
121
+ service.after('UPDATE', entity.name, cudHandler);
122
+ service.after('DELETE', entity.name, cudHandler);
123
+ }
99
124
  })
100
125
  }
101
126
 
@@ -183,7 +208,8 @@ const scanCachingAnnotations = async (srvs) => {
183
208
  cacheConfig.entities[entity.name] = {
184
209
  ttl: entity['@cache.ttl'],
185
210
  tags: entity['@cache.tags'],
186
- key: extractCacheProperties(entity, 'key')
211
+ key: extractCacheProperties(entity, 'key'),
212
+ invalidateOnWrite: entity['@cache.invalidateOnWrite'] || false
187
213
  };
188
214
  }
189
215
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cds-caching",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "A caching plugin for SAP CAP applications",
5
5
  "repository": {
6
6
  "type": "git",