From 04222465784ab6e6c4445f2c59168f50a964343f Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Sun, 4 Oct 2026 20:37:23 +0200 Subject: [PATCH] feat(map): land shared map links on the stack Point backend, viewer, and admin at the dev commits that shorten a map URL and restore mapState. Field-list tests drop the seeded MIA parent so they check the attribute table. --- CHANGELOG.md | 6 +++ README.md | 17 +++----- back/backend/sitmun-backend-core | 2 +- e2e/viewer/gfi-field-list.spec.ts | 12 +++++- e2e/viewer/helpers/mia.ts | 16 +++++--- front/admin/sitmun-admin-app | 2 +- front/viewer/sitmun-viewer-app | 2 +- profiles/development-lvh.env | 66 +++++++++++++++++++++++++++++++ 8 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 profiles/development-lvh.env diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e0bfb..f6b54a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), #### Backend Core +- **Map**: `POST /api/config/client/short-url` shortens a viewer map URL when the caller can open that application and territory. A public visitor with no cookie is allowed. `sitmun.short-url.provider` defaults to `none`. A provider I/O failure returns `Short URL provider failed` ([sitmun-backend-core#272](https://github.com/sitmun/sitmun-backend-core/issues/272)). - **Profile**: Client profile layers publish ordered `featureInfoFields` for `INFO` cartography parameters. The list is omitted when empty. Labels come from `STM_TRANSLATION` on `CartographyParameter.value` ([sitmun-backend-core#268](https://github.com/sitmun/sitmun-backend-core/pull/268), [#77](https://github.com/sitmun/sitmun-application-stack/issues/77)). #### Admin Application @@ -18,9 +19,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), #### Viewer Application +- **Map**: A share link keeps the current map route and stores the view in `mapState`. Opening that link restores the extent, a non-default background, catalog layers that were turned on, reordered, or changed (including their opacity, sublayers, stacking order, and the selected layer of a radio group), drawings, an imported file, a shared feature, and the 3D camera. A missing background, layer, drawing, or file does not stop the rest of the restore. The browser does not call tinyurl.com. A failed shorten shows a map-language message ([sitmun-viewer-app#189](https://github.com/sitmun/sitmun-viewer-app/issues/189)). - **Map**: A non-empty field list replaces the full GetFeatureInfo property table, in order, with the translated label. `T`, `N`, `F`, `U`, `P`, and `I` follow the map locale. `AUTO` keeps API-SITNA detection. `feature.data` is not rewritten ([sitmun-viewer-app#179](https://github.com/sitmun/sitmun-viewer-app/pull/179), [#77](https://github.com/sitmun/sitmun-application-stack/issues/77)). - **Map**: The field list follows the profile layer id, including a WMS group child. Identify highlights do not persist. The identify popup is solid white after a drag. The MIA report docks to the map and closes when the next identify has no MIA layer ([sitmun-viewer-app#187](https://github.com/sitmun/sitmun-viewer-app/pull/187)). +#### Stack + +- **Development**: `profiles/development-lvh.env` announces the stack as `http://lvh.me:9000`. The air-gapped default stays `localhost`. + #### Profile-level - **Liquibase**: Development changelog 78 sets a description, a metadata URL, and a dataset URL on folder Adreces (tree node 12339) in Visor IDEBarcelona. Gestió municipal (node 12338) stays empty ([sitmun-viewer-app#173](https://github.com/sitmun/sitmun-viewer-app/issues/173)). diff --git a/README.md b/README.md index e3088b9..fff4615 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,9 @@ The stack has four main components: # For Oracle cp profiles/oracle.env .env + # Local named host (lvh.me resolves to 127.0.0.1). Open http://lvh.me:9000 + cp profiles/development-lvh.env .env + # Edit environment variables for production nano .env ``` @@ -478,15 +481,7 @@ Admin, viewer, and normal WMS/WFS proxy flows work without it. Root Playwright m #### Frontend Configuration -```typescript -// Environment configuration for Angular apps -export const environment = { - production: boolean, - apiBaseURL: string, - logLevel: LogLevel, - hashLocationStrategy: boolean, -}; -``` +Admin `environment.ts` uses `apiBaseURL`. Viewer `environment.ts` uses `apiUrl` and `hashLocationStrategy`. Viewer `hashLocationStrategy` is `false` in `environment.ts` and `true` in `environment.prod.ts`. The production configuration is the only Docker build that compiles `environment.prod.ts`. #### Backend Configuration @@ -684,7 +679,7 @@ The frontend applications (Admin and Viewer) support three build configurations | Configuration | Use Case | API URL | Source Maps | Optimization | | -------------- | -------------------- | ------------------------ | ----------- | ------------- | | `development` | Local `ng serve` | `localhost:9000/backend` | Yes | No | -| `docker-dev` | Docker debugging | Template-based | Yes | No | +| `docker-dev` | Docker debugging | `localhost:9000/backend` (`environment.ts`) | Yes | No | | `production` | Docker production | Template-based | No | Yes | #### Local Development @@ -725,7 +720,7 @@ For debugging issues in a Docker environment, use the `docker-dev` configuration ENVIRONMENT=development docker compose build front ``` -This creates unoptimized builds with source maps enabled, while still using the template-based API URL configuration for Docker. +This creates unoptimized builds with source maps. `docker-dev` has no `fileReplacements` entry, so the image compiles `environment.ts` and calls `http://localhost:9000/backend`. The envsubst output in `environment.prod.ts` is compiled only by the production configuration. #### Environment Files diff --git a/back/backend/sitmun-backend-core b/back/backend/sitmun-backend-core index 7101916..3faa39a 160000 --- a/back/backend/sitmun-backend-core +++ b/back/backend/sitmun-backend-core @@ -1 +1 @@ -Subproject commit 7101916d38a54363356e5d6396964f2a60980218 +Subproject commit 3faa39a4b066bcb28bedc677652252b93b7ef03d diff --git a/e2e/viewer/gfi-field-list.spec.ts b/e2e/viewer/gfi-field-list.spec.ts index 5d85e3d..b708b11 100644 --- a/e2e/viewer/gfi-field-list.spec.ts +++ b/e2e/viewer/gfi-field-list.spec.ts @@ -35,7 +35,15 @@ async function identifyWithFieldList(page: Page, featureInfoFields: FieldSeed[]) throw new Error(`profile has no layer whose names include ${LAYER_NAME}`); } layer.featureInfoFields = featureInfoFields; - body.tasks = body.tasks ?? []; + // Toponímia is the seeded MIA parent. MIA removes that layer from the attribute popup. + body.tasks = (body.tasks ?? []).filter((task) => { + const typeId = task['typeId']; + const cartographyId = task['cartographyId']; + return !( + typeId === 16 && + String(cartographyId) === String(QUERYABLE_LEAF_CARTOGRAPHY_ID) + ); + }); body.tasks.push({ id: 'e2e-more-info', name: TASK_NAME, @@ -57,7 +65,7 @@ async function identifyWithFieldList(page: Page, featureInfoFields: FieldSeed[]) (response) => response.url().includes('/config/client/profile/') && response.ok(), ); - await loginAndOpenMap(page); + await loginAndOpenMap(page, { requireMiaParent: false }); const profileBody = (await (await profileSeen).json()) as ProfileBody; await loadQueryableLeafIntoCapas(page); await enableCapasGfi(page); diff --git a/e2e/viewer/helpers/mia.ts b/e2e/viewer/helpers/mia.ts index 21ef909..b75f3f0 100644 --- a/e2e/viewer/helpers/mia.ts +++ b/e2e/viewer/helpers/mia.ts @@ -22,6 +22,8 @@ export async function loginAndOpenMap( territoryId?: number; /** Cartography id of the expected type-16 MIA parent (default Toponímia 6). */ miaCartographyId?: number | string; + /** Set false when the spec removes that MIA parent so the attribute popup can render. */ + requireMiaParent?: boolean; }, ): Promise { const appId = options?.appId ?? APP_ID; @@ -73,12 +75,14 @@ export async function loginAndOpenMap( profileBody.tasks?.some((task) => task['ui-control'] === 'sitna.moreInfoAdvanced'), 'profile must include sitna.moreInfoAdvanced (setup task-availability)', ).toBeTruthy(); - expect( - profileBody.tasks?.some( - (task) => task.typeId === 16 && String(task.cartographyId) === miaCartographyId, - ), - `profile must include MIA parent on cartography ${miaCartographyId}`, - ).toBeTruthy(); + if (options?.requireMiaParent !== false) { + expect( + profileBody.tasks?.some( + (task) => task.typeId === 16 && String(task.cartographyId) === miaCartographyId, + ), + `profile must include MIA parent on cartography ${miaCartographyId}`, + ).toBeTruthy(); + } await page.locator('#tc-slot-toc').waitFor({ state: 'attached', timeout: 90_000 }); await page.locator('.tc-tools-panel').evaluate((panel) => { panel.classList.remove('tc-collapsed-right'); diff --git a/front/admin/sitmun-admin-app b/front/admin/sitmun-admin-app index 7cffd20..92ced84 160000 --- a/front/admin/sitmun-admin-app +++ b/front/admin/sitmun-admin-app @@ -1 +1 @@ -Subproject commit 7cffd2090653b7a5586de66cf7befe192dec2ac5 +Subproject commit 92ced84ec307256ae76f2980b8c81dda53a9350c diff --git a/front/viewer/sitmun-viewer-app b/front/viewer/sitmun-viewer-app index b709522..689dcbe 160000 --- a/front/viewer/sitmun-viewer-app +++ b/front/viewer/sitmun-viewer-app @@ -1 +1 @@ -Subproject commit b709522a82d3eec061f53e90ea233ce97c0d530f +Subproject commit 689dcbe37052dc3360b04e8e8d2df59fc28d39d6 diff --git a/profiles/development-lvh.env b/profiles/development-lvh.env new file mode 100644 index 0000000..d660146 --- /dev/null +++ b/profiles/development-lvh.env @@ -0,0 +1,66 @@ +############################################################################### +# Development stack announced as http://lvh.me:9000 +# +# lvh.me resolves to 127.0.0.1. Nginx forwards that name in X-Forwarded-Host. +# Open the stack at http://lvh.me:9000. A shared link is accepted when its host +# is lvh.me. +# +# The air-gapped default stays profiles/development.env (PUBLIC_HOSTNAME +# localhost). Production sets PUBLIC_HOSTNAME to the real site name. +# +# Usage: +# 1. cp profiles/development-lvh.env .env +# 2. docker compose up -d --force-recreate front +############################################################################### + +COMPOSE_PROFILES=postgres +# Opt-in example: COMPOSE_PROFILES=postgres,demo,mbtiles + +# --- Base URL --- +PUBLIC_URL_SCHEME=http +PUBLIC_HOSTNAME=lvh.me +PUBLIC_PORT=9000 +PUBLIC_NON_STANDARD_PORT=1 +PUBLIC_BASE_PATH=/ + +# --- Container --- +# LOCAL_PORT=9000 +# LOCAL_BASE_PATH=/ +ENVIRONMENT=development + +# --- Database --- +# SITMUN_CONFIG_DIR=development/backend +# DATABASE_TYPE=postgres +# DATABASE_URL=jdbc:postgresql://postgres:5432/ +# DATABASE=sitmun3 +# DATABASE_USERNAME=sitmun3 +# DATABASE_PASSWORD=sitmun3 + +# --- Liquibase (development seed) --- +# Development profile uses profiles/development/backend with extensive dev/demo data: +# - 42 Liquibase changesets including tourism data, test users, and sample configurations +# - SPRING_LIQUIBASE_CONTEXTS=dev (hardcoded in application.yml) +# Production profiles use minimal seed data (only 7 base changesets) + +# --- Hibernate --- +# JPA/Hibernate settings are configured via Spring profiles: +# - Dialect loaded from application-postgres.yml (PostgreSQLDialect) +# - DDL mode defaults to 'none' (Liquibase manages schema) +# Uncomment to override: +# SPRING_JPA_HIBERNATE_DDL_AUTO=none + +# --- Proxy --- +# FORCE_USE_OF_PROXY=true + +# --- Security --- +# MIDDLEWARE_SECRET=your-secure-secret-here + +# --- Development Mode --- +# Spring Boot profile automatically set to match DATABASE_TYPE (postgres) +# Logging levels use defaults from application.yml: +# - LOGGING_LEVEL_ROOT=INFO +# - LOGGING_LEVEL_ORG_SITMUN=DEBUG +# Uncomment to override: +# SPRING_PROFILES_ACTIVE=postgres +# LOGGING_LEVEL_ROOT=INFO +# LOGGING_LEVEL_ORG_SITMUN=DEBUG