Dev Cluster E2E — Onboarding: Sequence Diagrams ครบทุก Scenario (รันจริงผ่าน API)
E2E onboarding (NEW_REQUESTOR) ขับจริงบน deployed dev cluster ผ่าน API (ข้าม FE) — happy/reject/rework/cancel ครบทุก scenario พร้อม sequence diagram + US Status ladder จาก pod log + JSON · key: OC→US return ทำงานจริง (เดิมเข้าใจผิดว่าค้าง — สาเหตุคือข้าม pickup)
อัปเดต: 2026-06-29
เอกสารนี้ = ผลรัน E2E จริงบน deployed dev cluster (gateway
gateway-dev.exim.go.th+ AKSaks-SupperApp-dev) ขับ ผ่าน API ตรง ข้าม Frontend (call API + monitor pod log) รูปแบบตาม 07-e2e-real-run-sequences — sequence diagram ต่อ scenario · ทุก scenario รัน ≥2 ครั้ง · ladder/journey สร้างจาก pod log จริง (authoritative) driver: Atlasdocs/orchestration-integration/28062026/E2E_DRIVER.py+scratchpad/e2e_full.py· JSON journal ต่อ run:scratchpad/e2e_<scenario>_<run>_<refNo>.json
TL;DR
- ✅ Full happy path → US
Finalizedขับจริงบน cluster ผ่าน API (fresh signup, OTP via Redis, domain มั่ว) — 2 runs (FX-…00104 / FX-…00105) - ✅ OC→US return ทำงานจริง — OC publish
flow-progress-update(InReview→Reviewed→InApprove) +flow-approval-result(APPROVED) กลับ US ครบ (พิสูจน์จาก OC pod log) - 🔑 ไขปริศนา “OC→US ค้าง” ของ session ก่อน: เดิมขับ #50/#53 ข้าม pickup ยิง APPROVE ตรง → saga ค้าง
AwaitingPickup→ decision โดน gate → US ค้าง. ทำ pickup→approve ครบ = เดินจบทุก rung (operator error ไม่ใช่ deployed defect) - 🔑 OTP: email/domain มั่วได้ (
@e2e-driver.example) — Entra รับ + NS stamp Redis · email ไม่ออกเลยบน dev (NS ANE gateway พัง, ดู §7) → อ่าน OTP จาก Redis - ⚠️ CreateCompanyApplication (post-approval): ต้องมี
registerDate+juristicStatusครบ (Company.CreateGuard.AgainstNullOrWhiteSpace) — ขาด → stuck ที่ Approved (= test-data artifact ไม่ใช่ defect; เติมแล้วถึง Finalized) - ✅ branch: Reject / Rework / Cancel — ดู §4 (รัน ≥2 ครั้ง/scenario)
วิธีอ่าน diagram
- Actor: Requestor (ยื่น/OTP/submit/resubmit) · Admin·Maker (pickup + APPROVE non-final / Rework / Cancel) · Admin·Approver (pickup + APPROVE final / Reject)
- เส้น:
->>= HTTP (sync) ·-->>= ASB topic (async — ลากตรงถึงผู้ consume, ชื่อ topic บนเส้น) - mark: ✅ ขับจริง+verify (pod log/API) · 🟡 best-effort/ไม่ landed · 🔴 พัง
- ขับผ่าน API ตรง (ข้าม FE): pickup =
PATCH /workflow/pickup· approve/reject =POST /workflow/{iid}/action(หรือ/steps/{sid}/action) · ไม่มี FE/SignalR
0. Connection map (bird’s-eye) — cluster จริง
flowchart LR
REQ([Requestor]):::actor
MK([Admin Maker]):::actor
AP([Admin Approver]):::actor
US[UserService]
OC[Orchestrator]
WF[Workflow]
TS[Task]
NS[Notification]
RDS[(Redis)]
REQ -->|HTTP onboarding / submit| US
MK -->|HTTP pickup / approve / rework / cancel| WF
AP -->|HTTP pickup / approve / reject| WF
US -->|Entra signup → webhook| NS
NS -->|stamp OTP| RDS
REQ -.->|read OTP| RDS
US ==>|flow-submitted-for-approval| OC
OC -->|HTTP create| WF
WF -->|HTTP task lifecycle| TS
WF ==>|workflow-pickup / sent-to-reviewer / approval-pickup / workflow-decision| OC
OC ==>|flow-progress-update / flow-approval-result RETURN| US
WF -.->|workflow-maker-notification| NS
NS -.->|email ANE 🔴 พัง| REQ
classDef actor fill:#eee,stroke:#333;
==> = ASB topic · --> = HTTP · -.-> = best-effort/พัง
1. OC saga state machine (ที่ขับจริงตามนี้)
stateDiagram-v2
[*] --> AwaitingPickup: flow-submitted (Requestor Submit)
AwaitingPickup --> UnderReview: workflow-pickup (Maker PICKUP)
UnderReview --> UnderConsideration: sent-to-reviewer (Maker APPROVE non-final)
UnderConsideration --> InApprove: approval-pickup (Approver PICKUP)
InApprove --> Approved: workflow-decision APPROVE (Approver final)
UnderReview --> Rejected: workflow-decision REJECT
UnderConsideration --> Rejected: workflow-decision REJECT
InApprove --> Rejected: workflow-decision REJECT
UnderReview --> ReworkRequested: workflow-decision REWORK (Maker)
AwaitingPickup --> Cancelled: workflow-decision CANCEL
UnderReview --> Cancelled: workflow-decision CANCEL
Approved --> [*]
Rejected --> [*]
Cancelled --> [*]
บทเรียนหลัก: ทุก APPROVE/decision ต้องมี PICKUP ก่อน (AwaitingPickup→UnderReview ต้อง
workflow-pickup; UnderConsideration→InApprove ต้องapproval-pickup). ข้าม pickup = saga ค้าง → decision ถูก gate (= สาเหตุที่ #50/#53 session ก่อน US ค้างSubmitted)
3. ✅ Happy Path — submit → maker → approver → Finalized (verified ×2)
sequenceDiagram
autonumber
actor REQ as Requestor
participant US as UserService
participant RDS as Redis
participant OC as Orchestrator
participant WF as Workflow
participant TS as Task
actor MK as Admin·Maker
actor AP as Admin·Approver
rect rgb(232,248,232)
Note over REQ,US: LANE 1 — Submit (fresh signup, domain มั่ว)
REQ->>US: POST /onboarding/instances {NEW_REQUESTOR, req…@e2e-driver.example} ✅
REQ->>US: SaveDraft → Entra signup → refNo FX-202606-00104 ✅
US->>RDS: NS webhook stamp OTP (email ไม่ออก 🔴 ANE)
REQ->>RDS: read OtpCode ✅
REQ->>US: OTP Next ✅ → Juristic→UserMode→Designate→CompanyInfo→Signatory ✅
REQ->>US: FxRequestorTcConsentStep/Submit → SUBMITTED ✅
US-->>OC: flow-submitted-for-approval ✅
Note over OC: saga = AwaitingPickup
OC->>WF: POST /instances (create) → WF #59 ✅
WF->>TS: POST /tasks (New) ✅
end
rect rgb(232,248,232)
Note over MK,US: LANE 3 — Maker pickup + APPROVE (non-final) + RETURN progress
MK->>WF: PATCH /workflow/pickup {AppIds:[refNo], ActionBy:maker} ✅
WF->>TS: PATCH /tasks/pickup ✅
WF-->>OC: workflow-pickup (Published AppStatusChanged) ✅
Note over OC: saga = UnderReview
OC-->>US: flow-progress-update {InReview} → US=InReview ✅ (OC log)
MK->>WF: POST /workflow/{59}/action {APPROVE, AdminMaker, taskId} ✅ (504/~100s แต่ commit)
WF->>TS: POST /tasks (approver task) ✅
WF-->>OC: sent-to-reviewer (Published SentToReviewer) ✅
Note over OC: saga = UnderConsideration
OC-->>US: flow-progress-update {Reviewed} → US=Reviewed ✅ (OC log)
end
rect rgb(232,248,232)
Note over AP,US: LANE 4 — Approver pickup + APPROVE (final) + RETURN result → Finalized
AP->>WF: PATCH /workflow/pickup {AppIds:[refNo], ActionBy:approver} ✅
WF-->>OC: approval-pickup (Published ApprovalPickup) ✅
Note over OC: saga = InApprove
OC-->>US: flow-progress-update {InApprove} → US=InApprove ✅ (OC log)
AP->>WF: POST /workflow/{59}/action {APPROVE, AdminApprover, taskId} ✅ (200)
WF-->>OC: workflow-decision {APPROVE} (Published AppStatusRework|APPROVE|DecisionId) ✅
Note over OC: saga = Approved
OC-->>US: flow-approval-result {APPROVED, decisionId} → US=Approved ✅ (OC log)
US->>US: post-approval chain CreateCompanyApplication (ต้อง registerDate+juristicStatus) → Finalized ✅
end
3.1 Journey ladder — จาก OC pod log จริง (authoritative, FX-202606-00104 / flow c9f4fc36)
WF 19:14:58 Published AppStatusChanged → 59 (maker pickup)
OC 19:16:34 [Lego] flow-progress-update InReview
WF 19:16:34 Published SentToReviewer → 59 (maker APPROVE non-final)
OC 19:16:35 [Lego] flow-progress-update Reviewed
WF 19:16:55 Published ApprovalPickup → 59 (approver pickup)
OC 19:16:56 [Lego] flow-progress-update InApprove
WF 19:16:57 Published AppStatusRework | Action=APPROVE | DecisionId=635061d8… (approver APPROVE final)
OC 19:16:57 [Lego] flow-approval-result APPROVED decision=635061d8…
US Status: Submitted → InReview → Reviewed → InApprove → Approved → Finalized
US Status ladder (API poll):
) → ladder จริงครบทุก rungSubmitted → Reviewed → InApprove → Finalized— InReview ไม่ทันจับด้วย API poll (อยู่ในช่วง maker-APPROVE block ~100s) แต่ OC log ยืนยัน InReview ถูก publish (19:16
3.2 JSON ตัวอย่าง (จาก journal)
// 1) Submit (requestor) → SUBMITTED
POST /userservice-api/.../onboarding/instances/{id}/steps/FxRequestorTcConsentStep/actions/Submit
{"stepData":{"agreed":true,"tcVersion":"1.0"}}
→ 200 {"data":{"status":"SUBMITTED","refNo":"FX-202606-00104"}}
// 2) Maker PICKUP
PATCH /workflow-api/.../workflow/pickup
{"AppIds":["FX-202606-00104"],"ActionBy":"e2e-maker"}
→ 200 {"StatusCode":200,"StatusMessage":"Success"}
// 3) Maker APPROVE (non-final) — 504 ที่ ~100s แต่ WF commit (ตรวจ state)
POST /workflow-api/.../workflow/59/action
{"actionType":"APPROVE","role":"AdminMaker","taskId":55,"userId":"e2e-maker"}
→ 504 (Cloudflare; TakeAction ~100s) · state → DocStatus=Reviewed ✅
// 4) Approver APPROVE (final)
POST /workflow-api/.../workflow/59/action
{"actionType":"APPROVE","role":"AdminApprover","taskId":56,"userId":"e2e-approver"}
→ 200 {"StatusCode":200,"StatusMessage":"Success"}
// 5) US final
GET /userservice-api/.../admin/flow-instances/by-ref-no/FX-202606-00104
→ 200 {"data":{"status":"Finalized"}}
3.3 ผลรัน (×3 — 2 Finalized + 1 diagnostic)
| run | refNo | WF# | US Status ladder | ผล |
|---|---|---|---|---|
| 1 | FX-202606-00103 | 56 | Submitted→Reviewed→InApprove→Approved | 🟡 ค้าง Approved — CreateCompanyApplication stuck (payload ขาด registerDate/juristicStatus) → เผยว่าต้องเติม field |
| 2 | FX-202606-00104 | 59 | Submitted→…→Finalized | ✅ full (เติม field แล้ว) |
| 3 | FX-202606-00105 | 62 | Submitted→…→Finalized | ✅ full (reproducible) |
CreateCompanyApplication note:
CreateCompanyApplicationHandler.cs:46→Company.Create()ใช้Guard.AgainstNullOrWhiteSpace(registerDate)+(juristicStatus)→ ขาด = throw → step stuck → US ค้างApproved(retry-stuck ไม่ช่วยเพราะ data ยังขาด). = test-data artifact ไม่ใช่ dev defect · real juristic data (มี registerDate/status) ผ่าน → Finalized
4. Branch scenarios (รันจริง ≥2 ครั้ง/scenario)
4.1 — Reject (approver · terminal) → US Rejected
sequenceDiagram
autonumber
actor REQ as Requestor
participant US as UserService
participant OC as Orchestrator
participant WF as Workflow
actor MK as Admin·Maker
actor AP as Admin·Approver
Note over REQ,US: onboarding walk (OTP via Redis) → Submit → flow-submitted → WF #65
MK->>WF: PATCH /pickup {appId, maker} → Published AppStatusChanged ✅
Note over OC: saga UnderReview → US=InReview
MK->>WF: POST /{65}/action {APPROVE, AdminMaker} → Published SentToReviewer ✅
Note over OC: saga UnderConsideration → US=Reviewed
AP->>WF: PATCH /pickup {appId, approver} → Published ApprovalPickup ✅
Note over OC: saga InApprove
rect rgb(232,248,232)
Note over AP,US: ★ REJECT (approver)
AP->>WF: POST /{65}/action {REJECT, AdminApprover} → Published AppStatusRework|Action=REJECT|DecisionId ✅
Note over OC: saga Rejected
OC-->>US: flow-approval-result {REJECTED} → US=Rejected ✅
end
- WF journey (pod log, FX-…00106):
AppStatusChanged→65·SentToReviewer→65·ApprovalPickup→65·AppStatusRework | Action=REJECT | DecisionId=9c2fc392… - US ladder:
Submitted → Reviewed → Rejected(return landed: US เปลี่ยนเป็น Rejected ได้ก็ต่อเมื่อ consumeflow-approval-result REJECTED)
POST /workflow-api/.../workflow/65/action
{"actionType":"REJECT","role":"AdminApprover","taskId":61,"userId":"e2e-approver"}
→ 200 {"StatusCode":200,"StatusMessage":"Success"}
GET .../admin/flow-instances/by-ref-no/FX-202606-00106 → {"data":{"status":"Rejected"}}
| run | refNo | WF# | ladder | ผล |
|---|---|---|---|---|
| 1 | FX-202606-00106 | 65 | Submitted→Reviewed→Rejected | ✅ |
| 2 | FX-202606-00107 | 68 | Submitted→Reviewed→Rejected | ✅ |
4.2 — Rework (maker) → US Rework + WF Resubmit-200
sequenceDiagram
autonumber
actor REQ as Requestor
participant US as UserService
participant OC as Orchestrator
participant WF as Workflow
actor MK as Admin·Maker
Note over REQ,US: onboarding walk → Submit → flow-submitted → WF #80
MK->>WF: PATCH /pickup {appId, maker} → Published AppStatusChanged ✅
Note over OC: saga UnderReview → US=InReview
rect rgb(232,248,232)
Note over MK,US: ★ REWORK (maker · POST /{refNo}/rework — resolve by AppId)
MK->>WF: POST /{FX-…00111}/rework {reworkSteps:[CompanyInfoStep], taskId} → Published AppStatusRework|Action=Rework ✅
Note over OC: saga ReworkRequested
OC-->>US: flow-approval-result {CORRECTION_REQUESTED} → US=Rework ✅
end
rect rgb(255,245,230)
Note over WF: ★ WF-side resubmit เท่านั้น (label) — US-side requestor resubmit ไม่ทำ (ต้อง owner JWT)
REQ->>WF: POST /{80}/resubmit {} → 200 · WF DocStatus=ReSubmitted ✅
end
- WF journey (pod log, FX-…00111):
AppStatusChanged→80·AppStatusRework | Action=Rework | DecisionId=72b965b9… - OC return (pod log):
flow-progress-update InReview·flow-approval-result CORRECTION_REQUESTED decision=72b965b9…✅ - US ladder:
Submitted → InReview → Rework - ⚠️ resubmit leg = WF-state only (
POST /workflow/{id}/resubmitflips WF DocStatus→ReSubmitted, re-พิสูจน์ resubmit-200 fix สด) — US-side requestor resubmit + saga OnResubmit→UnderReview return ไม่ได้ทดสอบ (ต้อง requestor reopen + owner JWT, นอก scope API-only) - 🔑 rework endpoint resolve ด้วย
AppId(refNo)ไม่ใช่ numeric instanceId (ใช้/{instanceId}/reworkด้วย wf_id → 404Instance AppId .. not found)
POST /workflow-api/.../workflow/FX-202606-00111/rework
{"userId":"e2e-maker","role":"AdminMaker","taskId":76,"reworkSteps":[{"stepType":"CompanyInfoStep","note":"e2e rework"}]}
→ 200 Success · US=Rework
POST /workflow-api/.../workflow/80/resubmit {} → 200 · WF DocStatus=ReSubmitted
| run | refNo | WF# | ladder | ผล |
|---|---|---|---|---|
| 1 | FX-202606-00111 | 80 | Submitted→InReview→Rework | ✅ + resubmit-200 (WF DocStatus=ReSubmitted) |
| 2 | FX-202606-00112 | 83 | Submitted→InReview→Rework | ✅ + resubmit-200 |
4.3 — Cancel (maker) → US Cancelled · 🔴 gateway ไม่ expose /cancel
sequenceDiagram
autonumber
actor REQ as Requestor
participant US as UserService
participant OC as Orchestrator
participant WF as Workflow
actor MK as Admin·Maker
Note over REQ,US: onboarding walk → Submit → flow-submitted → WF #74
MK->>WF: PATCH /pickup {appId, maker} → Published AppStatusChanged ✅
Note over OC: saga UnderReview → US=InReview
rect rgb(232,248,232)
Note over MK,US: ★ CANCEL (maker · POST /{id}/cancel) — 🔴 gateway 404, ✅ in-cluster
MK->>WF: POST /{74}/cancel {taskId, userId, reason} → Published AppStatusRework|Action=CANCEL ✅ (in-cluster)
Note over OC: saga Cancelled
OC-->>US: flow-approval-result {CANCEL} → US=Cancelled ✅
end
- WF journey (pod log, FX-…00109):
AppStatusChanged→74·AppStatusRework | Action=CANCEL | DecisionId=f21dd3fe… - OC return (pod log):
flow-progress-update InReview·flow-approval-result CANCEL decision=f21dd3fe…✅ - US ladder:
Submitted → InReview → Cancelled - 🔴 gateway ไม่ expose
/cancel:POST gateway/workflow-api/.../workflow/{id}/cancel→ 404{"statusCode":404,"message":"Resource not found"}(format ไม่ใช่ของ WF app = gateway-level 404). route/cancelมีจริง + ทำงาน in-cluster (port-forwardsvc/workflow-service→ 200 CANCELLED) → public gateway ไม่ route/cancel(ขณะที่ /action /rework /resubmit /pickup expose ครบ) — ต้องเพิ่ม route ที่ gateway · ⚠️ ยังไม่แยกชั้นว่า APIM หรือ ingress (ต้องดู IaC routing config เพื่อ pin)
// via gateway:
POST gateway/workflow-api/.../workflow/74/cancel {...} → 404 {"statusCode":404,"message":"Resource not found"}
// in-cluster (bypass APIM):
POST 127.0.0.1:5701/api/workflow-service/v1/workflow/74/cancel
{"instanceId":74,"taskId":70,"userId":"e2e-maker","reason":"e2e cancel"}
→ 200 {"Result":{"InstanceId":74,"Status":"CANCELLED"}} · US=Cancelled
| run | refNo | WF# | ladder | ผล |
|---|---|---|---|---|
| 1 | FX-202606-00108 | 71 | Submitted→InReview→Cancelled | ✅ (in-cluster; gateway 404) |
| 2 | FX-202606-00109 | 74 | Submitted→InReview→Cancelled | ✅ (in-cluster; gateway 404) |
5. 🔑 สิ่งที่เจอระหว่างทดสอบ (สำคัญ)
| # | finding | impact |
|---|---|---|
| 1 | ทุก decision ต้อง PICKUP ก่อน (PATCH /workflow/pickup {AppIds,ActionBy}) — saga: AwaitingPickup→UnderReview ต้อง workflow-pickup, UnderConsideration→InApprove ต้อง approval-pickup | session ก่อน #50/#53 ข้าม pickup → ค้าง (เข้าใจผิดว่า OC→US พัง) — ที่จริง OC→US ทำงาน |
| 2 | gateway/APIM ไม่ expose /cancel → 404 (route ทำงาน in-cluster) | Cancel ผ่าน public API ไม่ได้ — ต้องเพิ่ม APIM operation |
| 3 | rework resolve ด้วย AppId(refNo) ไม่ใช่ numeric id | ใช้ /{wf_id}/rework → 404 |
| 4 | CreateCompanyApplication (post-approval) ต้อง registerDate+juristicStatus (Company.Create Guard) | ขาด → ค้าง Approved (test-data artifact ไม่ใช่ defect) |
| 5 | maker/approver APPROVE ได้ 504 (~100s) เพราะ block downstream — WF commit เสมอ (ตรวจ state ไม่เชื่อ HTTP code) | API poll พลาด transient rung (InReview) — pod log authoritative |
| 6 | email ไม่ออกเลยบน dev (NS ANE gateway, §7) | OTP/notification อ่าน Redis/log แทน |
6. สรุปผลรวม (11 runs)
| scenario | runs | refNo | terminal | return verified |
|---|---|---|---|---|
| Happy | 3 | FX-…00103/00104/00105 | Approved (data-stuck) / Finalized ×2 | ✅ OC flow-progress-update + flow-approval-result APPROVED |
| Reject | 2 | FX-…00106/00107 | Rejected | 🟢 inferred — US=Rejected (OC return log rolled, ไม่ได้ capture ตรง; happy/cancel/rework capture OC log ตรง) |
| Rework | 2 | FX-…00111/00112 | Rework + WF ReSubmitted | ✅ OC flow-approval-result CORRECTION_REQUESTED |
| Cancel | 2 | FX-…00108/00109 | Cancelled | ✅ OC flow-approval-result CANCEL (in-cluster) |
residue: FX-…00103 (Approved-stuck, ขาด data) + FX-…00110 (rework-fail attempt, InReview) = test artifact บน shared dev
7. OTP / Notification — ทำไม email ไม่ออก (NS ANE gateway พัง)
ดูเต็ม: Atlas docs/notification-service/29062026/OTP_EMAIL_SEND_BROKEN_DEV_ROOTCAUSE.md
- Entra → NS
auth-extension/email-otp-sendwebhook → NS stamp Redis สำเร็จ แต่ส่ง email ผ่าน ANE gateway fail/timeout ทุก recipient (gmail + domain มั่ว เหมือนกัน) → webhook คืน 500 · email ไม่ออกเลยบน dev - ทดสอบจึง อ่าน OTP จาก Redis (
DEV_Notification:otp:onboarding:{email}:{refCode}.OtpCode) ผ่าน VPN · email/domain มั่วได้ - maker/approver notification ก็ส่งไม่ออก (ANE เดียวกัน) + role-mapping ยัง NoRecipient