|
|
@@ -0,0 +1,266 @@
|
|
|
+# Data Factory Kestra / n8n Navigation Separation Implementation Plan
|
|
|
+
|
|
|
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
+
|
|
|
+**Goal:** Give governed Kestra production deployment and legacy n8n management independent Data Factory navigation and page lifecycles.
|
|
|
+
|
|
|
+**Architecture:** Move the Kestra deployment component out of the n8n workflow directory and expose it through a dedicated internal route. Keep the two n8n surfaces explicitly marked as legacy, preserve their current adapters, and protect the separation with source-level navigation contracts plus a production frontend build and Docker smoke test.
|
|
|
+
|
|
|
+**Tech Stack:** Vue 2, Vue Router, Vuetify, Python pytest contract tests, Docker Compose.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## File Structure
|
|
|
+
|
|
|
+- Create `tests/test_data_factory_orchestration_navigation_contract.py`: owns the engine-separation navigation contract.
|
|
|
+- Modify `tests/test_data_rule_frontend_contract.py`: points the existing governed deployment assertions at the new Kestra page.
|
|
|
+- Modify `frontend/src/router/routes.js`: defines independent Kestra and legacy n8n menu entries.
|
|
|
+- Create `frontend/src/views/dataFactory/productionLineDeployment/index.vue`: dedicated Kestra production deployment page, moved from the n8n directory.
|
|
|
+- Modify `frontend/src/views/dataFactory/workflow/index.vue`: retains only n8n workflow list, executions, and health.
|
|
|
+- Remove `frontend/src/views/dataFactory/workflow/ProductionLineDeployment.vue`: eliminates the physical page coupling.
|
|
|
+
|
|
|
+### Task 1: Add the failing navigation-separation contract
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `tests/test_data_factory_orchestration_navigation_contract.py`
|
|
|
+- Test: `tests/test_data_factory_orchestration_navigation_contract.py`
|
|
|
+
|
|
|
+- [ ] **Step 1: Write the failing contract**
|
|
|
+
|
|
|
+```python
|
|
|
+from pathlib import Path
|
|
|
+
|
|
|
+
|
|
|
+ROOT = Path(__file__).resolve().parents[1]
|
|
|
+ROUTES = ROOT / "frontend/src/router/routes.js"
|
|
|
+N8N_PAGE = ROOT / "frontend/src/views/dataFactory/workflow/index.vue"
|
|
|
+KESTRA_PAGE = ROOT / "frontend/src/views/dataFactory/productionLineDeployment/index.vue"
|
|
|
+
|
|
|
+
|
|
|
+def test_data_factory_exposes_independent_kestra_and_legacy_n8n_navigation():
|
|
|
+ routes = ROUTES.read_text(encoding="utf-8")
|
|
|
+ assert "数据生产线投产" in routes
|
|
|
+ assert "'/dataFactory/production-line-deployment'" in routes
|
|
|
+ assert "dataFactory/productionLineDeployment" in routes
|
|
|
+ assert "n8n 生产线管理(待下线)" in routes
|
|
|
+ assert "n8n 工作流管理(待下线)" in routes
|
|
|
+ assert routes.index("数据生产线投产") < routes.index("n8n 生产线管理(待下线)")
|
|
|
+
|
|
|
+
|
|
|
+def test_kestra_page_is_not_owned_by_the_n8n_workflow_page():
|
|
|
+ legacy = N8N_PAGE.read_text(encoding="utf-8")
|
|
|
+ kestra = KESTRA_PAGE.read_text(encoding="utf-8")
|
|
|
+ assert "ProductionLineDeployment" not in legacy
|
|
|
+ assert "数据生产线投产" not in legacy
|
|
|
+ assert "工作流列表" in legacy
|
|
|
+ assert "执行记录" in legacy
|
|
|
+ assert "健康检查" in legacy
|
|
|
+ assert "DATA FACTORY · GOVERNED RELEASE" in kestra
|
|
|
+ assert "Kestra revision" in kestra
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Run the contract and verify RED**
|
|
|
+
|
|
|
+Run: `PYTHONPATH=. .venv/bin/pytest -q tests/test_data_factory_orchestration_navigation_contract.py`
|
|
|
+
|
|
|
+Expected: FAIL because the dedicated Kestra route/page does not exist and the n8n page still imports `ProductionLineDeployment`.
|
|
|
+
|
|
|
+- [ ] **Step 3: Commit the RED contract**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add tests/test_data_factory_orchestration_navigation_contract.py
|
|
|
+git commit -m "test: define Kestra and n8n navigation boundary"
|
|
|
+```
|
|
|
+
|
|
|
+### Task 2: Create the dedicated Kestra page and remove the n8n tab coupling
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `frontend/src/views/dataFactory/productionLineDeployment/index.vue`
|
|
|
+- Remove: `frontend/src/views/dataFactory/workflow/ProductionLineDeployment.vue`
|
|
|
+- Modify: `frontend/src/views/dataFactory/workflow/index.vue`
|
|
|
+- Modify: `tests/test_data_rule_frontend_contract.py`
|
|
|
+
|
|
|
+- [ ] **Step 1: Move the governed deployment component**
|
|
|
+
|
|
|
+Move `frontend/src/views/dataFactory/workflow/ProductionLineDeployment.vue` byte-for-byte to `frontend/src/views/dataFactory/productionLineDeployment/index.vue`. Its imports use the `@/api/dataRules` alias, so the move does not change runtime dependencies.
|
|
|
+
|
|
|
+- [ ] **Step 2: Remove the Kestra tab from the legacy n8n page**
|
|
|
+
|
|
|
+Make `frontend/src/views/dataFactory/workflow/index.vue` contain only:
|
|
|
+
|
|
|
+```vue
|
|
|
+<v-tabs v-model="tab" @change="handleTabChange">
|
|
|
+ <v-tab>工作流列表</v-tab>
|
|
|
+ <v-tab>执行记录</v-tab>
|
|
|
+ <v-tab>健康检查</v-tab>
|
|
|
+</v-tabs>
|
|
|
+```
|
|
|
+
|
|
|
+Remove the `ProductionLineDeployment` import, component registration, and tab item. Keep `ExecutionRecord :tab="tab"` aligned with the new three-tab index.
|
|
|
+
|
|
|
+- [ ] **Step 3: Point the existing governed frontend contract at the new page**
|
|
|
+
|
|
|
+Change the constants in `tests/test_data_rule_frontend_contract.py` to:
|
|
|
+
|
|
|
+```python
|
|
|
+FACTORY = Path(
|
|
|
+ "frontend/src/views/dataFactory/productionLineDeployment/index.vue"
|
|
|
+)
|
|
|
+FACTORY_INDEX = FACTORY
|
|
|
+```
|
|
|
+
|
|
|
+The existing assertions continue to prove deployment, canary, activation, rollback, evidence, and readiness behavior.
|
|
|
+
|
|
|
+- [ ] **Step 4: Run focused tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```bash
|
|
|
+PYTHONPATH=. .venv/bin/pytest -q \
|
|
|
+ tests/test_data_factory_orchestration_navigation_contract.py \
|
|
|
+ tests/test_data_rule_frontend_contract.py
|
|
|
+```
|
|
|
+
|
|
|
+Expected: navigation test still fails only because the new router entry is absent; existing governed deployment assertions pass at the new location.
|
|
|
+
|
|
|
+### Task 3: Add independent router entries and legacy labels
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `frontend/src/router/routes.js`
|
|
|
+- Test: `tests/test_data_factory_orchestration_navigation_contract.py`
|
|
|
+
|
|
|
+- [ ] **Step 1: Insert the Kestra entry before the legacy entries**
|
|
|
+
|
|
|
+Add a Data Factory child with this contract:
|
|
|
+
|
|
|
+```javascript
|
|
|
+{
|
|
|
+ meun: '',
|
|
|
+ code: '',
|
|
|
+ hidden: 0,
|
|
|
+ rootId: 972,
|
|
|
+ icon: '',
|
|
|
+ remark: '',
|
|
|
+ type: 1,
|
|
|
+ title: '数据生产线投产',
|
|
|
+ local: '',
|
|
|
+ path: '/dataFactory/production-line-deployment',
|
|
|
+ urls: '',
|
|
|
+ children: [],
|
|
|
+ enName: 'Kestra Production Deployment',
|
|
|
+ id: 979,
|
|
|
+ redirect: '',
|
|
|
+ level: 2,
|
|
|
+ openPath: '',
|
|
|
+ active: '',
|
|
|
+ label: '数据生产线投产',
|
|
|
+ sort: 5,
|
|
|
+ parentId: 972,
|
|
|
+ effectiveStatus: true,
|
|
|
+ parentName: 'dataFactory',
|
|
|
+ component: 'dataFactory/productionLineDeployment',
|
|
|
+ meta: {
|
|
|
+ keepAlive: false,
|
|
|
+ allowClick: false,
|
|
|
+ roles: [],
|
|
|
+ enName: 'Kestra Production Deployment',
|
|
|
+ icon: '',
|
|
|
+ editModules: false,
|
|
|
+ title: '数据生产线投产',
|
|
|
+ fullScreen: false,
|
|
|
+ target: false,
|
|
|
+ effectiveStatus: true
|
|
|
+ },
|
|
|
+ name: 'productionLineDeployment',
|
|
|
+ style: '',
|
|
|
+ alwaysShow: 0,
|
|
|
+ metastr: '{"keepAlive":false,"allowClick":false,"enName":"Kestra Production Deployment","editModules":false,"title":"数据生产线投产","fullScreen":false,"target":false}',
|
|
|
+ open: null
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Rename the two n8n entries without changing behavior**
|
|
|
+
|
|
|
+Update the current external entry label/title to `n8n 生产线管理(待下线)` while retaining `process.env.VUE_APP_N8N_URL` and `target: true`.
|
|
|
+
|
|
|
+Update the current internal workflow entry label/title to `n8n 工作流管理(待下线)` while retaining `/dataFactory/workflow`, component `dataFactory/workflow`, and `target: false`.
|
|
|
+
|
|
|
+- [ ] **Step 3: Run RED-to-GREEN contract tests**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```bash
|
|
|
+PYTHONPATH=. .venv/bin/pytest -q \
|
|
|
+ tests/test_data_factory_orchestration_navigation_contract.py \
|
|
|
+ tests/test_data_rule_frontend_contract.py \
|
|
|
+ tests/test_cleanup_contract.py \
|
|
|
+ tests/test_architecture_artifacts.py
|
|
|
+```
|
|
|
+
|
|
|
+Expected: PASS.
|
|
|
+
|
|
|
+- [ ] **Step 4: Commit the implementation**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add \
|
|
|
+ frontend/src/router/routes.js \
|
|
|
+ frontend/src/views/dataFactory/workflow/index.vue \
|
|
|
+ frontend/src/views/dataFactory/productionLineDeployment/index.vue \
|
|
|
+ frontend/src/views/dataFactory/workflow/ProductionLineDeployment.vue \
|
|
|
+ tests/test_data_rule_frontend_contract.py
|
|
|
+git commit -m "feat: separate Kestra and legacy n8n navigation"
|
|
|
+```
|
|
|
+
|
|
|
+### Task 4: Build, deploy, and verify the local UAT frontend
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Verify: `frontend/`
|
|
|
+- Verify: `deploy/docker/docker-compose.yml`
|
|
|
+
|
|
|
+- [ ] **Step 1: Build the production frontend**
|
|
|
+
|
|
|
+Run: `npm --prefix frontend run build`
|
|
|
+
|
|
|
+Expected: exit 0 with a generated production bundle and no route-resolution error.
|
|
|
+
|
|
|
+- [ ] **Step 2: Rebuild and restart only the frontend service**
|
|
|
+
|
|
|
+Run: `docker compose -f deploy/docker/docker-compose.yml up -d --build frontend`
|
|
|
+
|
|
|
+Expected: frontend and its dependencies remain healthy; backend, Kestra, and n8n state are preserved.
|
|
|
+
|
|
|
+- [ ] **Step 3: Verify service health and deep links**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```bash
|
|
|
+docker compose -f deploy/docker/docker-compose.yml ps
|
|
|
+curl -fsS http://localhost:18183/dataFactory/production-line-deployment >/dev/null
|
|
|
+curl -fsS http://localhost:18183/dataFactory/workflow >/dev/null
|
|
|
+```
|
|
|
+
|
|
|
+Expected: frontend is healthy and both internal routes return the SPA document.
|
|
|
+
|
|
|
+- [ ] **Step 4: Browser acceptance**
|
|
|
+
|
|
|
+Verify in the local UAT UI:
|
|
|
+
|
|
|
+1. `数据工厂 > 数据生产线投产` opens the governed Kestra page.
|
|
|
+2. `数据工厂 > n8n 生产线管理(待下线)` opens the current n8n URL.
|
|
|
+3. `数据工厂 > n8n 工作流管理(待下线)` shows only three n8n tabs.
|
|
|
+4. Refreshing either internal deep link does not produce a 404.
|
|
|
+
|
|
|
+- [ ] **Step 5: Run final source checks and commit any generated contract update**
|
|
|
+
|
|
|
+Run:
|
|
|
+
|
|
|
+```bash
|
|
|
+git diff --check
|
|
|
+PYTHONPATH=. .venv/bin/pytest -q \
|
|
|
+ tests/test_data_factory_orchestration_navigation_contract.py \
|
|
|
+ tests/test_data_rule_frontend_contract.py \
|
|
|
+ tests/test_cleanup_contract.py \
|
|
|
+ tests/test_architecture_artifacts.py
|
|
|
+```
|
|
|
+
|
|
|
+Expected: all tests pass and `git diff --check` emits no output.
|