diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index c676ffcb0..6df7f285b 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -23,9 +23,9 @@ jobs:
timeout-minutes: 30
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Set up JDK 25
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v6
with:
java-version: 25
architecture: x64
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
index 7d22c66f5..f14d3ec14 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -16,15 +16,15 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
- name: Setup pnpm
- uses: pnpm/action-setup@v4
+ uses: pnpm/action-setup@v6
with:
run_install: false
- name: Setup Node.js
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v7
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
index 2bff8bd6f..8a564c360 100644
--- a/.github/workflows/publish-snapshot.yml
+++ b/.github/workflows/publish-snapshot.yml
@@ -28,17 +28,17 @@ jobs:
timeout-minutes: 30
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Set up JDK 25
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v6
with:
java-version: 25
architecture: x64
distribution: temurin
cache: maven
server-id: github
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
+ server-username-env-var: MAVEN_USERNAME
+ server-password-env-var: MAVEN_PASSWORD
- name: Verify and deploy SNAPSHOT
# Se invoca deploy:deploy directo porque central-publishing-maven-plugin (extensions=true)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cacb3f5ed..6737a1fc4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -37,17 +37,17 @@ jobs:
timeout-minutes: 45
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Set up JDK 25
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v6
with:
java-version: 25
architecture: x64
distribution: temurin
cache: maven
server-id: github
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
+ server-username-env-var: MAVEN_USERNAME
+ server-password-env-var: MAVEN_PASSWORD
- name: Check the version
# A wrong version would publish an immutable artifact (and spend Central quota), so fail before building.
@@ -86,17 +86,17 @@ jobs:
timeout-minutes: 45
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v7
- name: Set up JDK 25
- uses: actions/setup-java@v4
+ uses: actions/setup-java@v6
with:
java-version: 25
architecture: x64
distribution: temurin
cache: maven
server-id: central
- server-username: MAVEN_USERNAME # env variable for username in deploy
- server-password: MAVEN_PASSWORD # env variable for token in deploy
+ server-username-env-var: MAVEN_USERNAME # env variable for username in deploy
+ server-password-env-var: MAVEN_PASSWORD # env variable for token in deploy
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
- name: Sign and deploy to Maven Central
diff --git a/docs/backend/ARCHITECTURE.md b/docs/backend/ARCHITECTURE.md
index baffb0e97..a863094f3 100644
--- a/docs/backend/ARCHITECTURE.md
+++ b/docs/backend/ARCHITECTURE.md
@@ -403,6 +403,24 @@ Rules:
browser on its own.
- Unauthenticated requests get a JSON 401 (not a redirect to `/login`), so `fetch` callers can handle them.
+### Caching the metadata endpoints
+
+View descriptors, entity metadata and navigation (`GET /apps/api/metadata/views/{id}`, `/entities`, `/entities/{id}`,
+`/entities/{id}/views[/{view}]`, `/entities/by-path`, `/navigation`) are practically static, but not always: texts depend
+on the request locale (`Messages.getDefaultLocale()`, which an application resolves per request through its
+`LocaleProvider`), and an application may build descriptors dynamically or vary them per user. The framework therefore
+adds nothing by default and leaves the decision to the application through `MetadataHttpCachePolicy`:
+
+- **Default:** no cache headers, exactly as before.
+- **`dynamia.app.metadata.http-cache=etag`:** the included `ContentEtagMetadataHttpCachePolicy`. A strong ETag from the
+ content and the locale, `Cache-Control: private, no-cache` and `Vary: Accept-Language`. The client revalidates on every
+ use and gets a `304` with no body when nothing changed, and never a stale descriptor after a deployment or a hot-reload.
+- **Your own policy:** register a `MetadataHttpCachePolicy` bean (for example an ETag from the application version, or a
+ `Vary` that includes the user). It replaces the included one.
+
+Only the read-only endpoints above take part; actions, entity data (`/entities/ref/**`) and executions are never cached,
+and neither are `/apps/api/dashboard` or `/apps/api/reports`.
+
---
## Dependency Injection & Spring Integration
diff --git a/examples/demo-zk-books/pom.xml b/examples/demo-zk-books/pom.xml
index 3e4eafdae..bfb849738 100644
--- a/examples/demo-zk-books/pom.xml
+++ b/examples/demo-zk-books/pom.xml
@@ -47,7 +47,7 @@
${maven.build.timestamp}yyyyMMdd
- 26.10.1
+ 26.10.2
diff --git a/examples/dynamia-movies/README.md b/examples/dynamia-movies/README.md
index c13769197..f5f3a5c35 100644
--- a/examples/dynamia-movies/README.md
+++ b/examples/dynamia-movies/README.md
@@ -2,7 +2,7 @@
A movie catalog in the spirit of IMDb, built to try **everything new in DynamiaTools** in one project that was
generated with the CLI (`dynamia new`, backend Java + frontend Vue) and runs against the **SNAPSHOT** of this
-repository (`26.10.1`).
+repository (`26.10.2`).
| Folder | What it is |
|---|---|
diff --git a/examples/dynamia-movies/backend/README.md b/examples/dynamia-movies/backend/README.md
index 7c560a6c7..ae3da65b2 100644
--- a/examples/dynamia-movies/backend/README.md
+++ b/examples/dynamia-movies/backend/README.md
@@ -1,6 +1,6 @@
# Dynamia Movies — backend
-Spring Boot 4 + DynamiaTools `26.10.1`. See the [project README](../README.md) for the whole picture.
+Spring Boot 4 + DynamiaTools `26.10.2`. See the [project README](../README.md) for the whole picture.
```bash
./mvnw spring-boot:run # http://localhost:8484 (admin / adminadmin)
diff --git a/examples/dynamia-movies/backend/pom.xml b/examples/dynamia-movies/backend/pom.xml
index 35cff80d6..373da50e9 100644
--- a/examples/dynamia-movies/backend/pom.xml
+++ b/examples/dynamia-movies/backend/pom.xml
@@ -10,12 +10,12 @@
tools.dynamia.demosmovies
- 26.10.1
+ 26.10.2dynamia-moviesMovie catalog demo: Dynamia Tools backoffice + public Vue site25
- 26.10.1
+ 26.10.2
diff --git a/examples/dynamia-movies/frontend/package.json b/examples/dynamia-movies/frontend/package.json
index 5e00a4d82..fa4af7980 100644
--- a/examples/dynamia-movies/frontend/package.json
+++ b/examples/dynamia-movies/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "dynamia-movies",
"private": true,
- "version": "26.10.1",
+ "version": "26.10.2",
"description": "Public movie site (Vue 3) for the Dynamia Movies demo. It reads the automatic REST API of DynamiaTools.",
"type": "module",
"scripts": {
diff --git a/extensions/dashboard/packages/dashboard-sdk/package.json b/extensions/dashboard/packages/dashboard-sdk/package.json
index 4c58519de..d739c9fbf 100644
--- a/extensions/dashboard/packages/dashboard-sdk/package.json
+++ b/extensions/dashboard/packages/dashboard-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/dashboard-sdk",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "TypeScript/JavaScript client SDK for the Dynamia Dashboard extension REST API",
"keywords": [
diff --git a/extensions/dashboard/packages/dashboard-vue/package.json b/extensions/dashboard/packages/dashboard-vue/package.json
index c7319619b..c9c7617fc 100644
--- a/extensions/dashboard/packages/dashboard-vue/package.json
+++ b/extensions/dashboard/packages/dashboard-vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/dashboard-vue",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "Vue 3 components and composables to render Dynamia dashboards without ZK",
"keywords": [
diff --git a/extensions/dashboard/packages/dashboard-vue/src/components/ViewerWidget.vue b/extensions/dashboard/packages/dashboard-vue/src/components/ViewerWidget.vue
index 8287757c2..7427904d5 100644
--- a/extensions/dashboard/packages/dashboard-vue/src/components/ViewerWidget.vue
+++ b/extensions/dashboard/packages/dashboard-vue/src/components/ViewerWidget.vue
@@ -1,16 +1,16 @@
diff --git a/extensions/dashboard/packages/dashboard-vue/test/components.test.ts b/extensions/dashboard/packages/dashboard-vue/test/components.test.ts
index e2c8f9637..4151d2e38 100644
--- a/extensions/dashboard/packages/dashboard-vue/test/components.test.ts
+++ b/extensions/dashboard/packages/dashboard-vue/test/components.test.ts
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
-import { defineComponent } from 'vue';
+import { defineComponent, h } from 'vue';
import { flushPromises, mount } from '@vue/test-utils';
-import { DynamiaDashboard, KpiWidget, WidgetRendererRegistry, registerBuiltinWidgets } from '../src/index.js';
+import { DynamiaDashboard, KpiWidget, ViewerWidget, WidgetRendererRegistry, registerBuiltinWidgets } from '../src/index.js';
import { dashboardDescriptor, makeClient, widgetResponse } from './helpers.js';
const chartCtor = vi.fn();
@@ -139,3 +139,23 @@ describe('built-in widgets', () => {
expect(chartDestroy).toHaveBeenCalled();
});
});
+
+describe('ViewerWidget', () => {
+ const Viewer = defineComponent({ props: ['descriptorId', 'viewType', 'source', 'value', 'readOnly'], setup: () => () => h('div', { 'data-testid': 'viewer' }) });
+ const mountWidget = (data: unknown) => mount(ViewerWidget, { props: { data: data as never }, global: { stubs: { DynamiaViewer: Viewer } } });
+
+ it('gives a list value to the viewer as its data source', () => {
+ const rows = [{ a: 1 }, { a: 2 }];
+ const props = mountWidget({ descriptorId: 'clientesWidget', value: rows }).findComponent(Viewer).props();
+
+ expect(props).toMatchObject({ descriptorId: 'clientesWidget', source: rows, readOnly: true });
+ expect(props.value).toBeUndefined();
+ });
+
+ it('gives any other value to the viewer as its value', () => {
+ const props = mountWidget({ viewType: 'form', value: { name: 'Ana' } }).findComponent(Viewer).props();
+
+ expect(props).toMatchObject({ viewType: 'form', value: { name: 'Ana' } });
+ expect(props.source).toBeUndefined();
+ });
+});
diff --git a/extensions/dashboard/sources/core/pom.xml b/extensions/dashboard/sources/core/pom.xml
index 2e2add073..488c1cbe4 100644
--- a/extensions/dashboard/sources/core/pom.xml
+++ b/extensions/dashboard/sources/core/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.dashboard.parent
- 26.10.1
+ 26.10.2../pom.xml
@@ -36,12 +36,12 @@
tools.dynamiatools.dynamia.viewers
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2
diff --git a/extensions/dashboard/sources/pom.xml b/extensions/dashboard/sources/pom.xml
index e2e124abd..430ffb623 100644
--- a/extensions/dashboard/sources/pom.xml
+++ b/extensions/dashboard/sources/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/dashboard/sources/zk/pom.xml b/extensions/dashboard/sources/zk/pom.xml
index e99fac6e3..f92101580 100644
--- a/extensions/dashboard/sources/zk/pom.xml
+++ b/extensions/dashboard/sources/zk/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.dashboard.parent
- 26.10.1
+ 26.10.2../pom.xml
@@ -38,17 +38,17 @@
tools.dynamia.modulestools.dynamia.modules.dashboard.core
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.api
- 26.10.1
+ 26.10.2
diff --git a/extensions/email-sms/sources/core/pom.xml b/extensions/email-sms/sources/core/pom.xml
index cb0fc074a..847ebedd5 100644
--- a/extensions/email-sms/sources/core/pom.xml
+++ b/extensions/email-sms/sources/core/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modules.email.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2tools.dynamia.modules.email
@@ -50,17 +50,17 @@
tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.crud
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.templates
- 26.10.1
+ 26.10.2org.springframework
diff --git a/extensions/email-sms/sources/pom.xml b/extensions/email-sms/sources/pom.xml
index 623e6b108..d0bfaca5f 100644
--- a/extensions/email-sms/sources/pom.xml
+++ b/extensions/email-sms/sources/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
@@ -85,7 +85,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2
diff --git a/extensions/email-sms/sources/ui/pom.xml b/extensions/email-sms/sources/ui/pom.xml
index f6b7786bf..5737b321d 100644
--- a/extensions/email-sms/sources/ui/pom.xml
+++ b/extensions/email-sms/sources/ui/pom.xml
@@ -22,7 +22,7 @@
tools.dynamia.modules.email.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2DynamiaModules - Email UI
@@ -34,12 +34,12 @@
tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.email
- 26.10.1
+ 26.10.2tools.dynamia.zk.addons
diff --git a/extensions/entity-files/packages/files-sdk/package.json b/extensions/entity-files/packages/files-sdk/package.json
index 8a3763f06..732c44fcc 100644
--- a/extensions/entity-files/packages/files-sdk/package.json
+++ b/extensions/entity-files/packages/files-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/files-sdk",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "TypeScript/JavaScript client SDK for the Dynamia Entity Files extension REST API",
"keywords": [
diff --git a/extensions/entity-files/sources/core/pom.xml b/extensions/entity-files/sources/core/pom.xml
index ffc86c95f..ff3a111d8 100644
--- a/extensions/entity-files/sources/core/pom.xml
+++ b/extensions/entity-files/sources/core/pom.xml
@@ -22,7 +22,7 @@
tools.dynamia.modules.entityfiles.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2DynamiaModules - EntityFiles - Coretools.dynamia.modules.entityfiles
@@ -53,7 +53,7 @@
tools.dynamiatools.dynamia.ui.testing
- 26.10.1
+ 26.10.2test
@@ -65,20 +65,20 @@
tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2jartools.dynamiatools.dynamia.io
- 26.10.1
+ 26.10.2jartools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2jar
diff --git a/extensions/entity-files/sources/pom.xml b/extensions/entity-files/sources/pom.xml
index 4e165233e..37bf1041d 100644
--- a/extensions/entity-files/sources/pom.xml
+++ b/extensions/entity-files/sources/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/entity-files/sources/s3/pom.xml b/extensions/entity-files/sources/s3/pom.xml
index 6eb90dc44..afdf5812f 100644
--- a/extensions/entity-files/sources/s3/pom.xml
+++ b/extensions/entity-files/sources/s3/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.entityfiles.parent
- 26.10.1
+ 26.10.2DynamiaModules - EntityFiles - S3
@@ -49,7 +49,7 @@
tools.dynamia.modulestools.dynamia.modules.entityfiles
- 26.10.1
+ 26.10.2software.amazon.awssdk
diff --git a/extensions/entity-files/sources/ui/pom.xml b/extensions/entity-files/sources/ui/pom.xml
index b0d7c30db..bd0cbd098 100644
--- a/extensions/entity-files/sources/ui/pom.xml
+++ b/extensions/entity-files/sources/ui/pom.xml
@@ -22,7 +22,7 @@
tools.dynamia.modules.entityfiles.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2DynamiaModules - EntityFiles UItools.dynamia.modules.entityfiles.ui
@@ -48,18 +48,18 @@
tools.dynamia.modulestools.dynamia.modules.entityfiles
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2jartools.dynamiatools.dynamia.ui.testing
- 26.10.1
+ 26.10.2test
diff --git a/extensions/file-importer/sources/core/pom.xml b/extensions/file-importer/sources/core/pom.xml
index 51310dce3..56a2feeba 100644
--- a/extensions/file-importer/sources/core/pom.xml
+++ b/extensions/file-importer/sources/core/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modules.importer.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2Dynamia Modules - Importer Coretools.dynamia.modules.importer
@@ -56,7 +56,7 @@
tools.dynamiatools.dynamia.reports
- 26.10.1
+ 26.10.2
diff --git a/extensions/file-importer/sources/pom.xml b/extensions/file-importer/sources/pom.xml
index f7be914eb..7062894f8 100644
--- a/extensions/file-importer/sources/pom.xml
+++ b/extensions/file-importer/sources/pom.xml
@@ -26,7 +26,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/file-importer/sources/ui/pom.xml b/extensions/file-importer/sources/ui/pom.xml
index c6661dca5..d95084fc3 100644
--- a/extensions/file-importer/sources/ui/pom.xml
+++ b/extensions/file-importer/sources/ui/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modules.importer.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2Dynamia Modules - Importer UItools.dynamia.modules.importer.ui
@@ -55,13 +55,13 @@
tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.importer
- 26.10.1
+ 26.10.2
diff --git a/extensions/finances/sources/api/pom.xml b/extensions/finances/sources/api/pom.xml
index 433ee5540..30547be34 100644
--- a/extensions/finances/sources/api/pom.xml
+++ b/extensions/finances/sources/api/pom.xml
@@ -26,7 +26,7 @@
tools.dynamia.modulestools.dynamia.modules.finances.parent
- 26.10.1
+ 26.10.2Dynamia Modules - Finances API
diff --git a/extensions/finances/sources/pom.xml b/extensions/finances/sources/pom.xml
index 3a45d04ba..ba8ddd479 100644
--- a/extensions/finances/sources/pom.xml
+++ b/extensions/finances/sources/pom.xml
@@ -26,7 +26,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/http-functions/packages/http-functions-sdk/package.json b/extensions/http-functions/packages/http-functions-sdk/package.json
index abc7f24cd..eacb43bda 100644
--- a/extensions/http-functions/packages/http-functions-sdk/package.json
+++ b/extensions/http-functions/packages/http-functions-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/http-functions-sdk",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "TypeScript/JavaScript client SDK for the Dynamia HTTP Functions extension REST API",
"keywords": [
diff --git a/extensions/http-functions/sources/core/pom.xml b/extensions/http-functions/sources/core/pom.xml
index 5914e2b89..5fbf549ad 100644
--- a/extensions/http-functions/sources/core/pom.xml
+++ b/extensions/http-functions/sources/core/pom.xml
@@ -4,7 +4,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../../pom.xmlDynamiaModules - Http Functions Core
@@ -20,12 +20,12 @@
tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2
diff --git a/extensions/http-functions/sources/pom.xml b/extensions/http-functions/sources/pom.xml
index 90800f87f..79c4632be 100644
--- a/extensions/http-functions/sources/pom.xml
+++ b/extensions/http-functions/sources/pom.xml
@@ -5,7 +5,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
@@ -66,7 +66,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2
diff --git a/extensions/http-functions/sources/ui/pom.xml b/extensions/http-functions/sources/ui/pom.xml
index 483607195..08ed4ab39 100644
--- a/extensions/http-functions/sources/ui/pom.xml
+++ b/extensions/http-functions/sources/ui/pom.xml
@@ -5,7 +5,7 @@
tools.dynamia.modulestools.dynamia.modules.functions.parent
- 26.10.1
+ 26.10.2DynamiaModules - Http Functions UI
@@ -18,12 +18,12 @@
tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.functions.core
- 26.10.1
+ 26.10.2
diff --git a/extensions/pom.xml b/extensions/pom.xml
index 1b3427ed7..fa36af765 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -6,7 +6,7 @@
tools.dynamiatools.dynamia.parent
- 26.10.1
+ 26.10.2../pom.xml
diff --git a/extensions/reports/packages/reports-sdk/package.json b/extensions/reports/packages/reports-sdk/package.json
index 2de8c1f36..bdbec4e48 100644
--- a/extensions/reports/packages/reports-sdk/package.json
+++ b/extensions/reports/packages/reports-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/reports-sdk",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "TypeScript/JavaScript client SDK for the Dynamia Reports extension REST API",
"keywords": [
diff --git a/extensions/reports/packages/reports-sdk/src/types.ts b/extensions/reports/packages/reports-sdk/src/types.ts
index 03f14c80d..77bba6595 100644
--- a/extensions/reports/packages/reports-sdk/src/types.ts
+++ b/extensions/reports/packages/reports-sdk/src/types.ts
@@ -122,6 +122,10 @@ export interface ReportColumn {
format?: string | null;
width?: string | null;
upperCase: boolean;
+ /** CSS class name(s) the designer chose for every cell of the column, e.g. `green lighten-5`; absent when not set */
+ cellStyle?: string;
+ /** CSS class name(s) the designer chose for the header of the column; absent when not set */
+ columnStyle?: string;
}
/** A filter a report accepts. */
diff --git a/extensions/reports/packages/reports-vue/package.json b/extensions/reports/packages/reports-vue/package.json
index 82208b8ac..94ba85327 100644
--- a/extensions/reports/packages/reports-vue/package.json
+++ b/extensions/reports/packages/reports-vue/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/reports-vue",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "Vue 3 components and composables to list, run, export and design Dynamia reports without ZK",
"keywords": [
diff --git a/extensions/reports/sources/api/pom.xml b/extensions/reports/sources/api/pom.xml
index e1cb0664f..e921f40b0 100644
--- a/extensions/reports/sources/api/pom.xml
+++ b/extensions/reports/sources/api/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.reports.parent
- 26.10.1
+ 26.10.2DynamiaModules - Reports API
diff --git a/extensions/reports/sources/api/src/main/java/tools/dynamia/modules/reports/api/v2/ReportColumn.java b/extensions/reports/sources/api/src/main/java/tools/dynamia/modules/reports/api/v2/ReportColumn.java
index bb2d0ba39..eabe048fb 100644
--- a/extensions/reports/sources/api/src/main/java/tools/dynamia/modules/reports/api/v2/ReportColumn.java
+++ b/extensions/reports/sources/api/src/main/java/tools/dynamia/modules/reports/api/v2/ReportColumn.java
@@ -1,5 +1,7 @@
package tools.dynamia.modules.reports.api.v2;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
/**
* A column of a report result.
*
@@ -10,7 +12,19 @@
* @param format optional display format pattern
* @param width optional width, for example {@code 120px}
* @param upperCase true if the text should be shown in upper case
+ * @param cellStyle optional CSS class name(s) the designer chose for every cell of the column (for example {@code green lighten-5});
+ * omitted from the JSON when not set
+ * @param columnStyle optional CSS class name(s) the designer chose for the header of the column; omitted from the JSON when not set
*/
public record ReportColumn(String name, String label, String dataType, String align, String format, String width,
- boolean upperCase) {
+ boolean upperCase,
+ @JsonInclude(JsonInclude.Include.NON_NULL) String cellStyle,
+ @JsonInclude(JsonInclude.Include.NON_NULL) String columnStyle) {
+
+ /**
+ * A column without designer styles.
+ */
+ public ReportColumn(String name, String label, String dataType, String align, String format, String width, boolean upperCase) {
+ this(name, label, dataType, align, format, width, upperCase, null, null);
+ }
}
diff --git a/extensions/reports/sources/core/pom.xml b/extensions/reports/sources/core/pom.xml
index 78f97f8f6..ce5382aac 100644
--- a/extensions/reports/sources/core/pom.xml
+++ b/extensions/reports/sources/core/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.reports.parent
- 26.10.1
+ 26.10.2DynamiaModules - Reports Core
@@ -50,17 +50,17 @@
tools.dynamia.modulestools.dynamia.modules.reports.api
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2org.springframework
@@ -69,23 +69,23 @@
tools.dynamiatools.dynamia.reports
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.templates
- 26.10.1
+ 26.10.2compiletools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.dashboard.core
- 26.10.1
+ 26.10.2com.github.librepdf
diff --git a/extensions/reports/sources/core/src/main/java/tools/dynamia/modules/reports/core/api/ReportColumns.java b/extensions/reports/sources/core/src/main/java/tools/dynamia/modules/reports/core/api/ReportColumns.java
index f10bfef02..3944e7c79 100644
--- a/extensions/reports/sources/core/src/main/java/tools/dynamia/modules/reports/core/api/ReportColumns.java
+++ b/extensions/reports/sources/core/src/main/java/tools/dynamia/modules/reports/core/api/ReportColumns.java
@@ -80,7 +80,11 @@ private static ReportColumn of(ReportField field) {
DataType type = field.getDataType() != null ? field.getDataType() : DataType.TEXT;
TextAlign align = field.getAlign() != null ? field.getAlign() : TextAlign.LEFT;
return new ReportColumn(field.getName(), field.getLabel(), type.name(), align.name(), field.getFormat(),
- field.getWidth(), field.isUpperCase());
+ field.getWidth(), field.isUpperCase(), blankToNull(field.getCellStyle()), blankToNull(field.getColumnStyle()));
+ }
+
+ private static String blankToNull(String style) {
+ return style == null || style.isBlank() ? null : style.trim();
}
private static DataType infer(String name, ReportData data) {
diff --git a/extensions/reports/sources/core/src/test/java/tools/dynamia/modules/reports/core/api/ReportColumnStylesTest.java b/extensions/reports/sources/core/src/test/java/tools/dynamia/modules/reports/core/api/ReportColumnStylesTest.java
new file mode 100644
index 000000000..b150d8946
--- /dev/null
+++ b/extensions/reports/sources/core/src/test/java/tools/dynamia/modules/reports/core/api/ReportColumnStylesTest.java
@@ -0,0 +1,85 @@
+package tools.dynamia.modules.reports.core.api;
+
+import org.junit.jupiter.api.Test;
+import tools.dynamia.modules.reports.core.domain.Report;
+import tools.dynamia.modules.reports.core.domain.ReportField;
+import tools.jackson.databind.json.JsonMapper;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The styles a designer chooses for a field reach the API columns, so a front end other than ZK can honor them.
+ */
+class ReportColumnStylesTest {
+
+ private static ReportField field(Report report, String name, int order, String cellStyle, String columnStyle) {
+ var field = new ReportField();
+ field.setReport(report);
+ field.setName(name);
+ field.setLabel(name);
+ field.setOrder(order);
+ field.setCellStyle(cellStyle);
+ field.setColumnStyle(columnStyle);
+ report.getFields().add(field);
+ return field;
+ }
+
+ private static Report report() {
+ var report = new Report();
+ report.setName("Styles");
+ report.setAutofields(false);
+ return report;
+ }
+
+ @Test
+ void declaredColumnsCarryTheDesignersStyles() {
+ var report = report();
+ field(report, "total", 1, "green lighten-5", "bold");
+
+ var column = ReportColumns.declared(report).get(0);
+
+ assertEquals("green lighten-5", column.cellStyle());
+ assertEquals("bold", column.columnStyle());
+ }
+
+ @Test
+ void blankStylesAreNotSent() {
+ var report = report();
+ field(report, "a", 1, " ", "");
+ field(report, "b", 2, null, null);
+
+ var columns = ReportColumns.declared(report);
+
+ assertNull(columns.get(0).cellStyle());
+ assertNull(columns.get(0).columnStyle());
+ assertNull(columns.get(1).cellStyle());
+ }
+
+ @Test
+ void theJsonOmitsTheStyleKeysWhenThereAreNoneAndKeepsTheRest() {
+ var report = report();
+ field(report, "a", 1, null, null);
+ field(report, "b", 2, "red lighten-4", null);
+ var mapper = JsonMapper.builder().build();
+
+ var plain = mapper.writeValueAsString(ReportColumns.declared(report).get(0));
+ var styled = mapper.writeValueAsString(ReportColumns.declared(report).get(1));
+
+ assertFalse(plain.contains("cellStyle"));
+ assertFalse(plain.contains("columnStyle"));
+ assertTrue(plain.contains("\"format\":null"), "the existing keys keep being sent as before: " + plain);
+ assertTrue(styled.contains("\"cellStyle\":\"red lighten-4\""));
+ assertFalse(styled.contains("columnStyle"));
+ }
+
+ @Test
+ void theOldConstructorStillWorks() {
+ var column = new tools.dynamia.modules.reports.api.v2.ReportColumn("a", "A", "TEXT", "LEFT", null, null, false);
+
+ assertNull(column.cellStyle());
+ assertNull(column.columnStyle());
+ }
+}
diff --git a/extensions/reports/sources/pom.xml b/extensions/reports/sources/pom.xml
index 18f8072d8..fd76f5fda 100644
--- a/extensions/reports/sources/pom.xml
+++ b/extensions/reports/sources/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/reports/sources/ui/pom.xml b/extensions/reports/sources/ui/pom.xml
index b674f4189..444f3a7d9 100644
--- a/extensions/reports/sources/ui/pom.xml
+++ b/extensions/reports/sources/ui/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.reports.parent
- 26.10.1
+ 26.10.2DynamiaModules - Reports UI
@@ -49,12 +49,12 @@
tools.dynamia.modulestools.dynamia.modules.reports.core
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2io.swagger.core.v3
@@ -65,7 +65,7 @@
tools.dynamiatools.dynamia.ui.testing
- 26.10.1
+ 26.10.2test
diff --git a/extensions/saas/packages/saas-sdk/package.json b/extensions/saas/packages/saas-sdk/package.json
index f0e96dc93..d6bc4f65a 100644
--- a/extensions/saas/packages/saas-sdk/package.json
+++ b/extensions/saas/packages/saas-sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@dynamia-tools/saas-sdk",
- "version": "26.10.1",
+ "version": "26.10.2",
"website": "https://dynamia.tools",
"description": "TypeScript/JavaScript client SDK for the Dynamia SaaS extension REST API",
"keywords": [
diff --git a/extensions/saas/sources/api/pom.xml b/extensions/saas/sources/api/pom.xml
index 6af4e60c1..fa92b380c 100644
--- a/extensions/saas/sources/api/pom.xml
+++ b/extensions/saas/sources/api/pom.xml
@@ -26,7 +26,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.parent
- 26.10.1
+ 26.10.2
@@ -55,7 +55,7 @@
tools.dynamiatools.dynamia.actions
- 26.10.1
+ 26.10.2org.springframework.boot
diff --git a/extensions/saas/sources/core/pom.xml b/extensions/saas/sources/core/pom.xml
index 474aba04c..d1f5ff3d2 100644
--- a/extensions/saas/sources/core/pom.xml
+++ b/extensions/saas/sources/core/pom.xml
@@ -22,7 +22,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.parent
- 26.10.1
+ 26.10.2DynamiaModules - SaaS Core
@@ -49,18 +49,18 @@
tools.dynamia.modulestools.dynamia.modules.saas.api
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.integration
- 26.10.1
+ 26.10.2
@@ -96,12 +96,12 @@
tools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.entityfiles
- 26.10.1
+ 26.10.2org.hibernate.orm
diff --git a/extensions/saas/sources/jpa/pom.xml b/extensions/saas/sources/jpa/pom.xml
index 1d2282dfe..cc01f5698 100644
--- a/extensions/saas/sources/jpa/pom.xml
+++ b/extensions/saas/sources/jpa/pom.xml
@@ -24,7 +24,7 @@
tools.dynamia.modules.saas.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2DynamiaModules - SaaS JPA
@@ -35,12 +35,12 @@
tools.dynamia.modulestools.dynamia.modules.saas.api
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2
diff --git a/extensions/saas/sources/migration/pom.xml b/extensions/saas/sources/migration/pom.xml
index 84d735b81..504b656da 100644
--- a/extensions/saas/sources/migration/pom.xml
+++ b/extensions/saas/sources/migration/pom.xml
@@ -24,7 +24,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.parent
- 26.10.1
+ 26.10.2DynamiaModules - SaaS Migration
@@ -57,42 +57,42 @@
tools.dynamia.modulestools.dynamia.modules.saas
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.api
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.entityfiles
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.integration
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2
diff --git a/extensions/saas/sources/pom.xml b/extensions/saas/sources/pom.xml
index e933d4cc0..bc5a1270a 100644
--- a/extensions/saas/sources/pom.xml
+++ b/extensions/saas/sources/pom.xml
@@ -23,7 +23,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/saas/sources/remote/pom.xml b/extensions/saas/sources/remote/pom.xml
index 612acdbb6..fec8c5da0 100644
--- a/extensions/saas/sources/remote/pom.xml
+++ b/extensions/saas/sources/remote/pom.xml
@@ -25,7 +25,7 @@
tools.dynamia.modules.saas.parenttools.dynamia.modules
- 26.10.1
+ 26.10.2
@@ -38,7 +38,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.jpa
- 26.10.1
+ 26.10.2
diff --git a/extensions/saas/sources/ui/pom.xml b/extensions/saas/sources/ui/pom.xml
index c82088ac4..024950cca 100644
--- a/extensions/saas/sources/ui/pom.xml
+++ b/extensions/saas/sources/ui/pom.xml
@@ -22,7 +22,7 @@
tools.dynamia.modulestools.dynamia.modules.saas.parent
- 26.10.1
+ 26.10.2DynamiaModules - SaaS UItools.dynamia.modules.saas.ui
@@ -54,12 +54,12 @@
tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas
- 26.10.1
+ 26.10.2
@@ -70,12 +70,12 @@
tools.dynamia.modulestools.dynamia.modules.entityfiles.ui
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.ui.testing
- 26.10.1
+ 26.10.2test
diff --git a/extensions/security/sources/core/pom.xml b/extensions/security/sources/core/pom.xml
index c2522e97d..4381ae4c6 100644
--- a/extensions/security/sources/core/pom.xml
+++ b/extensions/security/sources/core/pom.xml
@@ -17,7 +17,7 @@
tools.dynamia.modulestools.dynamia.modules.security.parent
- 26.10.1
+ 26.10.24.0.0
@@ -30,41 +30,41 @@
tools.dynamiatools.dynamia.ui
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.saas.api
- 26.10.1
+ 26.10.2tools.dynamia.modulestools.dynamia.modules.entityfiles
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.integration
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2
diff --git a/extensions/security/sources/pom.xml b/extensions/security/sources/pom.xml
index e9b10a112..eb800cb10 100644
--- a/extensions/security/sources/pom.xml
+++ b/extensions/security/sources/pom.xml
@@ -19,7 +19,7 @@
tools.dynamia.modulestools.dynamia.modules.parent
- 26.10.1
+ 26.10.2../../pom.xml
diff --git a/extensions/security/sources/ui/pom.xml b/extensions/security/sources/ui/pom.xml
index 0e446c9bd..259fbe02b 100644
--- a/extensions/security/sources/ui/pom.xml
+++ b/extensions/security/sources/ui/pom.xml
@@ -17,7 +17,7 @@
tools.dynamia.modulestools.dynamia.modules.security.parent
- 26.10.1
+ 26.10.2DynamiaModules - Security UI
@@ -44,18 +44,18 @@
tools.dynamia.modulestools.dynamia.modules.security
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.zk
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.app
- 26.10.1
+ 26.10.2
diff --git a/platform/app/pom.xml b/platform/app/pom.xml
index 5d0770b90..da70f9e29 100644
--- a/platform/app/pom.xml
+++ b/platform/app/pom.xml
@@ -23,7 +23,7 @@
tools.dynamiatools.dynamia.parent
- 26.10.1
+ 26.10.2../../pom.xml
@@ -82,58 +82,58 @@
tools.dynamiatools.dynamia.actions
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.commons
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.crud
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.domain
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.integration
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.io
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.navigation
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.reports
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.templates
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.viewers
- 26.10.1
+ 26.10.2tools.dynamiatools.dynamia.web
- 26.10.1
+ 26.10.2
@@ -213,7 +213,7 @@
tools.dynamiatools.dynamia.domain.jpa
- 26.10.1
+ 26.10.2test
diff --git a/platform/app/src/main/java/tools/dynamia/app/controllers/cache/ContentEtagMetadataHttpCachePolicy.java b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/ContentEtagMetadataHttpCachePolicy.java
new file mode 100644
index 000000000..309802ece
--- /dev/null
+++ b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/ContentEtagMetadataHttpCachePolicy.java
@@ -0,0 +1,36 @@
+package tools.dynamia.app.controllers.cache;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HexFormat;
+import java.util.Locale;
+
+/**
+ * Included policy, enabled with {@code dynamia.app.metadata.http-cache=etag}: a strong ETag derived from the content
+ * and the locale, {@code Cache-Control: private, no-cache} and {@code Vary: Accept-Language}.
+ *
+ * {@code no-cache} makes the client revalidate on every use, which costs a {@code 304} with no body instead of the
+ * whole descriptor, and never serves a stale one after a deployment or a descriptor hot-reload: the ETag is the hash of
+ * what would be sent, so it changes with it.
+ */
+public class ContentEtagMetadataHttpCachePolicy implements MetadataHttpCachePolicy {
+
+ @Override
+ public MetadataCacheDirectives directives(String path, Locale locale, byte[] content) {
+ MessageDigest digest = sha256();
+ digest.update(locale.toLanguageTag().getBytes(StandardCharsets.UTF_8));
+ digest.update((byte) 0);
+ digest.update(content);
+ String eTag = "\"" + HexFormat.of().formatHex(digest.digest(), 0, 16) + "\"";
+ return new MetadataCacheDirectives(eTag, "private, no-cache", "Accept-Language");
+ }
+
+ private static MessageDigest sha256() {
+ try {
+ return MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+}
diff --git a/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataCacheDirectives.java b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataCacheDirectives.java
new file mode 100644
index 000000000..072aea6b1
--- /dev/null
+++ b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataCacheDirectives.java
@@ -0,0 +1,12 @@
+package tools.dynamia.app.controllers.cache;
+
+/**
+ * HTTP cache headers for one metadata response.
+ *
+ * @param eTag the validator, quoted ({@code "abc"}); when present a matching {@code If-None-Match} is answered with
+ * {@code 304 Not Modified} and no body. May be {@code null}.
+ * @param cacheControl the {@code Cache-Control} value, or {@code null} to not set it
+ * @param vary the {@code Vary} header names, comma separated, or {@code null} to not set it
+ */
+public record MetadataCacheDirectives(String eTag, String cacheControl, String vary) {
+}
diff --git a/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataHttpCacheAdvice.java b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataHttpCacheAdvice.java
new file mode 100644
index 000000000..39e6a6731
--- /dev/null
+++ b/platform/app/src/main/java/tools/dynamia/app/controllers/cache/MetadataHttpCacheAdvice.java
@@ -0,0 +1,102 @@
+package tools.dynamia.app.controllers.cache;
+
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.env.Environment;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.http.server.ServletServerHttpResponse;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.context.request.ServletWebRequest;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+import tools.dynamia.app.controllers.ApplicationMetadataController;
+import tools.dynamia.commons.Messages;
+import tools.dynamia.commons.StringPojoParser;
+import tools.jackson.databind.json.JsonMapper;
+
+import java.util.List;
+
+/**
+ * Applies the {@link MetadataHttpCachePolicy} to the read-only metadata endpoints of
+ * {@link ApplicationMetadataController}: view descriptors, entity metadata and navigation. Nothing else is touched
+ * (actions, entity data and executions are never cached).
+ *
+ * The policy is the application's bean if there is one; otherwise {@link ContentEtagMetadataHttpCachePolicy} when
+ * {@code dynamia.app.metadata.http-cache=etag}; otherwise none, and responses are exactly as before.
+ */
+@RestControllerAdvice(assignableTypes = ApplicationMetadataController.class)
+public class MetadataHttpCacheAdvice implements ResponseBodyAdvice