docs(16): create API migration phase plans (5 plans in 2 waves)
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
---
|
||||
phase: 16-api-migration
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [n8n-actions.json]
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "User can start a stopped container via Telegram and sees success message"
|
||||
- "User can stop a running container via Telegram and sees success message"
|
||||
- "User can restart a container via Telegram and sees success message"
|
||||
- "Starting an already-running container shows 'already started' (not an error)"
|
||||
- "Stopping an already-stopped container shows 'already stopped' (not an error)"
|
||||
artifacts:
|
||||
- path: "n8n-actions.json"
|
||||
provides: "Container lifecycle operations via Unraid GraphQL mutations"
|
||||
contains: "graphql"
|
||||
key_links:
|
||||
- from: "n8n-actions.json mutation nodes"
|
||||
to: "Unraid GraphQL API"
|
||||
via: "POST mutations (start, stop)"
|
||||
pattern: "mutation.*docker.*start|stop"
|
||||
- from: "GraphQL Error Handler"
|
||||
to: "Format Start/Stop/Restart Result Code nodes"
|
||||
via: "ALREADY_IN_STATE mapped to statusCode 304"
|
||||
pattern: "statusCode.*304"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Migrate n8n-actions.json from Docker socket proxy to Unraid GraphQL API for container start, stop, and restart operations.
|
||||
|
||||
Purpose: Container lifecycle actions are the second-most-used feature. This plan replaces 4 Docker API HTTP Request nodes (1 container list + 3 actions) with GraphQL equivalents, using Container ID Registry for name-to-PrefixedID translation and GraphQL Error Handler for ALREADY_IN_STATE mapping.
|
||||
|
||||
Output: n8n-actions.json with all Docker API nodes replaced by Unraid GraphQL mutations, restart implemented as sequential stop+start (no native restart mutation), error handling preserving existing statusCode 304 pattern.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/luc/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/luc/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/16-api-migration/16-RESEARCH.md
|
||||
@.planning/phases/15-infrastructure-foundation/15-01-SUMMARY.md
|
||||
@.planning/phases/15-infrastructure-foundation/15-02-SUMMARY.md
|
||||
@n8n-actions.json
|
||||
@n8n-workflow.json (for Phase 15 utility node code — Container ID Registry, GraphQL Error Handler, HTTP Template)
|
||||
@ARCHITECTURE.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Replace container list query and resolve with Container ID Registry</name>
|
||||
<files>n8n-actions.json</files>
|
||||
<action>
|
||||
Replace the container lookup flow in n8n-actions.json. Currently:
|
||||
- "Has Container ID?" IF node → "Get All Containers" HTTP Request → "Resolve Container ID" Code node
|
||||
|
||||
The current flow fetches ALL containers from Docker API, then searches by name in Code node to find the container ID. Replace with Unraid GraphQL query + Container ID Registry:
|
||||
|
||||
1. **Replace "Get All Containers"** (GET docker-socket-proxy:2375/v1.47/containers/json?all=true):
|
||||
- Change to: POST `={{ $env.UNRAID_HOST }}/graphql`
|
||||
- Body: `{"query": "query { docker { containers { id names state image } } }"}`
|
||||
- Headers: `Content-Type: application/json`, `x-api-key: ={{ $env.UNRAID_API_KEY }}`
|
||||
- Timeout: 15000ms, error handling: `continueRegularOutput`
|
||||
- Rename to "Query All Containers"
|
||||
|
||||
2. **Add GraphQL Response Normalizer** Code node after the HTTP Request (before Resolve Container ID). Copy normalizer logic from n8n-workflow.json utility node. This transforms GraphQL response to Docker API contract format so "Resolve Container ID" Code node works unchanged.
|
||||
|
||||
3. **Add Container ID Registry update** after normalizer — a Code node that updates the static data registry with fresh name→PrefixedID mappings. This is critical because downstream mutations need PrefixedIDs.
|
||||
|
||||
4. **Update "Resolve Container ID"** Code node: After normalization, this node already works (it searches by `Names[0]`). However, enhance it to also output the `unraidId` (PrefixedID) from the `Id` field, so downstream mutation nodes can use it directly. Add to the output: `unraidId: matched.Id` (the normalizer preserves the full PrefixedID in the `Id` field).
|
||||
|
||||
Wire: Has Container ID? (false) → Query All Containers → Normalizer → Registry Update → Resolve Container ID → Route Action
|
||||
</action>
|
||||
<verify>
|
||||
Load n8n-actions.json and verify:
|
||||
1. "Get All Containers" node replaced with GraphQL query
|
||||
2. Normalizer Code node exists between HTTP Request and Resolve Container ID
|
||||
3. Resolve Container ID outputs unraidId field
|
||||
4. No "docker-socket-proxy" in any URL
|
||||
</verify>
|
||||
<done>
|
||||
Container lookup uses Unraid GraphQL API with normalizer. Container ID Registry updated on every lookup. Resolve Container ID outputs unraidId (PrefixedID) for downstream mutations.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Replace start/stop/restart HTTP nodes with GraphQL mutations</name>
|
||||
<files>n8n-actions.json</files>
|
||||
<action>
|
||||
Replace the 3 Docker API action nodes with Unraid GraphQL mutations:
|
||||
|
||||
1. **Replace "Start Container"** (POST docker-socket-proxy:2375/v1.47/containers/{id}/start):
|
||||
- Add a **"Build Start Mutation"** Code node before the HTTP Request that constructs the GraphQL mutation body:
|
||||
```javascript
|
||||
const data = $('Route Action').item.json;
|
||||
const unraidId = data.unraidId || data.containerId;
|
||||
return { json: { query: `mutation { docker { start(id: "${unraidId}") { id state } } }` } };
|
||||
```
|
||||
- Change HTTP Request to: POST `={{ $env.UNRAID_HOST }}/graphql`, body from expression `={{ JSON.stringify({query: $json.query}) }}`
|
||||
- Headers: `Content-Type: application/json`, `x-api-key: ={{ $env.UNRAID_API_KEY }}`
|
||||
- Timeout: 15000ms, error handling: `continueRegularOutput`
|
||||
- Add **GraphQL Error Handler** Code node after HTTP Request (before Format Start Result). Copy error handler logic from n8n-workflow.json utility node. Maps `ALREADY_IN_STATE` → `{statusCode: 304}`, `NOT_FOUND` → `{statusCode: 404}`.
|
||||
- Wire: Route Action → Build Start Mutation → Start Container (HTTP) → Error Handler → Format Start Result
|
||||
|
||||
2. **Replace "Stop Container"** (POST docker-socket-proxy:2375/v1.47/containers/{id}/stop?t=10):
|
||||
- Same pattern as Start: Build Stop Mutation → HTTP Request → Error Handler → Format Stop Result
|
||||
- Mutation: `mutation { docker { stop(id: "${unraidId}") { id state } } }`
|
||||
- Timeout: 15000ms
|
||||
|
||||
3. **Replace "Restart Container"** (POST docker-socket-proxy:2375/v1.47/containers/{id}/restart?t=10):
|
||||
Unraid has NO native restart mutation. Implement as sequential stop + start:
|
||||
|
||||
a. **Build Stop-for-Restart Mutation** Code node:
|
||||
```javascript
|
||||
const data = $('Route Action').item.json;
|
||||
const unraidId = data.unraidId || data.containerId;
|
||||
return { json: { query: `mutation { docker { stop(id: "${unraidId}") { id state } } }`, unraidId } };
|
||||
```
|
||||
b. **Stop For Restart** HTTP Request node (same config as Stop Container)
|
||||
c. **Handle Stop-for-Restart Result** Code node:
|
||||
- Check response: if success OR statusCode 304 (already stopped) → proceed to start
|
||||
- If error → fail restart
|
||||
```javascript
|
||||
const response = $input.item.json;
|
||||
const prevData = $('Build Stop-for-Restart Mutation').item.json;
|
||||
if (response.statusCode && response.statusCode !== 304 && !response.data) {
|
||||
return { json: { error: true, statusCode: response.statusCode, message: 'Failed to stop container for restart' } };
|
||||
}
|
||||
return { json: { query: `mutation { docker { start(id: "${prevData.unraidId}") { id state } } }` } };
|
||||
```
|
||||
d. **Start After Stop** HTTP Request node (same config as Start Container)
|
||||
e. **Restart Error Handler** Code node (same GraphQL Error Handler logic)
|
||||
f. Wire: Route Action → Build Stop-for-Restart → Stop For Restart (HTTP) → Handle Stop-for-Restart → Start After Stop (HTTP) → Restart Error Handler → Format Restart Result
|
||||
|
||||
**Critical:** The existing "Format Restart Result" Code node checks `response.statusCode === 304` which means "already running". For restart, 304 on the start step would mean the container didn't actually stop then start — it was already running. This is correct behavior for the existing Format Restart Result node.
|
||||
|
||||
**Existing Format Start/Stop/Restart Result Code nodes remain UNCHANGED.** They already check:
|
||||
- `response.statusCode === 304` → "already in desired state"
|
||||
- `!response.message && !response.error` → success (Docker 204 No Content pattern)
|
||||
- The GraphQL Error Handler output maps to match these exact patterns.
|
||||
|
||||
Rename Docker-centric HTTP Request nodes:
|
||||
- "Start Container" → "Start Container" (keep name, just change URL/method)
|
||||
- "Stop Container" → "Stop Container" (keep name)
|
||||
- Remove old "Restart Container" single-node and replace with stop+start chain
|
||||
</action>
|
||||
<verify>
|
||||
Load n8n-actions.json and verify:
|
||||
1. Zero "docker-socket-proxy" references in any node URL
|
||||
2. Start and Stop nodes use POST to `$env.UNRAID_HOST/graphql` with mutation bodies
|
||||
3. Restart implemented as 2 HTTP Request nodes (stop then start) with intermediate error handling
|
||||
4. GraphQL Error Handler Code nodes exist after each mutation HTTP Request
|
||||
5. Format Start/Stop/Restart Result Code nodes are UNCHANGED from pre-migration
|
||||
6. All connections valid
|
||||
7. Push to n8n via API and verify HTTP 200
|
||||
</verify>
|
||||
<done>
|
||||
Container start/stop use single GraphQL mutations. Restart uses sequential stop+start with ALREADY_IN_STATE tolerance on stop step. Error Handler maps GraphQL errors to statusCode 304 pattern. Format Result nodes unchanged. Workflow pushed to n8n.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. Load n8n-actions.json and confirm zero "docker-socket-proxy" references
|
||||
2. Confirm start/stop mutations use correct GraphQL syntax
|
||||
3. Confirm restart is 2-step (stop → start) with 304 tolerance on stop
|
||||
4. Confirm GraphQL Error Handler maps ALREADY_IN_STATE to statusCode 304
|
||||
5. Confirm Format Start/Stop/Restart Result Code nodes are byte-for-byte identical to pre-migration
|
||||
6. Push to n8n and verify HTTP 200
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- n8n-actions.json has zero Docker socket proxy references
|
||||
- Start/stop operations use GraphQL mutations with Error Handler
|
||||
- Restart operates as sequential stop+start with ALREADY_IN_STATE tolerance
|
||||
- Format Result Code nodes unchanged (zero-change migration for output formatting)
|
||||
- Container ID Registry updated on container lookup
|
||||
- Workflow valid and pushed to n8n
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-api-migration/16-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user