n8n: add backend endpoints for the RAGcore Procedure Sync workflow
GET /api/v1/integrations/n8n/procedures lists every procedure Markdown file Fleet Ops ships (all languages) with a stable per-document id and content hash, ready for workflow 3 to push into RAGcore. POST .../procedures-sync-result records the sync outcome as an idempotent audit event, matching the existing return-callback/workflow-error pattern. Extracted frontmatter parsing out of the demo knowledge provider into a shared module so both read the same source of truth.
This commit is contained in:
@@ -191,3 +191,69 @@ def test_workflow_error_bounds_summary_length(client):
|
||||
headers={"X-Service-Token": settings.n8n_callback_token},
|
||||
)
|
||||
assert response.status_code == 422
|
||||
|
||||
|
||||
def test_procedures_rejects_wrong_service_token(client):
|
||||
response = client.get(
|
||||
"/api/v1/integrations/n8n/procedures", headers={"X-Service-Token": "wrong-token"}
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_procedures_lists_every_language_with_stable_ids(client):
|
||||
settings = get_settings()
|
||||
response = client.get(
|
||||
"/api/v1/integrations/n8n/procedures",
|
||||
headers={"X-Service-Token": settings.n8n_callback_token},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
documents = response.json()["documents"]
|
||||
assert len(documents) > 0
|
||||
assert {d["language"] for d in documents} == {"en-GB", "nl-BE", "fr-BE"}
|
||||
checkout_docs = [d for d in documents if d["document_id"] == "vehicle-checkout-procedure"]
|
||||
assert len(checkout_docs) == 3 # one per language
|
||||
assert all(d["content"] and d["content_hash"] for d in checkout_docs)
|
||||
# Same document_id, different language, must not collide on id.
|
||||
assert len({d["id"] for d in checkout_docs}) == 3
|
||||
|
||||
second_response = client.get(
|
||||
"/api/v1/integrations/n8n/procedures",
|
||||
headers={"X-Service-Token": settings.n8n_callback_token},
|
||||
)
|
||||
second_ids = {d["id"] for d in second_response.json()["documents"]}
|
||||
assert second_ids == {d["id"] for d in documents} # ids are stable across requests
|
||||
|
||||
|
||||
def test_procedures_sync_result_rejects_wrong_service_token(client):
|
||||
response = client.post(
|
||||
"/api/v1/integrations/n8n/procedures-sync-result",
|
||||
json={"execution_id": str(uuid.uuid4()), "synced": 5, "failed": 0},
|
||||
headers={"X-Service-Token": "wrong-token"},
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_procedures_sync_result_registers_and_is_idempotent(client, ops_client):
|
||||
settings = get_settings()
|
||||
headers = {"X-Service-Token": settings.n8n_callback_token}
|
||||
execution_id = str(uuid.uuid4())
|
||||
body = {"execution_id": execution_id, "synced": 33, "failed": 1}
|
||||
|
||||
first = client.post(
|
||||
"/api/v1/integrations/n8n/procedures-sync-result", json=body, headers=headers
|
||||
)
|
||||
second = client.post(
|
||||
"/api/v1/integrations/n8n/procedures-sync-result", json=body, headers=headers
|
||||
)
|
||||
|
||||
assert first.status_code == 200
|
||||
assert first.json()["status"] == "registered"
|
||||
assert second.status_code == 200
|
||||
assert second.json()["status"] == "already_registered"
|
||||
|
||||
audit_events = ops_client.get(
|
||||
"/api/v1/audit", params={"action": "n8n_procedures_synced"}
|
||||
).json()
|
||||
matching = [e for e in audit_events if e["metadata"]["execution_id"] == execution_id]
|
||||
assert len(matching) == 1
|
||||
assert matching[0]["after"] == {"synced": 33, "failed": 1}
|
||||
|
||||
Reference in New Issue
Block a user