bcquality/custom/knowledge/integration/send-an-idempotency-key-on-every-outbound-call.bad.al
Tharanga Chandrasekara 07140e2223 Add new action skills for AL testing and documentation
- Introduced `al-test-writer` to generate AL test codeunits for production objects based on TDD principles.
- Added `al-userguide-test-writer` to create test codeunits from user guide steps, mapping actions and assertions.
- Implemented `bc-extension-test-guide` to generate a comprehensive TEST_GUIDE.md for Business Central extensions, covering various categories.
- Created `bc-webclient-runner` to automate UI testing of the Business Central web client, capturing screenshots and asserting UI states.
- Developed `page-scripting-e2e` to produce a recording plan for Page Scripting, ensuring a structured approach to browser-level testing.
2026-06-07 12:26:47 +12:00

37 lines
1.7 KiB
AL

// Anti-pattern: a fresh key per attempt (and the loop would be just as broken with no key).
// Every retry looks like a brand-new request, so after an uncertain failure the receiver
// applies the side effect AGAIN. A flaky payment service produces duplicate charges exactly
// when it is least healthy.
codeunit 50151 "Outbound Caller Bad"
{
procedure Send(var IntegrationMessage: Record "Integration Message")
var
Client: HttpClient;
Request: HttpRequestMessage;
Content: HttpContent;
Headers: HttpHeaders;
Response: HttpResponseMessage;
Attempt: Integer;
begin
for Attempt := 1 to 3 do begin
Content.WriteFrom(IntegrationMessage.GetRequest());
Request.Content := Content;
Request.Method := 'POST';
Request.SetRequestUri('https://pay.contoso.com/api/charges');
Request.GetHeaders(Headers);
// BAD: a new GUID on every attempt. The key is supposed to let the receiver
// recognise a retry, but a value that changes each time is functionally NO key:
// attempt 2 and attempt 3 each look like a completely new charge request.
Headers.Add('Idempotency-Key', Format(CreateGuid()));
// The dangerous case is the UNCERTAIN failure. If attempt 1 actually reached the
// service and captured the payment, but the response was lost to a timeout, then
// Send returns false here and the loop retries. Attempt 2 carries a different key,
// so the service captures the payment a SECOND time. The customer is charged twice.
if Client.Send(Request, Response) and Response.IsSuccessStatusCode() then
exit;
end;
end;
}