feat(rbac): hide unauthorized menu sections and enforce route guards
This commit is contained in:
@@ -96,4 +96,12 @@
|
||||
|
||||
- **MVP**: Complete Phases 1 and 2. This gives a working auth system with local users.
|
||||
- **Increment 1**: Complete Phase 3. This adds the critical security controls (RBAC).
|
||||
- **Increment 2**: Complete Phase 4. This adds corporate SSO convenience.
|
||||
- **Increment 2**: Complete Phase 4. This adds corporate SSO convenience.
|
||||
|
||||
## Post-Delivery RBAC Navigation Hardening (2026-03-06)
|
||||
|
||||
- [x] D055 Investigate frontend navigation visibility mismatch (menu items shown despite backend 403 RBAC) in `frontend/src/lib/components/layout/Sidebar.svelte` and `frontend/src/lib/components/layout/TopNavbar.svelte`
|
||||
- [x] D056 Implement shared frontend permission utilities and route-level permission enforcement in `frontend/src/lib/auth/permissions.js` and `frontend/src/components/auth/ProtectedRoute.svelte`
|
||||
- [x] D057 Implement RBAC-aware sidebar navigation builder and integrate permission-filtered categories in `frontend/src/lib/components/layout/sidebarNavigation.js` and `frontend/src/lib/components/layout/Sidebar.svelte`
|
||||
- [x] D058 Add automated frontend tests for permission normalization/checking and sidebar visibility matrix in `frontend/src/lib/auth/__tests__/permissions.test.js` and `frontend/src/lib/components/layout/__tests__/sidebarNavigation.test.js`
|
||||
- [x] D059 Execute targeted frontend test verification for RBAC navigation filtering (`npm run test -- src/lib/auth/__tests__/permissions.test.js src/lib/components/layout/__tests__/sidebarNavigation.test.js`)
|
||||
@@ -1,7 +1,7 @@
|
||||
# Implementation Plan: Clean Repository Enterprise Preparation
|
||||
|
||||
**Branch**: `023-clean-repo-enterprise` | **Date**: 2026-03-03 | **Spec**: [`spec.md`](./spec.md)
|
||||
**Input**: Feature specification from [`/specs/023-clean-repo-enterprise/spec.md`](./spec.md)
|
||||
**Branch**: `023-clean-repo-enterprise` | **Date**: 2026-03-04 | **Spec**: [`spec.md`](./spec.md)
|
||||
**Input**: Feature specification from [`/specs/023-clean-repo-enterprise/spec.md`](./spec.md) + clarifications session 2026-03-04
|
||||
|
||||
## Summary
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
1) исключает тестовые/демо-данные из дистрибутива,
|
||||
2) блокирует любые внешние интернет-источники ресурсов,
|
||||
3) допускает загрузку ресурсов только с внутренних серверов компании,
|
||||
4) предоставляет обязательную проверку compliance и аудитный отчёт перед выпуском.
|
||||
4) предоставляет обязательную проверку compliance и аудитный отчёт перед выпуском,
|
||||
5) управляется через декларативный конфиг `.clean-release.yaml` в корне репозитория (FR-015–FR-020),
|
||||
6) включает очистку БД от тестовых пользователей/демо-данных как обязательную стадию.
|
||||
|
||||
Ключевой UX фиксируется как интерактивный TUI сценарий на основе [`ux_reference.md`](./ux_reference.md): оператор в одном консольном интерфейсе запускает проверку, видит прогресс по этапам, нарушения и итоговый статус (`COMPLIANT`/`BLOCKED`).
|
||||
|
||||
@@ -31,7 +33,7 @@
|
||||
**Scale/Scope**:
|
||||
- 1 enterprise release flow;
|
||||
- 1 TUI сценарий подготовки/проверки;
|
||||
- 3–6 новых/обновлённых модулей проверки и отчётности;
|
||||
- 6–9 новых/обновлённых модулей (+config_loader, filesystem_scanner, db_cleanup_executor);
|
||||
- документация и контракты в пределах feature-папки.
|
||||
|
||||
## Constitution Check
|
||||
@@ -95,6 +97,8 @@ frontend/
|
||||
2. Политика source isolation (детекция и запрет внешних internet endpoints, allowlist внутренних серверов).
|
||||
3. Формат compliance-отчёта для релизного аудита (минимальный обязательный набор полей).
|
||||
4. Паттерн интеграции TUI-проверки в существующий release workflow (ручной запуск + CI gate).
|
||||
5. **[НОВОЕ]** Декларативный конфиг `.clean-release.yaml` как единый source of truth для всех стадий валидации.
|
||||
6. **[НОВОЕ]** Очистка БД от тестовых пользователей и демо-данных как обязательная стадия pipeline.
|
||||
|
||||
Выход Phase 0: заполненный [`research.md`](./research.md) без `NEEDS CLARIFICATION`.
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
3. Настроен внутренний реестр серверов ресурсов (artifact/git/package mirrors).
|
||||
4. Внешний интернет для узла проверки недоступен/запрещён согласно корпоративной политике.
|
||||
5. Доступен TUI-скрипт проверки clean-compliance.
|
||||
6. В корне репозитория есть `.clean-release.yaml` с секциями `prohibited_categories`, `allowed_sources` и `database_cleanup`.
|
||||
|
||||
## Step 1 — Запуск TUI
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [ ] No implementation details (languages, frameworks, APIs)
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
@@ -22,7 +22,7 @@
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [ ] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
@@ -33,20 +33,12 @@
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [ ] No implementation details leak into specification
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
### Failing items & evidence
|
||||
### Validation update
|
||||
|
||||
1) **No implementation details (languages, frameworks, APIs)** — **FAIL**
|
||||
Evidence in [spec.md](../spec.md), **Assumptions**:
|
||||
- "Apache Superset API provides access to dashboard metadata including owner and modified_by fields"
|
||||
|
||||
2) **Success criteria are technology-agnostic (no implementation details)** — **FAIL**
|
||||
Evidence in [spec.md](../spec.md), **Success Criteria**:
|
||||
- "SC-002: Dashboard list loads filtered results within 2 seconds after preference is saved"
|
||||
(This is acceptable as a user-facing performance target, but the current wording implies system internals; should be phrased as a user-perceived outcome.)
|
||||
|
||||
3) **No implementation details leak into specification** — **FAIL**
|
||||
Root cause: same as (1) and (2). Fix by rewriting assumptions and SC-002 to be implementation-agnostic and user-perceived.
|
||||
- ✅ Assumptions in [spec.md](../spec.md) were rewritten to domain/business wording.
|
||||
- ✅ Success criteria wording was updated to user-perceived outcomes (not implementation internals).
|
||||
- ✅ No implementation-detail leakage remains in specification text.
|
||||
@@ -192,6 +192,41 @@ paths:
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
- name: filter_title
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
- name: filter_git_status
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
- name: filter_llm_status
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
- name: filter_changed_on
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
- name: filter_actor
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: Dashboards page with effective profile filter metadata
|
||||
@@ -322,6 +357,38 @@ components:
|
||||
items:
|
||||
$ref: "#/components/schemas/SupersetAccountCandidate"
|
||||
|
||||
GitStatus:
|
||||
type: object
|
||||
properties:
|
||||
branch:
|
||||
type: string
|
||||
nullable: true
|
||||
sync_status:
|
||||
type: string
|
||||
enum: [OK, DIFF, NO_REPO, ERROR]
|
||||
nullable: true
|
||||
has_repo:
|
||||
type: boolean
|
||||
nullable: true
|
||||
has_changes_for_commit:
|
||||
type: boolean
|
||||
nullable: true
|
||||
|
||||
LastTask:
|
||||
type: object
|
||||
properties:
|
||||
task_id:
|
||||
type: string
|
||||
nullable: true
|
||||
status:
|
||||
type: string
|
||||
enum: [PENDING, RUNNING, SUCCESS, FAILED, ERROR, AWAITING_INPUT, WAITING_INPUT, AWAITING_MAPPING]
|
||||
nullable: true
|
||||
validation_status:
|
||||
type: string
|
||||
enum: [PASS, FAIL, WARN, UNKNOWN]
|
||||
nullable: true
|
||||
|
||||
DashboardItem:
|
||||
type: object
|
||||
required:
|
||||
@@ -338,6 +405,9 @@ components:
|
||||
last_modified:
|
||||
type: string
|
||||
nullable: true
|
||||
created_by:
|
||||
type: string
|
||||
nullable: true
|
||||
modified_by:
|
||||
type: string
|
||||
nullable: true
|
||||
@@ -345,6 +415,10 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
git_status:
|
||||
$ref: "#/components/schemas/GitStatus"
|
||||
last_task:
|
||||
$ref: "#/components/schemas/LastTask"
|
||||
|
||||
EffectiveProfileFilter:
|
||||
type: object
|
||||
|
||||
@@ -27,7 +27,7 @@ class UserDashboardPreference:
|
||||
|
||||
---
|
||||
|
||||
# [DEF:backend.src.core.superset_client.profile_lookup_extension:Module]
|
||||
# [DEF:backend.src.core.superset_profile_lookup:Module]
|
||||
# @TIER: STANDARD
|
||||
# @SEMANTICS: superset, users, lookup, environment, normalization
|
||||
# @PURPOSE: Query account candidates from selected Superset environment for profile binding.
|
||||
@@ -45,7 +45,7 @@ class SupersetAccountLookupAdapter:
|
||||
# @PRE: raw payload may differ across Superset versions.
|
||||
# @POST: Returns stable fields {username, display_name, email, is_active}.
|
||||
def normalize_user_payload(self): ...
|
||||
# [/DEF:backend.src.core.superset_client.profile_lookup_extension:Module]
|
||||
# [/DEF:backend.src.core.superset_profile_lookup:Module]
|
||||
|
||||
---
|
||||
|
||||
@@ -213,7 +213,21 @@ Scenario: User binds Superset account from environment and sees filtered dashboa
|
||||
|
||||
1. [`frontend.src.routes.profile.+page`](#deffrontendsrcroutesprofilepagemodule) requests account candidates for selected environment.
|
||||
2. [`backend.src.api.routes.profile`](#defbackendsrcapiroutesprofilemodule) validates self-scope and forwards lookup request.
|
||||
3. [`backend.src.services.profile_service`](#defbackendsrcservicesprofile_servicemodule) orchestrates lookup via [`backend.src.core.superset_client.profile_lookup_extension`](#defbackendsrccoresuperset_clientprofile_lookup_extensionmodule).
|
||||
3. [`backend.src.services.profile_service`](#defbackendsrcservicesprofile_servicemodule) orchestrates lookup via [`backend.src.core.superset_profile_lookup`](#defbackendsrccoresuperset_profile_lookupmodule).
|
||||
4. User saves preference; profile service normalizes username and persists [`backend.src.models.profile`](#defbackendsrcmodelsprofilemodule).
|
||||
5. On `/dashboards`, [`backend.src.api.routes.dashboards.profile_filter_extension`](#defbackendsrcapiroutesdashboardsprofile_filter_extensionmodule) applies `owners OR modified_by` matching and returns effective filter metadata.
|
||||
6. [`frontend.src.routes.dashboards.+page.profile_filter_ui_extension`](#deffrontendsrcroutesdashboardspageprofile_filter_ui_extensionmodule) shows active badge and supports temporary "show all" override.
|
||||
6. [`frontend.src.routes.dashboards.+page.profile_filter_ui_extension`](#deffrontendsrcroutesdashboardspageprofile_filter_ui_extensionmodule) shows active badge and supports temporary "show all" override.
|
||||
|
||||
---
|
||||
|
||||
## Edge-case Trace Evidence (2026-03-05)
|
||||
|
||||
| Contract Edge / Invariant | Verification Test | Status |
|
||||
|---|---|---|
|
||||
| `enable_without_username` validation | `backend/src/api/routes/__tests__/test_profile_api.py::test_patch_profile_preferences_validation_error` | ✅ |
|
||||
| `cross_user_mutation` guard | `backend/src/api/routes/__tests__/test_profile_api.py::test_patch_profile_preferences_cross_user_denied` | ✅ |
|
||||
| `lookup_env_not_found` recovery path | `backend/src/api/routes/__tests__/test_profile_api.py::test_lookup_superset_accounts_env_not_found` | ✅ |
|
||||
| `owners OR modified_by` normalization contract | `backend/src/api/routes/__tests__/test_dashboards.py::test_get_dashboards_profile_filter_contract_owners_or_modified_by` | ✅ |
|
||||
| `override_show_all` semantics | `backend/src/api/routes/__tests__/test_dashboards.py::test_get_dashboards_override_show_all_contract` | ✅ |
|
||||
| `no_match_results` under active profile filter | `backend/src/api/routes/__tests__/test_dashboards.py::test_get_dashboards_profile_filter_no_match_results_contract` | ✅ |
|
||||
| page-scoped override restore UX | `frontend/src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js` | ✅ |
|
||||
@@ -211,6 +211,24 @@ Each story will include independent acceptance tests and explicit completion evi
|
||||
|
||||
**Gate Result (post-design)**: PASS
|
||||
|
||||
## Implementation Notes (2026-03-05)
|
||||
|
||||
- Profile persistence, API routes, and Superset lookup are fully wired:
|
||||
- `backend/src/models/profile.py`
|
||||
- `backend/src/schemas/profile.py`
|
||||
- `backend/src/services/profile_service.py`
|
||||
- `backend/src/api/routes/profile.py`
|
||||
- `backend/src/core/superset_profile_lookup.py`
|
||||
- router registration includes `profile.router` in `backend/src/app.py`.
|
||||
- Dashboards API implements profile-default behavior with deterministic metadata:
|
||||
- `page_context`, `apply_profile_default`, `override_show_all`
|
||||
- response envelope `effective_profile_filter`.
|
||||
- Frontend flows implemented and validated:
|
||||
- profile page preload/save/cancel + degraded lookup fallback,
|
||||
- dashboards filter badge + temporary show-all + restore-on-return.
|
||||
- Test evidence captured in [`quickstart.md`](./quickstart.md) and [`tests/coverage.md`](./tests/coverage.md).
|
||||
- Operational note: Svelte deprecation/a11y warnings remain in dashboards UI (`on:*` directive deprecations and label association warnings). They are non-blocking for feature behavior and should be addressed in a dedicated frontend cleanup task.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
> Fill ONLY if Constitution Check has violations that must be justified
|
||||
|
||||
@@ -159,14 +159,14 @@ Expected:
|
||||
|
||||
## Acceptance Checklist (Operator)
|
||||
|
||||
- [ ] Profile page visible and reachable from navigation.
|
||||
- [ ] Environment-based account lookup works for binding.
|
||||
- [ ] Lookup failure path allows manual entry (non-blocking).
|
||||
- [ ] Preference save persists across reload/session.
|
||||
- [ ] `/dashboards` applies default filter by `owners OR modified_by`.
|
||||
- [ ] Temporary clear shows all dashboards without changing saved preference.
|
||||
- [ ] Re-entering `/dashboards` restores default filtered behavior.
|
||||
- [ ] i18n texts are present for new profile/lookup/filter states (EN + RU).
|
||||
- [x] Profile page visible and reachable from navigation.
|
||||
- [x] Environment-based account lookup works for binding.
|
||||
- [x] Lookup failure path allows manual entry (non-blocking).
|
||||
- [x] Preference save persists across reload/session.
|
||||
- [x] `/dashboards` applies default filter by `owners OR modified_by`.
|
||||
- [x] Temporary clear shows all dashboards without changing saved preference.
|
||||
- [x] Re-entering `/dashboards` restores default filtered behavior.
|
||||
- [x] i18n texts are present for new profile/lookup/filter states (EN + RU).
|
||||
|
||||
---
|
||||
|
||||
@@ -197,6 +197,22 @@ cd backend && .venv/bin/python3 -m pytest src/api/routes/__tests__ -k "profile o
|
||||
|
||||
---
|
||||
|
||||
## Validation Evidence (2026-03-05)
|
||||
|
||||
Automated verification runs completed:
|
||||
|
||||
1. Frontend integration scenarios:
|
||||
- `cd frontend && npm run test -- src/routes/profile/__tests__/profile-preferences.integration.test.js src/routes/profile/__tests__/profile-settings-state.integration.test.js src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js`
|
||||
- Result: **3 test files passed, 6 tests passed**.
|
||||
|
||||
2. Backend profile/dashboards route contracts:
|
||||
- `cd backend && .venv/bin/python3 -m pytest src/api/routes/__tests__/test_profile_api.py src/api/routes/__tests__/test_dashboards.py`
|
||||
- Result: **26 tests passed**.
|
||||
|
||||
Artifacts and matrix are recorded in [`tests/coverage.md`](./tests/coverage.md).
|
||||
|
||||
---
|
||||
|
||||
## Exit Criteria
|
||||
|
||||
Feature is considered ready for implementation tasking when:
|
||||
|
||||
@@ -117,19 +117,19 @@ As a user, I want to temporarily view all dashboards even when my filter is enab
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: Users can configure their dashboard filter preference in under 30 seconds
|
||||
- **SC-002**: After saving preferences, users see the dashboard list update to the filtered view within 2 seconds
|
||||
- **SC-002**: After saving preferences, users can return to the dashboards list and see their personalized view within 2 seconds
|
||||
- **SC-003**: 95% of users successfully save their profile settings on first attempt
|
||||
- **SC-004**: Users report a 50% reduction in time spent finding their dashboards in surveys
|
||||
- **SC-005**: During peak usage, users can save preferences reliably without noticeable slowdowns
|
||||
- **SC-006**: Filter correctly identifies user-owned dashboards with 99% accuracy based on owner and modified_by fields
|
||||
- **SC-006**: Filter correctly identifies user-related dashboards with 99% accuracy based on dashboard ownership and last-editor information
|
||||
- **SC-007**: Profile settings persist correctly across 100% of user sessions
|
||||
|
||||
## Assumptions
|
||||
|
||||
- Users have an existing Apache Superset account with a unique username
|
||||
- The application can access dashboard metadata that includes owner and modified_by identifiers
|
||||
- The application has an existing user authentication system that can be extended with profile settings
|
||||
- Dashboard list page exists and can be modified to support filtering
|
||||
- Dashboard records include information about dashboard ownership and last editor
|
||||
- Users are authenticated before they can open and update profile settings
|
||||
- Users have a central dashboards list where the default filter behavior can be applied
|
||||
- Apache Superset username format follows standard conventions (alphanumeric, underscores, hyphens allowed)
|
||||
- Each user has one global Superset username that is used in all environments
|
||||
- Configured Superset environments expose account listing data that can be queried during profile binding
|
||||
- Configured Superset environments provide discoverable account names for profile binding
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
|
||||
**Purpose**: Initialize scaffolding and shared assets for profile preferences and dashboards filter UX.
|
||||
|
||||
- [ ] T001 Create feature scaffolding files in `backend/src/models/profile.py`, `backend/src/schemas/profile.py`, `backend/src/services/profile_service.py`, `backend/src/api/routes/profile.py`, and `frontend/src/routes/profile/+page.svelte`
|
||||
- [ ] T002 [P] Add shared fixture placeholders for profile/filter flows in `backend/tests/fixtures/profile/fixtures_profile_filter.json` and `frontend/src/routes/profile/__tests__/fixtures/profile.fixtures.js`
|
||||
- [ ] T003 [P] Add i18n placeholder keys for profile lookup/filter states in `frontend/src/lib/i18n/locales/en.json` and `frontend/src/lib/i18n/locales/ru.json`
|
||||
- [ ] T004 [P] Add profile navigation placeholder entry in `frontend/src/lib/components/layout/Sidebar.svelte`
|
||||
- [x] T001 Create feature scaffolding files in `backend/src/models/profile.py`, `backend/src/schemas/profile.py`, `backend/src/services/profile_service.py`, `backend/src/api/routes/profile.py`, and `frontend/src/routes/profile/+page.svelte`
|
||||
- [x] T002 [P] Add shared fixture placeholders for profile/filter flows in `backend/tests/fixtures/profile/fixtures_profile_filter.json` and `frontend/src/routes/profile/__tests__/fixtures/profile.fixtures.js`
|
||||
- [x] T003 [P] Add i18n placeholder keys for profile lookup/filter states in `frontend/src/lib/i18n/locales/en.json` and `frontend/src/lib/i18n/locales/ru.json`
|
||||
- [x] T004 [P] Add profile navigation placeholder entry in `frontend/src/lib/components/layout/Sidebar.svelte`
|
||||
|
||||
---
|
||||
|
||||
@@ -32,14 +32,14 @@
|
||||
|
||||
**⚠️ CRITICAL**: No user story work starts before this phase is complete.
|
||||
|
||||
- [ ] T005 Implement persistent `UserDashboardPreference` entity and repository access in `backend/src/models/profile.py` and `backend/src/core/auth/repository.py`
|
||||
- [ ] T006 [P] Implement profile preference and Superset lookup schemas in `backend/src/schemas/profile.py`
|
||||
- [ ] T007 [P] Implement Superset account lookup adapter for selected environment (paging/sort passthrough and normalization) in `backend/src/core/superset_client.py`
|
||||
- [ ] T008 Implement profile domain orchestration in `backend/src/services/profile_service.py` (CRITICAL: PRE authenticated user + payload/environment; POST self-scoped normalized preference save and deterministic actor matching helper; UX_STATE backend service, no direct UI states; TEST_FIXTURE `valid_profile_update`; TEST_EDGE `enable_without_username`, `cross_user_mutation`, `lookup_env_not_found`)
|
||||
- [ ] T009 Implement profile API endpoints in `backend/src/api/routes/profile.py` (CRITICAL: PRE valid auth token, self context, `environment_id` for lookup; POST returns self-only preference payload or degraded lookup warning payload; UX_STATE supports profile page save/lookup state mapping through stable response shape; TEST_FIXTURE `get_my_preference_ok`; TEST_EDGE `unauthorized_request`, `invalid_username_payload`, `superset_lookup_upstream_error`)
|
||||
- [ ] T010 Wire profile router registration in `backend/src/api/routes/__init__.py` and `backend/src/app.py`
|
||||
- [ ] T011 [P] Extend frontend API methods for profile preferences, Superset account lookup, and dashboards profile filter params in `frontend/src/lib/api.js`
|
||||
- [ ] T012 [P] Create backend test skeletons for profile and dashboards filter contracts in `backend/src/api/routes/__tests__/test_profile_api.py` and `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
- [x] T005 Implement persistent `UserDashboardPreference` entity and repository access in `backend/src/models/profile.py` and `backend/src/core/auth/repository.py`
|
||||
- [x] T006 [P] Implement profile preference and Superset lookup schemas in `backend/src/schemas/profile.py`
|
||||
- [x] T007 [P] Implement Superset account lookup adapter for selected environment (paging/sort passthrough and normalization) in `backend/src/core/superset_client.py`
|
||||
- [x] T008 Implement profile domain orchestration in `backend/src/services/profile_service.py` (CRITICAL: PRE authenticated user + payload/environment; POST self-scoped normalized preference save and deterministic actor matching helper; UX_STATE backend service, no direct UI states; TEST_FIXTURE `valid_profile_update`; TEST_EDGE `enable_without_username`, `cross_user_mutation`, `lookup_env_not_found`)
|
||||
- [x] T009 Implement profile API endpoints in `backend/src/api/routes/profile.py` (CRITICAL: PRE valid auth token, self context, `environment_id` for lookup; POST returns self-only preference payload or degraded lookup warning payload; UX_STATE supports profile page save/lookup state mapping through stable response shape; TEST_FIXTURE `get_my_preference_ok`; TEST_EDGE `unauthorized_request`, `invalid_username_payload`, `superset_lookup_upstream_error`)
|
||||
- [x] T010 Wire profile router registration in `backend/src/api/routes/__init__.py` and `backend/src/app.py`
|
||||
- [x] T011 [P] Extend frontend API methods for profile preferences, Superset account lookup, and dashboards profile filter params in `frontend/src/lib/api.js`
|
||||
- [x] T012 [P] Create backend test skeletons for profile and dashboards filter contracts in `backend/src/api/routes/__tests__/test_profile_api.py` and `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
|
||||
**Checkpoint**: Foundation ready; user stories can now be implemented and validated independently.
|
||||
|
||||
@@ -53,19 +53,19 @@
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
- [ ] T013 [P] [US1] Add contract tests for `GET/PATCH /api/profile/preferences` validation and persistence rules in `backend/src/api/routes/__tests__/test_profile_api.py`
|
||||
- [ ] T014 [P] [US1] Add dashboards filter contract tests for `owners OR modified_by` with trim + case-insensitive matching in `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
- [ ] T015 [P] [US1] Add frontend integration test for profile binding happy path (lookup success and manual fallback save) in `frontend/src/routes/profile/__tests__/profile-preferences.integration.test.js`
|
||||
- [x] T013 [P] [US1] Add contract tests for `GET/PATCH /api/profile/preferences` validation and persistence rules in `backend/src/api/routes/__tests__/test_profile_api.py`
|
||||
- [x] T014 [P] [US1] Add dashboards filter contract tests for `owners OR modified_by` with trim + case-insensitive matching in `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
- [x] T015 [P] [US1] Add frontend integration test for profile binding happy path (lookup success and manual fallback save) in `frontend/src/routes/profile/__tests__/profile-preferences.integration.test.js`
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [ ] T016 [US1] Implement lookup + save rules (global username, normalization, non-blocking degraded lookup) in `backend/src/services/profile_service.py` (CRITICAL: PRE authenticated user and selected environment for lookup; POST persisted normalized global username and lookup fallback remains save-capable; UX_STATE supports `LookupLoading/LookupError/Saving/SaveSuccess/SaveError` via service outcomes; TEST_FIXTURE `valid_profile_update`; TEST_EDGE `enable_without_username`, `lookup_env_not_found`)
|
||||
- [ ] T017 [US1] Implement profile route request/response mapping for preference save/get + account lookup in `backend/src/api/routes/profile.py` (CRITICAL: PRE self-scoped authenticated request; POST stable `ProfilePreferenceResponse` and `SupersetAccountLookupResponse`; UX_STATE provides explicit degraded warning for profile UI recovery; TEST_FIXTURE `get_my_preference_ok`; TEST_EDGE `invalid_username_payload`, `superset_lookup_upstream_error`)
|
||||
- [ ] T018 [US1] Implement default profile filtering on dashboards main list in `backend/src/api/routes/dashboards.py` (CRITICAL: PRE parsed `page_context/apply_profile_default/override_show_all` query context; POST response totals and pagination remain deterministic with `owners OR modified_by` matching; UX_STATE supports filtered list and empty-filtered state metadata; TEST_FIXTURE `profile_filter_applied`; TEST_EDGE `no_match_results`)
|
||||
- [ ] T019 [P] [US1] Implement profile page form with environment selector, account suggestions, manual username field, toggle, and save/cancel actions in `frontend/src/routes/profile/+page.svelte` (CRITICAL: PRE authenticated user and initial preference load; POST persisted values reflected in form state; UX_STATE `Default`, `LookupLoading`, `LookupError`, `Saving`, `SaveSuccess`, `SaveError`; TEST_FIXTURE `bind_account_happy_path`; TEST_EDGE `lookup_failed_manual_fallback`, `invalid_username`, `cancel_changes`)
|
||||
- [ ] T020 [P] [US1] Implement dashboards active-filter indicator and filtered empty-state rendering in `frontend/src/routes/dashboards/+page.svelte` (CRITICAL: PRE effective filter metadata returned by dashboards API; POST filtered context is visible and understandable; UX_STATE `FilterActive`, `EmptyFiltered`; TEST_FIXTURE `default_profile_filter_applied`; TEST_EDGE `no_matching_dashboards`)
|
||||
- [ ] T021 [US1] Finalize localized copy for profile binding, lookup warning, and filter-active texts in `frontend/src/lib/i18n/locales/en.json` and `frontend/src/lib/i18n/locales/ru.json`
|
||||
- [ ] T022 [US1] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
- [x] T016 [US1] Implement lookup + save rules (global username, normalization, non-blocking degraded lookup) in `backend/src/services/profile_service.py` (CRITICAL: PRE authenticated user and selected environment for lookup; POST persisted normalized global username and lookup fallback remains save-capable; UX_STATE supports `LookupLoading/LookupError/Saving/SaveSuccess/SaveError` via service outcomes; TEST_FIXTURE `valid_profile_update`; TEST_EDGE `enable_without_username`, `lookup_env_not_found`)
|
||||
- [x] T017 [US1] Implement profile route request/response mapping for preference save/get + account lookup in `backend/src/api/routes/profile.py` (CRITICAL: PRE self-scoped authenticated request; POST stable `ProfilePreferenceResponse` and `SupersetAccountLookupResponse`; UX_STATE provides explicit degraded warning for profile UI recovery; TEST_FIXTURE `get_my_preference_ok`; TEST_EDGE `invalid_username_payload`, `superset_lookup_upstream_error`)
|
||||
- [x] T018 [US1] Implement default profile filtering on dashboards main list in `backend/src/api/routes/dashboards.py` (CRITICAL: PRE parsed `page_context/apply_profile_default/override_show_all` query context; POST response totals and pagination remain deterministic with `owners OR modified_by` matching; UX_STATE supports filtered list and empty-filtered state metadata; TEST_FIXTURE `profile_filter_applied`; TEST_EDGE `no_match_results`)
|
||||
- [x] T019 [P] [US1] Implement profile page form with environment selector, account suggestions, manual username field, toggle, and save/cancel actions in `frontend/src/routes/profile/+page.svelte` (CRITICAL: PRE authenticated user and initial preference load; POST persisted values reflected in form state; UX_STATE `Default`, `LookupLoading`, `LookupError`, `Saving`, `SaveSuccess`, `SaveError`; TEST_FIXTURE `bind_account_happy_path`; TEST_EDGE `lookup_failed_manual_fallback`, `invalid_username`, `cancel_changes`)
|
||||
- [x] T020 [P] [US1] Implement dashboards active-filter indicator and filtered empty-state rendering in `frontend/src/routes/dashboards/+page.svelte` (CRITICAL: PRE effective filter metadata returned by dashboards API; POST filtered context is visible and understandable; UX_STATE `FilterActive`, `EmptyFiltered`; TEST_FIXTURE `default_profile_filter_applied`; TEST_EDGE `no_matching_dashboards`)
|
||||
- [x] T021 [US1] Finalize localized copy for profile binding, lookup warning, and filter-active texts in `frontend/src/lib/i18n/locales/en.json` and `frontend/src/lib/i18n/locales/ru.json`
|
||||
- [x] T022 [US1] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
|
||||
**Checkpoint**: US1 is independently functional and demo-ready as MVP.
|
||||
|
||||
@@ -79,15 +79,15 @@
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
- [ ] T023 [P] [US2] Add backend authorization tests for self-only preference mutation and cross-user rejection in `backend/src/api/routes/__tests__/test_profile_api.py`
|
||||
- [ ] T024 [P] [US2] Add frontend tests for preload, cancel without persistence, and saved-state reload in `frontend/src/routes/profile/__tests__/profile-settings-state.integration.test.js`
|
||||
- [x] T023 [P] [US2] Add backend authorization tests for self-only preference mutation and cross-user rejection in `backend/src/api/routes/__tests__/test_profile_api.py`
|
||||
- [x] T024 [P] [US2] Add frontend tests for preload, cancel without persistence, and saved-state reload in `frontend/src/routes/profile/__tests__/profile-settings-state.integration.test.js`
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T025 [US2] Implement preload and cancel-to-last-saved behavior in `frontend/src/routes/profile/+page.svelte` (CRITICAL: PRE current preference loaded before editing; POST cancel does not mutate persisted data; UX_STATE `Default`, `Saving`, `SaveError` retain user context; TEST_EDGE `cancel_changes`)
|
||||
- [ ] T026 [US2] Enforce self-scope mutation guard across service and route in `backend/src/services/profile_service.py` and `backend/src/api/routes/profile.py` (CRITICAL: PRE authenticated actor identity; POST attempts to mutate another user are denied; UX_STATE maps denial into actionable error feedback; TEST_EDGE `cross_user_mutation`)
|
||||
- [ ] T027 [US2] Implement consistent save success/error/validation feedback mapping in `frontend/src/routes/profile/+page.svelte` and `frontend/src/lib/api.js` (CRITICAL: PRE response includes validation or error details; POST user gets clear recovery guidance without data loss; UX_STATE `SaveSuccess`, `SaveError`; TEST_EDGE `invalid_username`)
|
||||
- [ ] T028 [US2] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
- [x] T025 [US2] Implement preload and cancel-to-last-saved behavior in `frontend/src/routes/profile/+page.svelte` (CRITICAL: PRE current preference loaded before editing; POST cancel does not mutate persisted data; UX_STATE `Default`, `Saving`, `SaveError` retain user context; TEST_EDGE `cancel_changes`)
|
||||
- [x] T026 [US2] Enforce self-scope mutation guard across service and route in `backend/src/services/profile_service.py` and `backend/src/api/routes/profile.py` (CRITICAL: PRE authenticated actor identity; POST attempts to mutate another user are denied; UX_STATE maps denial into actionable error feedback; TEST_EDGE `cross_user_mutation`)
|
||||
- [x] T027 [US2] Implement consistent save success/error/validation feedback mapping in `frontend/src/routes/profile/+page.svelte` and `frontend/src/lib/api.js` (CRITICAL: PRE response includes validation or error details; POST user gets clear recovery guidance without data loss; UX_STATE `SaveSuccess`, `SaveError`; TEST_EDGE `invalid_username`)
|
||||
- [x] T028 [US2] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
|
||||
**Checkpoint**: US2 independently functional with robust state management and ownership guardrails.
|
||||
|
||||
@@ -101,14 +101,14 @@
|
||||
|
||||
### Tests for User Story 3
|
||||
|
||||
- [ ] T029 [P] [US3] Add backend tests for `override_show_all`, `page_context`, and effective filter metadata semantics in `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
- [ ] T030 [P] [US3] Add frontend integration test for temporary clear and restore-on-return flow in `frontend/src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js`
|
||||
- [x] T029 [P] [US3] Add backend tests for `override_show_all`, `page_context`, and effective filter metadata semantics in `backend/src/api/routes/__tests__/test_dashboards.py`
|
||||
- [x] T030 [P] [US3] Add frontend integration test for temporary clear and restore-on-return flow in `frontend/src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js`
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T031 [US3] Implement override semantics and effective filter metadata in dashboards API response in `backend/src/api/routes/dashboards.py` (CRITICAL: PRE main-list context with override flag; POST `effective_profile_filter.applied=false` when override is active and persisted preference remains unchanged; UX_STATE enables clear distinction between active-filter and override states; TEST_FIXTURE `profile_filter_applied`; TEST_EDGE `override_show_all`, `no_match_results`)
|
||||
- [ ] T032 [US3] Implement temporary show-all control and restore-on-return behavior in `frontend/src/routes/dashboards/+page.svelte` (CRITICAL: PRE active filtered state available from response metadata; POST override is page-scoped and non-persistent; UX_STATE `FilterActive`, `OverrideActive`, `EmptyFiltered`; TEST_FIXTURE `default_profile_filter_applied`; TEST_EDGE `temporary_show_all`, `return_to_page`)
|
||||
- [ ] T033 [US3] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
- [x] T031 [US3] Implement override semantics and effective filter metadata in dashboards API response in `backend/src/api/routes/dashboards.py` (CRITICAL: PRE main-list context with override flag; POST `effective_profile_filter.applied=false` when override is active and persisted preference remains unchanged; UX_STATE enables clear distinction between active-filter and override states; TEST_FIXTURE `profile_filter_applied`; TEST_EDGE `override_show_all`, `no_match_results`)
|
||||
- [x] T032 [US3] Implement temporary show-all control and restore-on-return behavior in `frontend/src/routes/dashboards/+page.svelte` (CRITICAL: PRE active filtered state available from response metadata; POST override is page-scoped and non-persistent; UX_STATE `FilterActive`, `OverrideActive`, `EmptyFiltered`; TEST_FIXTURE `default_profile_filter_applied`; TEST_EDGE `temporary_show_all`, `return_to_page`)
|
||||
- [x] T033 [US3] Verify implementation matches `specs/024-user-dashboard-filter/ux_reference.md` (Happy Path & Errors)
|
||||
|
||||
**Checkpoint**: US3 independently functional with non-persistent override behavior.
|
||||
|
||||
@@ -118,10 +118,10 @@
|
||||
|
||||
**Purpose**: Align final contracts/docs and record verification evidence across all delivered stories.
|
||||
|
||||
- [ ] T034 [P] Reconcile OpenAPI contract with implemented payloads and query semantics in `specs/024-user-dashboard-filter/contracts/api.yaml`
|
||||
- [ ] T035 [P] Reconcile CRITICAL module contract annotations and edge-case traces in `specs/024-user-dashboard-filter/contracts/modules.md`
|
||||
- [ ] T036 [P] Execute quickstart validation and record outcomes in `specs/024-user-dashboard-filter/quickstart.md` and `specs/024-user-dashboard-filter/tests/coverage.md`
|
||||
- [ ] T037 Document final implementation notes and operational constraints in `specs/024-user-dashboard-filter/plan.md`
|
||||
- [x] T034 [P] Reconcile OpenAPI contract with implemented payloads and query semantics in `specs/024-user-dashboard-filter/contracts/api.yaml`
|
||||
- [x] T035 [P] Reconcile CRITICAL module contract annotations and edge-case traces in `specs/024-user-dashboard-filter/contracts/modules.md`
|
||||
- [x] T036 [P] Execute quickstart validation and record outcomes in `specs/024-user-dashboard-filter/quickstart.md` and `specs/024-user-dashboard-filter/tests/coverage.md`
|
||||
- [x] T037 Document final implementation notes and operational constraints in `specs/024-user-dashboard-filter/plan.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -200,5 +200,22 @@ Task: "T030 [US3] Frontend override restore integration test in frontend/src/rou
|
||||
|
||||
### UX Preservation Rule
|
||||
|
||||
No task in this plan intentionally degrades UX defined in `specs/024-user-dashboard-filter/ux_reference.md`.
|
||||
Mandatory UX verification tasks are included at the end of each user story phase: **T022**, **T028**, **T033**.
|
||||
No task in this plan intentionally degrades UX defined in `specs/024-user-dashboard-filter/ux_reference.md`.
|
||||
Mandatory UX verification tasks are included at the end of each user story phase: **T022**, **T028**, **T033**.
|
||||
|
||||
---
|
||||
|
||||
## Post-Delivery Debug Status (2026-03-05)
|
||||
|
||||
- [x] D001 Investigate degraded `/api/profile/superset-accounts` lookup for `sort_order=asc` in `ss-dev`
|
||||
- [x] D002 Confirm diagnosis using enhanced runtime logs before behavior fix
|
||||
- [x] D003 Apply behavior fix and re-run targeted profile lookup verification
|
||||
- [x] D004 Investigate zero profile-filter matches on `/api/dashboards` for `admin` by logging raw Superset actor fields
|
||||
- [x] D005 Confirm diagnosis from actor-field logs before changing filter behavior
|
||||
- [x] D006 Implement dashboard actor hydration fallback (detail API) and verify targeted dashboards filter tests
|
||||
- [x] D007 Investigate regression where `/api/dashboards` triggered per-dashboard `SupersetClient.get_dashboard` fan-out
|
||||
- [x] D008 Replace O(N) hydration with O(1) profile actor alias lookup (username + display name) in dashboards filter path
|
||||
- [x] D009 Verify updated dashboards filter behavior with targeted backend tests including no-detail-fanout assertion
|
||||
- [x] D007 Investigate regression where `/api/dashboards` triggers per-dashboard `SupersetClient.get_dashboard` fan-out
|
||||
- [x] D008 Replace O(N) hydration with O(1) profile actor alias lookup (username + display name) in dashboards filter path
|
||||
- [x] D009 Verify updated dashboards filter behavior with targeted backend tests including no-detail-fanout assertion
|
||||
49
specs/024-user-dashboard-filter/tests/coverage.md
Normal file
49
specs/024-user-dashboard-filter/tests/coverage.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Test Coverage Matrix: User Profile Dashboard Filter
|
||||
|
||||
## Validation Snapshot
|
||||
|
||||
- **Date (UTC)**: 2026-03-05
|
||||
- **Feature**: `024-user-dashboard-filter`
|
||||
|
||||
## Executed Automated Test Runs
|
||||
|
||||
### 1) Backend profile + dashboards contracts
|
||||
|
||||
```bash
|
||||
cd backend && .venv/bin/python3 -m pytest src/api/routes/__tests__/test_profile_api.py src/api/routes/__tests__/test_dashboards.py
|
||||
```
|
||||
|
||||
- **Result**: `27 passed`
|
||||
|
||||
### 2) Frontend profile + dashboards integration scenarios
|
||||
|
||||
```bash
|
||||
cd frontend && npm run test -- src/routes/profile/__tests__/profile-preferences.integration.test.js src/routes/profile/__tests__/profile-settings-state.integration.test.js src/routes/dashboards/__tests__/dashboard-profile-override.integration.test.js
|
||||
```
|
||||
|
||||
- **Result**: `3 files passed`, `6 tests passed`
|
||||
|
||||
## Coverage by User Story
|
||||
|
||||
| User Story | Scope | Evidence | Status |
|
||||
|---|---|---|---|
|
||||
| US1 | Profile binding + default dashboard filtering | `test_profile_api.py`, `test_dashboards.py`, `profile-preferences.integration.test.js` | ✅ |
|
||||
| US2 | Preload/cancel/saved-state management + self-scope guard | `test_profile_api.py`, `profile-settings-state.integration.test.js` | ✅ |
|
||||
| US3 | Temporary show-all override + restore-on-return | `test_dashboards.py`, `dashboard-profile-override.integration.test.js` | ✅ |
|
||||
|
||||
## Contract Edge Evidence
|
||||
|
||||
| Contract Edge | Verification Test | Status |
|
||||
|---|---|---|
|
||||
| `enable_without_username` | `test_patch_profile_preferences_validation_error` | ✅ |
|
||||
| `cross_user_mutation` | `test_patch_profile_preferences_cross_user_denied` | ✅ |
|
||||
| `lookup_env_not_found` | `test_lookup_superset_accounts_env_not_found` | ✅ |
|
||||
| `owners OR modified_by` (trim + case-insensitive) | `test_get_dashboards_profile_filter_contract_owners_or_modified_by` | ✅ |
|
||||
| `override_show_all` metadata semantics | `test_get_dashboards_override_show_all_contract` | ✅ |
|
||||
| `no_match_results` with active profile filter | `test_get_dashboards_profile_filter_no_match_results_contract` | ✅ |
|
||||
| restore-on-return behavior (page-scoped override) | `dashboard-profile-override.integration.test.js` | ✅ |
|
||||
|
||||
## Notes
|
||||
|
||||
- Feature scenarios required by quickstart and story contracts are covered by automated tests above.
|
||||
- Frontend run reports Svelte deprecation/a11y warnings in dashboards UI; these are non-blocking for current feature behavior and do not fail tests.
|
||||
Reference in New Issue
Block a user