измененные спеки таски
This commit is contained in:
612
specs/019-superset-ux-redesign/contracts/modules.md
Normal file
612
specs/019-superset-ux-redesign/contracts/modules.md
Normal file
@@ -0,0 +1,612 @@
|
||||
# Module Contracts: Superset-Style UX Redesign
|
||||
|
||||
**Feature**: 019-superset-ux-redesign
|
||||
**Date**: 2026-02-10
|
||||
**Semantic Protocol Version**: GRACE-Poly (UX Edition)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines module-level contracts following the Semantic Protocol. Each module is annotated with `[DEF]` anchors, `@TIER` classification, and full Design-by-Contract (`@PRE`, `@POST`, `@UX_STATE`) specifications.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Modules
|
||||
|
||||
### 1. SidebarStore
|
||||
|
||||
```javascript
|
||||
// [DEF:SidebarStore:Store]
|
||||
// @TIER: STANDARD
|
||||
// @SEMANTICS: navigation, state-management, persistence
|
||||
// @PURPOSE: Manage sidebar visibility, active navigation state, and mobile overlay behavior
|
||||
// @LAYER: UI
|
||||
// @RELATION: DEPENDS_ON -> localStorage
|
||||
// @RELATION: BINDS_TO -> +layout.svelte
|
||||
// @INVARIANT: isExpanded state is always synced with localStorage key 'sidebar_state'
|
||||
|
||||
// @UX_STATE: Idle -> Sidebar visible with current category highlighted
|
||||
// @UX_STATE: Collapsed -> Icons-only mode, tooltips on hover
|
||||
// @UX_STATE: MobileOverlay -> Full-screen overlay with backdrop, close on outside click
|
||||
// @UX_STATE: Toggling -> CSS transition animation (200ms ease-in-out)
|
||||
|
||||
// @PRE: localStorage is available and accessible
|
||||
// @PRE: defaultValue has valid shape { isExpanded: boolean, activeCategory: string, activeItem: string, isMobileOpen: boolean }
|
||||
|
||||
// @POST: Store always emits valid SidebarState shape
|
||||
// @POST: localStorage 'sidebar_state' is updated on every state change
|
||||
// @POST: isMobileOpen is reset to false when screen size changes to desktop (>=768px)
|
||||
|
||||
// @SIDE_EFFECT: Writes to localStorage
|
||||
// @SIDE_EFFECT: Triggers CSS transitions via class binding
|
||||
|
||||
export const sidebarStore = persistentStore('sidebar_state', {
|
||||
isExpanded: true,
|
||||
activeCategory: 'dashboards',
|
||||
activeItem: '/dashboards',
|
||||
isMobileOpen: false
|
||||
});
|
||||
|
||||
export function toggleSidebar() { /* ... */ }
|
||||
export function setActiveCategory(category) { /* ... */ }
|
||||
export function setActiveItem(path) { /* ... */ }
|
||||
export function openMobileSidebar() { /* ... */ }
|
||||
export function closeMobileSidebar() { /* ... */ }
|
||||
// [/DEF:SidebarStore]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. TaskDrawerStore
|
||||
|
||||
```javascript
|
||||
// [DEF:TaskDrawerStore:Store]
|
||||
// @TIER: CRITICAL
|
||||
// @SEMANTICS: task-management, resource-mapping, real-time, drawer-state
|
||||
// @PURPOSE: Manage Task Drawer visibility, active task tracking, and resource-to-task associations
|
||||
// @LAYER: UI
|
||||
// @RELATION: DEPENDS_ON -> WebSocket connection
|
||||
// @RELATION: DEPENDS_ON -> task:status events
|
||||
// @RELATION: DEPENDS_ON -> task:log events
|
||||
// @RELATION: BINDS_TO -> TaskDrawer.svelte
|
||||
// @INVARIANT: resourceTaskMap always contains valid task associations
|
||||
// @INVARIANT: When isOpen is false, activeTaskId may still reference a running task
|
||||
|
||||
// @UX_STATE: Closed -> Drawer hidden, Activity indicator shows active count
|
||||
// @UX_STATE: Open-Loading -> Drawer visible, loading spinner for logs
|
||||
// @UX_STATE: Open-Streaming -> Drawer visible, real-time log stream
|
||||
// @UX_STATE: Open-InputRequired -> Interactive form rendered in drawer (PasswordPrompt, etc.)
|
||||
// @UX_STATE: Open-Completed -> Task finished, success/error status displayed
|
||||
|
||||
// @UX_FEEDBACK: Toast notification when task completes while drawer is closed
|
||||
// @UX_FEEDBACK: Activity indicator badge pulses when new tasks start
|
||||
// @UX_RECOVERY: If WebSocket disconnects, show "Reconnecting..." with retry button
|
||||
|
||||
// @PRE: WebSocket connection is established before subscribing to task events
|
||||
// @PRE: taskId passed to openDrawerForTask() is a valid UUID string
|
||||
// @PRE: resourceId in updateResourceTask() matches pattern {type}:{uuid}
|
||||
|
||||
// @POST: After openDrawerForTask(taskId): isOpen === true && activeTaskId === taskId
|
||||
// @POST: After closeDrawer(): isOpen === false, activeTaskId unchanged
|
||||
// @POST: resourceTaskMap is updated when task:status events are received
|
||||
// @POST: When task status changes to SUCCESS|ERROR, entry remains in map for 5 minutes then auto-cleanup
|
||||
|
||||
// @SIDE_EFFECT: Subscribes to WebSocket events
|
||||
// @SIDE_EFFECT: May trigger browser notification for task completion
|
||||
|
||||
export const taskDrawerStore = writable({
|
||||
isOpen: false,
|
||||
activeTaskId: null,
|
||||
resourceTaskMap: {} // { resourceId: { taskId, status, startedAt } }
|
||||
});
|
||||
|
||||
export function openDrawerForTask(taskId) { /* ... */ }
|
||||
export function closeDrawer() { /* ... */ }
|
||||
export function updateResourceTask(resourceId, taskId, status) { /* ... */ }
|
||||
export function clearCompletedTasks() { /* ... */ }
|
||||
// [/DEF:TaskDrawerStore]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. ActivityStore
|
||||
|
||||
```javascript
|
||||
// [DEF:ActivityStore:Store]
|
||||
// @TIER: STANDARD
|
||||
// @SEMANTICS: activity-indicator, derived-state, task-count
|
||||
// @PURPOSE: Provide reactive count of active tasks for the navbar Activity indicator
|
||||
// @LAYER: UI
|
||||
// @RELATION: DEPENDS_ON -> TaskDrawerStore
|
||||
// @RELATION: BINDS_TO -> TopNavbar.svelte
|
||||
// @INVARIANT: activeCount is always >= 0
|
||||
// @INVARIANT: activeCount equals count of RUNNING tasks in resourceTaskMap
|
||||
|
||||
// @UX_STATE: Idle -> Badge hidden or shows "0"
|
||||
// @UX_STATE: Active-N -> Badge shows count N with pulse animation
|
||||
// @UX_STATE: Active-Many -> Badge shows "9+" when count exceeds 9
|
||||
|
||||
// @PRE: TaskDrawerStore is initialized and emitting values
|
||||
|
||||
// @POST: activeCount reflects exact count of tasks with status === 'RUNNING'
|
||||
// @POST: recentTasks contains last 5 tasks sorted by startedAt desc
|
||||
|
||||
export const activityStore = derived(
|
||||
taskDrawerStore,
|
||||
($drawer) => {
|
||||
const tasks = Object.values($drawer.resourceTaskMap);
|
||||
const activeCount = tasks.filter(t => t.status === 'RUNNING').length;
|
||||
const recentTasks = tasks
|
||||
.sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt))
|
||||
.slice(0, 5);
|
||||
return { activeCount, recentTasks };
|
||||
}
|
||||
);
|
||||
// [/DEF:ActivityStore]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Sidebar Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:Sidebar:Component] -->
|
||||
<!-- @TIER: CRITICAL -->
|
||||
<!-- @SEMANTICS: navigation, layout, responsive -->
|
||||
<!-- @PURPOSE: Persistent left sidebar with resource categories and collapsible sections -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> sidebarStore -->
|
||||
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
|
||||
<!-- @INVARIANT: Exactly one category is marked as active at all times -->
|
||||
|
||||
<!-- @UX_STATE: Idle-Expanded -> Full sidebar with text labels, 240px width -->
|
||||
<!-- @UX_STATE: Idle-Collapsed -> Icon-only mode, 64px width, tooltips visible -->
|
||||
<!-- @UX_STATE: Mobile-Hidden -> Sidebar off-screen, hamburger button visible in TopNavbar -->
|
||||
<!-- @UX_STATE: Mobile-Open -> Full overlay with backdrop, closes on outside click -->
|
||||
<!-- @UX_STATE: Category-Expanded -> Sub-items visible under category -->
|
||||
<!-- @UX_STATE: Category-Collapsed -> Sub-items hidden, chevron rotated -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Active item has highlighted background and left border accent -->
|
||||
<!-- @UX_FEEDBACK: Collapse/expand animation is 200ms ease-in-out -->
|
||||
<!-- @UX_FEEDBACK: Mobile overlay has semi-transparent backdrop -->
|
||||
|
||||
<!-- @PRE: sidebarStore is initialized -->
|
||||
<!-- @PRE: i18n translations are loaded for navigation labels -->
|
||||
|
||||
<!-- @POST: Clicking category toggles its expanded state -->
|
||||
<!-- @POST: Clicking item navigates to route and sets activeItem -->
|
||||
<!-- @POST: On mobile, clicking outside closes the sidebar -->
|
||||
|
||||
<script>
|
||||
// Props
|
||||
export let categories = [
|
||||
{ id: 'dashboards', label: 'DASHBOARDS', items: [{ path: '/dashboards', label: 'Overview' }] },
|
||||
{ id: 'datasets', label: 'DATASETS', items: [{ path: '/datasets', label: 'All Datasets' }] },
|
||||
{ id: 'storage', label: 'STORAGE', items: [{ path: '/backups', label: 'Backups' }, { path: '/repos', label: 'Repositories' }] },
|
||||
{ id: 'admin', label: 'ADMIN', items: [{ path: '/users', label: 'Users' }, { path: '/settings', label: 'Settings' }], adminOnly: true }
|
||||
];
|
||||
</script>
|
||||
<!-- [/DEF:Sidebar] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. TopNavbar Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:TopNavbar:Component] -->
|
||||
<!-- @TIER: CRITICAL -->
|
||||
<!-- @SEMANTICS: global-navigation, activity-indicator, user-menu -->
|
||||
<!-- @PURPOSE: Consistent top navigation bar with global search, activity indicator, and user menu -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> activityStore -->
|
||||
<!-- @RELATION: DEPENDS_ON -> authStore -->
|
||||
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
|
||||
<!-- @INVARIANT: Height is fixed at 64px across all states -->
|
||||
|
||||
<!-- @UX_STATE: Idle -> All elements visible, activity badge shows count or hidden -->
|
||||
<!-- @UX_STATE: UserMenu-Open -> Dropdown visible with Profile, Settings, Logout -->
|
||||
<!-- @UX_STATE: Search-Focused -> Search input has focus ring, placeholder visible -->
|
||||
<!-- @UX_STATE: Activity-Pulse -> Badge pulses when new task starts -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Clicking logo navigates to home (/dashboards) -->
|
||||
<!-- @UX_FEEDBACK: Activity badge shows count with color: gray (0), blue (running), red (error) -->
|
||||
<!-- @UX_FEEDBACK: User menu closes on outside click or Escape key -->
|
||||
|
||||
<!-- @PRE: User is authenticated -->
|
||||
<!-- @PRE: activityStore is subscribed and emitting values -->
|
||||
|
||||
<!-- @POST: Clicking Activity indicator opens TaskDrawer -->
|
||||
<!-- @POST: Clicking Logout clears auth state and redirects to /login -->
|
||||
|
||||
<script>
|
||||
// Props
|
||||
export let logoText = 'Superset Tools';
|
||||
export let searchPlaceholder = 'Search...';
|
||||
</script>
|
||||
<!-- [/DEF:TopNavbar] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. TaskDrawer Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:TaskDrawer:Component] -->
|
||||
<!-- @TIER: CRITICAL -->
|
||||
<!-- @SEMANTICS: task-monitoring, log-streaming, interactive-input -->
|
||||
<!-- @PURPOSE: Slide-out drawer for real-time task monitoring and interaction -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
|
||||
<!-- @RELATION: DEPENDS_ON -> TaskLogViewer component -->
|
||||
<!-- @RELATION: DEPENDS_ON -> WebSocket service -->
|
||||
<!-- @RELATION: BINDS_TO -> +layout.svelte -->
|
||||
<!-- @INVARIANT: Drawer width is fixed at 480px on desktop, 100% on mobile -->
|
||||
<!-- @INVARIANT: Log stream auto-scrolls to bottom unless user scrolls up -->
|
||||
|
||||
<!-- @UX_STATE: Closed -> Drawer off-screen right, no content rendered -->
|
||||
<!-- @UX_STATE: Opening -> Slide animation 150ms, content loads -->
|
||||
<!-- @UX_STATE: Open-Loading -> Spinner shown while fetching initial logs -->
|
||||
<!-- @UX_STATE: Open-Streaming -> Logs append in real-time with timestamps -->
|
||||
<!-- @UX_STATE: Open-InputRequired -> Input form overlays log area -->
|
||||
<!-- @UX_STATE: Open-Error -> Error banner at top with retry option -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Close button (X) in top-right corner -->
|
||||
<!-- @UX_FEEDBACK: Task status badge in header (color-coded) -->
|
||||
<!-- @UX_FEEDBACK: Log entries have source labels (plugin, system, user) -->
|
||||
<!-- @UX_RECOVERY: If task fails, show "View Details" button linking to full error -->
|
||||
|
||||
<!-- @PRE: taskDrawerStore.isOpen === true before rendering content -->
|
||||
<!-- @PRE: WebSocket connection is active for real-time updates -->
|
||||
<!-- @PRE: TaskLogViewer component is available -->
|
||||
|
||||
<!-- @POST: Drawer closes when clicking X, Escape key, or outside (optional) -->
|
||||
<!-- @POST: Closing drawer does NOT cancel running task -->
|
||||
<!-- @POST: Re-opening drawer restores previous scroll position -->
|
||||
|
||||
<script>
|
||||
// Props
|
||||
export let width = 480; // px
|
||||
export let closeOnOutsideClick = false;
|
||||
</script>
|
||||
<!-- [/DEF:TaskDrawer] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. DashboardHub Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:DashboardHub:Component] -->
|
||||
<!-- @TIER: CRITICAL -->
|
||||
<!-- @SEMANTICS: resource-hub, bulk-operations, data-grid -->
|
||||
<!-- @PURPOSE: Central hub for dashboard management with bulk selection and actions -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> requestApi -->
|
||||
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
|
||||
<!-- @RELATION: CALLS -> GET /api/dashboards -->
|
||||
<!-- @RELATION: DISPATCHES -> MigrationPlugin -->
|
||||
<!-- @RELATION: DISPATCHES -> BackupPlugin -->
|
||||
<!-- @INVARIANT: Selected dashboards persist across pagination -->
|
||||
|
||||
<!-- @UX_STATE: Loading -> Skeleton loaders in grid rows -->
|
||||
<!-- @UX_STATE: Empty-NoEnv -> "Select an environment" message with selector -->
|
||||
<!-- @UX_STATE: Empty-NoData -> "No dashboards found" with refresh button -->
|
||||
<!-- @UX_STATE: Idle-Grid -> Dashboard rows with checkboxes, pagination visible -->
|
||||
<!-- @UX_STATE: Selecting -> Checkboxes checked, floating action panel appears -->
|
||||
<!-- @UX_STATE: BulkAction-Modal -> Migration or Backup modal open -->
|
||||
<!-- @UX_STATE: Task-Triggered -> TaskDrawer opens automatically -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Row hover highlights entire row -->
|
||||
<!-- @UX_FEEDBACK: Git status icon: green check (synced), orange diff (changes), gray (none) -->
|
||||
<!-- @UX_FEEDBACK: Last task status: badge with color (green=success, red=error, blue=running) -->
|
||||
<!-- @UX_FEEDBACK: Floating panel slides up from bottom when items selected -->
|
||||
<!-- @UX_RECOVERY: Failed environment connection shows error banner with retry -->
|
||||
|
||||
<!-- @PRE: User has permission plugin:migration:execute for Migrate action -->
|
||||
<!-- @PRE: User has permission plugin:backup:execute for Backup action -->
|
||||
<!-- @PRE: At least one environment is configured -->
|
||||
|
||||
<!-- @POST: Clicking status badge opens TaskDrawer with that task -->
|
||||
<!-- @POST: Bulk Migrate opens modal with source (read-only), target selector, db mappings -->
|
||||
<!-- @POST: Bulk Backup opens modal with schedule options (one-time or cron) -->
|
||||
<!-- @POST: Search filters results in real-time (debounced 300ms) -->
|
||||
<!-- @POST: Pagination preserves selected items across page changes -->
|
||||
|
||||
<script>
|
||||
// State
|
||||
let dashboards = [];
|
||||
let selectedIds = new Set();
|
||||
let currentPage = 1;
|
||||
let pageSize = 10;
|
||||
let searchQuery = '';
|
||||
let environmentId = null;
|
||||
</script>
|
||||
<!-- [/DEF:DashboardHub] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. DatasetHub Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:DatasetHub:Component] -->
|
||||
<!-- @TIER: CRITICAL -->
|
||||
<!-- @SEMANTICS: resource-hub, semantic-mapping, documentation -->
|
||||
<!-- @PURPOSE: Central hub for dataset management with column mapping and documentation generation -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> requestApi -->
|
||||
<!-- @RELATION: DEPENDS_ON -> taskDrawerStore -->
|
||||
<!-- @RELATION: CALLS -> GET /api/datasets -->
|
||||
<!-- @RELATION: DISPATCHES -> MapperPlugin -->
|
||||
<!-- @RELATION: DISPATCHES -> LLMAnalysisPlugin -->
|
||||
<!-- @INVARIANT: Mapped % is calculated as (mapped_columns / total_columns) * 100 -->
|
||||
|
||||
<!-- @UX_STATE: Loading -> Skeleton loaders in grid rows -->
|
||||
<!-- @UX_STATE: Empty-NoEnv -> "Select an environment" message -->
|
||||
<!-- @UX_STATE: Empty-NoData -> "No datasets found" -->
|
||||
<!-- @UX_STATE: Idle-Grid -> Dataset rows with metadata columns -->
|
||||
<!-- @UX_STATE: Selecting -> Floating panel with Map Columns, Generate Docs, Validate -->
|
||||
<!-- @UX_STATE: Detail-View -> Expanded row or modal with table breakdown -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Mapped % column shows progress bar + percentage text -->
|
||||
<!-- @UX_FEEDBACK: Tables column shows count of SQL tables extracted -->
|
||||
<!-- @UX_FEEDBACK: Columns column shows "X/Y" format (mapped/total) -->
|
||||
<!-- @UX_RECOVERY: Failed mapping shows error toast with "Retry" action -->
|
||||
|
||||
<!-- @PRE: User has permission plugin:mapper:execute for Map Columns -->
|
||||
<!-- @PRE: User has permission plugin:llm_analysis:execute for Generate Docs -->
|
||||
<!-- @PRE: LLM providers are configured for documentation generation -->
|
||||
|
||||
<!-- @POST: Clicking dataset name opens detail view with tables breakdown -->
|
||||
<!-- @POST: Map Columns modal shows source selection (PostgreSQL or XLSX) -->
|
||||
<!-- @POST: Generate Docs modal shows LLM provider selection and options -->
|
||||
<!-- @POST: Search filters by name, schema, and table names -->
|
||||
|
||||
<script>
|
||||
// State
|
||||
let datasets = [];
|
||||
let selectedIds = new Set();
|
||||
let currentPage = 1;
|
||||
let pageSize = 10;
|
||||
let searchQuery = '';
|
||||
let environmentId = null;
|
||||
</script>
|
||||
<!-- [/DEF:DatasetHub] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 9. Breadcrumbs Component
|
||||
|
||||
```svelte
|
||||
<!-- [DEF:Breadcrumbs:Component] -->
|
||||
<!-- @TIER: STANDARD -->
|
||||
<!-- @SEMANTICS: navigation, hierarchy, wayfinding -->
|
||||
<!-- @PURPOSE: Show page hierarchy and allow navigation to parent levels -->
|
||||
<!-- @LAYER: UI -->
|
||||
<!-- @RELATION: DEPENDS_ON -> $page store from $app/stores -->
|
||||
<!-- @INVARIANT: Home is always first item, current page is last item -->
|
||||
|
||||
<!-- @UX_STATE: Short-Path -> All items visible (<= 3 levels) -->
|
||||
<!-- @UX_STATE: Long-Path -> Middle items truncated with ellipsis (...) -->
|
||||
|
||||
<!-- @UX_FEEDBACK: Clickable items have hover underline -->
|
||||
<!-- @UX_FEEDBACK: Current page (last item) is not clickable, shown in bold -->
|
||||
|
||||
<!-- @PRE: Route structure is defined and consistent -->
|
||||
|
||||
<!-- @POST: Clicking breadcrumb navigates to that level -->
|
||||
<!-- @POST: Deep paths (>3 levels) truncate middle segments -->
|
||||
|
||||
<script>
|
||||
// Props
|
||||
export let items = []; // [{ label: string, path: string | null }]
|
||||
export let maxVisible = 3;
|
||||
</script>
|
||||
<!-- [/DEF:Breadcrumbs] -->
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Modules
|
||||
|
||||
### 10. Dashboards API
|
||||
|
||||
```python
|
||||
# [DEF:DashboardsAPI:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @SEMANTICS: rest-api, resource-fetching, git-integration
|
||||
# @PURPOSE: API endpoints for Dashboard Hub - list dashboards with metadata
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> superset_client
|
||||
# @RELATION: DEPENDS_ON -> git_service
|
||||
# @RELATION: DEPENDS_ON -> task_manager
|
||||
# @RELATION: CALLS -> Superset API /api/v1/dashboard/
|
||||
|
||||
# @PRE: env_id parameter is valid UUID of existing environment
|
||||
# @PRE: User has permission to read dashboards from specified environment
|
||||
|
||||
# @POST: Response includes all dashboards accessible in environment
|
||||
# @POST: Each dashboard has git_status with branch and sync_status
|
||||
# @POST: Each dashboard has last_task with most recent task status
|
||||
# @POST: Search parameter filters by title and slug (case-insensitive)
|
||||
|
||||
# Endpoint: GET /api/dashboards
|
||||
# Query: env_id (required), search (optional)
|
||||
# Response: { dashboards: [...] }
|
||||
|
||||
# Endpoint: POST /api/dashboards/migrate
|
||||
# Body: { source_env_id, target_env_id, dashboard_ids, db_mappings }
|
||||
# Response: { task_id }
|
||||
|
||||
# Endpoint: POST /api/dashboards/backup
|
||||
# Body: { env_id, dashboard_ids, schedule (optional cron) }
|
||||
# Response: { task_id }
|
||||
# [/DEF:DashboardsAPI]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 11. Datasets API
|
||||
|
||||
```python
|
||||
# [DEF:DatasetsAPI:Module]
|
||||
# @TIER: CRITICAL
|
||||
# @SEMANTICS: rest-api, dataset-metadata, column-mapping
|
||||
# @PURPOSE: API endpoints for Dataset Hub - list datasets with mapping info
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> superset_client
|
||||
# @RELATION: DEPENDS_ON -> mapping_service
|
||||
# @RELATION: DEPENDS_ON -> task_manager
|
||||
# @RELATION: CALLS -> Superset API /api/v1/dataset/
|
||||
|
||||
# @PRE: env_id parameter is valid UUID of existing environment
|
||||
# @PRE: User has permission to read datasets from specified environment
|
||||
|
||||
# @POST: Response includes all datasets accessible in environment
|
||||
# @POST: Each dataset has mapped_fields count (mapped/total)
|
||||
# @POST: Each dataset has extracted SQL table names
|
||||
# @POST: Search filters by table_name, schema, and extracted table names
|
||||
|
||||
# Endpoint: GET /api/datasets
|
||||
# Query: env_id (required), search (optional)
|
||||
# Response: { datasets: [...] }
|
||||
|
||||
# Endpoint: POST /api/datasets/map-columns
|
||||
# Body: { env_id, dataset_ids, source_type, connection_id or file }
|
||||
# Response: { task_id }
|
||||
|
||||
# Endpoint: POST /api/datasets/generate-docs
|
||||
# Body: { env_id, dataset_ids, llm_provider, options }
|
||||
# Response: { task_id }
|
||||
# [/DEF:DatasetsAPI]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 12. Activity API
|
||||
|
||||
```python
|
||||
# [DEF:ActivityAPI:Module]
|
||||
# @TIER: STANDARD
|
||||
# @SEMANTICS: rest-api, activity-summary, task-metrics
|
||||
# @PURPOSE: Provide activity summary for navbar indicator
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> task_manager
|
||||
# @RELATION: CALLS -> task_manager.get_active_tasks()
|
||||
|
||||
# @PRE: User is authenticated
|
||||
|
||||
# @POST: Returns count of RUNNING tasks
|
||||
# @POST: Returns last 5 tasks sorted by started_at desc
|
||||
# @POST: Tasks include resource_name and resource_type for display
|
||||
|
||||
# Endpoint: GET /api/activity
|
||||
# Response: { active_count: number, recent_tasks: [...] }
|
||||
# [/DEF:ActivityAPI]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 13. ResourceService
|
||||
|
||||
```python
|
||||
# [DEF:ResourceService:Class]
|
||||
# @TIER: STANDARD
|
||||
# @SEMANTICS: service-layer, shared-logic, resource-fetching
|
||||
# @PURPOSE: Shared logic for fetching resources from Superset with caching
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON -> superset_client
|
||||
# @RELATION: DEPENDS_ON -> ConfigManager
|
||||
# @INVARIANT: All methods accept env_id and resolve via ConfigManager
|
||||
|
||||
# @PRE: env_id corresponds to valid Environment configuration
|
||||
# @PRE: ConfigManager returns valid Superset connection params
|
||||
|
||||
# @POST: Returns normalized resource data structure
|
||||
# @POST: Handles connection errors gracefully with meaningful messages
|
||||
# @POST: Caches results for 30 seconds to reduce API load
|
||||
|
||||
class ResourceService:
|
||||
async def fetch_dashboards(env_id: str, search: str = None) -> list[Dashboard]: ...
|
||||
async def fetch_datasets(env_id: str, search: str = None) -> list[Dataset]: ...
|
||||
async def get_git_status(env_id: str, resource_id: str) -> GitStatus: ...
|
||||
async def get_last_task(resource_type: str, resource_id: str) -> TaskSummary: ...
|
||||
# [/DEF:ResourceService]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contract Compliance Matrix
|
||||
|
||||
| Module | TIER | @PRE/@POST | @UX_STATE | @RELATION | Status |
|
||||
|--------|------|------------|-----------|-----------|--------|
|
||||
| SidebarStore | STANDARD | ✅ | ✅ | ✅ | Ready |
|
||||
| TaskDrawerStore | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| ActivityStore | STANDARD | ✅ | ✅ | ✅ | Ready |
|
||||
| Sidebar | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| TopNavbar | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| TaskDrawer | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| DashboardHub | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| DatasetHub | CRITICAL | ✅ | ✅ | ✅ | Ready |
|
||||
| Breadcrumbs | STANDARD | ✅ | ✅ | ✅ | Ready |
|
||||
| DashboardsAPI | CRITICAL | ✅ | N/A | ✅ | Ready |
|
||||
| DatasetsAPI | CRITICAL | ✅ | N/A | ✅ | Ready |
|
||||
| ActivityAPI | STANDARD | ✅ | N/A | ✅ | Ready |
|
||||
| ResourceService | STANDARD | ✅ | N/A | ✅ | Ready |
|
||||
|
||||
---
|
||||
|
||||
## UX State Mapping to Components
|
||||
|
||||
| UX State (from ux_reference.md) | Component | @UX_STATE Tag |
|
||||
|----------------------------------|-----------|---------------|
|
||||
| Sidebar Expanded | Sidebar | Idle-Expanded |
|
||||
| Sidebar Collapsed | Sidebar | Idle-Collapsed |
|
||||
| Mobile Overlay | Sidebar | Mobile-Open |
|
||||
| Task Drawer Closed | TaskDrawer | Closed |
|
||||
| Task Drawer Streaming | TaskDrawer | Open-Streaming |
|
||||
| Task Drawer Input Required | TaskDrawer | Open-InputRequired |
|
||||
| Activity Badge Active | TopNavbar | Activity-Pulse |
|
||||
| Dashboard Grid Loading | DashboardHub | Loading |
|
||||
| Dashboard Grid Empty | DashboardHub | Empty-NoData |
|
||||
| Bulk Selection Active | DashboardHub | Selecting |
|
||||
| Migration Modal Open | DashboardHub | BulkAction-Modal |
|
||||
| Dataset Grid Loading | DatasetHub | Loading |
|
||||
| Dataset Detail View | DatasetHub | Detail-View |
|
||||
|
||||
---
|
||||
|
||||
## Design-by-Contract Enforcement
|
||||
|
||||
### Critical Modules (Full Contracts Required)
|
||||
|
||||
All CRITICAL tier modules MUST have:
|
||||
- Complete `@PRE` conditions for all public functions
|
||||
- Complete `@POST` conditions guaranteeing output
|
||||
- All `@UX_STATE` transitions documented
|
||||
- `@UX_FEEDBACK` and `@UX_RECOVERY` where applicable
|
||||
- `@RELATION` tags for all dependencies
|
||||
|
||||
### Standard Modules (Basic Contracts Required)
|
||||
|
||||
All STANDARD tier modules MUST have:
|
||||
- `@PURPOSE` declaration
|
||||
- Key `@PRE` and `@POST` for main operations
|
||||
- Primary `@UX_STATE` states
|
||||
- `@RELATION` tags for major dependencies
|
||||
|
||||
---
|
||||
|
||||
## Constitution Compliance
|
||||
|
||||
| Principle | Compliance | Notes |
|
||||
|-----------|------------|-------|
|
||||
| I. Semantic Protocol | ✅ | All modules use [DEF] anchors and proper tagging |
|
||||
| II. Everything is a Plugin | ✅ | Dashboard/Dataset hubs dispatch to existing plugins |
|
||||
| III. Unified Frontend | ✅ | Uses requestApi, i18n, and component library |
|
||||
| IV. Security & Access Control | ✅ | Permission checks in @PRE conditions |
|
||||
| V. Independent Testability | ✅ | Each hub has defined independent test scenarios |
|
||||
| VI. Asynchronous Execution | ✅ | All bulk operations return task_id, use TaskManager |
|
||||
Reference in New Issue
Block a user