From ad98afbe57bbbaf097f68eb8b178e6304290be72 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 29 Jul 2026 02:31:29 +0530 Subject: [PATCH] Add Method 1 (fetch by request_id) for self/current-session data + fix latest-consent-anchor drift bug - M3HiuWorkflowService: data_fetch now supports fetch_by_request_id=1, sending request_id (not consent_id) per bridge Method 1 -- targets the current/self consent request's own session, distinct from Method 2 (consent_id, which can resolve to a stale session sharing the same artifact). - Patient::abdm_content_fetch_only(): tries Method 2 (consent_id) -> Method 1 (request_id, self data) -> Method 3 (abha_address, historical fallback) in order, stopping as soon as real documents are found. - Fixed a real pre-existing bug this surfaced: getLatestAbdmSyncSnapshot() used the same flawed 'status=SUCCESS implies COMPLETED' check (without requiring workflow_state=DATA_RECEIVED) already fixed elsewhere in computeConsentSessionDetail(). This let broad/historical data_fetch rows (status=success, but decrypted_data=[]) hijack priority-500 'best' selection and silently drift the resolved 'latest granted consent' to an unrelated historical consent_id after Method 3 fallback ran. Now requires workflow_state=DATA_RECEIVED, matching the earlier fix. --- app/Controllers/Patient.php | 44 ++++++++++++++++++++- app/Libraries/Abdm/M3HiuWorkflowService.php | 29 ++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/app/Controllers/Patient.php b/app/Controllers/Patient.php index e1873a1..1135a20 100644 --- a/app/Controllers/Patient.php +++ b/app/Controllers/Patient.php @@ -2070,6 +2070,39 @@ class Patient extends BaseController $documentsPersisted = (int) ($fetchResult['documents_persisted'] ?? 0); $documentsUpdated = (int) ($fetchResult['documents_updated'] ?? 0); + // The consent-artifact-scoped fetch above (bridge "Method 2") may resolve + // to a stale/different session sharing the same consent artifact id. Per + // the bridge gateway team, GET /v1/hiu/data/fetch?request_id=... ("Method + // 1") targets the ONE specific consent request instead -- this is the + // "self"/current session's own data. Try it before falling back to the + // broader by-ABHA-address historical lookup (Method 3). + if ($documentsPersisted + $documentsUpdated === 0 && $consentRequestRef !== '') { + try { + $requestIdFetchResult = $service->runOperation('data_fetch', [ + 'abha_address' => $abhaContext['abha_address'], + 'consentRequestId' => $consentRequestRef, + 'fetch_by_request_id' => 1, + ]); + } catch (\Throwable $e) { + $requestIdFetchResult = null; + } + + if (is_array($requestIdFetchResult) && (int) ($requestIdFetchResult['ok'] ?? 0) === 1) { + $reqDocumentsPersisted = (int) ($requestIdFetchResult['documents_persisted'] ?? 0); + $reqDocumentsUpdated = (int) ($requestIdFetchResult['documents_updated'] ?? 0); + if ($reqDocumentsPersisted + $reqDocumentsUpdated > 0) { + return $this->response->setJSON([ + 'ok' => 1, + 'phase' => 'COMPLETED', + 'message' => 'Records fetched successfully using existing granted consent.', + 'documents_persisted' => $reqDocumentsPersisted, + 'documents_updated' => $reqDocumentsUpdated, + 'data' => ['data_fetch' => $fetchResult, 'data_fetch_by_request_id' => $requestIdFetchResult], + ]); + } + } + } + // The consent-artifact-scoped fetch above (bridge "Method 1/2") only // returns data for the ONE consent/request we targeted. Per the bridge // gateway team, GET /v1/hiu/data/fetch?abha_address=... ("Method 3") @@ -3552,7 +3585,16 @@ class Patient extends BaseController $phase = 'REQUESTED'; $priority = 120; - if (($operation === 'DATA_FETCH' || $operation === 'HI_DATA_PUSH_CALLBACK') && $status === 'SUCCESS') { + // NOTE: `status` only reflects whether the bridge API call itself + // succeeded (no HTTP/transport error) -- it does NOT mean the HIP + // actually sent any health records (see M3HiuWorkflowService:: + // resolveState()/runOperation(), which always set status=success for + // any error-free response, even one reporting decrypted_data=[]). + // Whether data truly arrived is tracked separately via workflow_state + // (DATA_PENDING vs DATA_RECEIVED); require that here too, otherwise a + // broad/historical data_fetch attempt with empty decrypted_data can + // wrongly hijack "latest consent" resolution with a stale consent_id. + if (($operation === 'DATA_FETCH' || $operation === 'HI_DATA_PUSH_CALLBACK') && $status === 'SUCCESS' && $state === 'DATA_RECEIVED') { $phase = 'COMPLETED'; $priority = 500; } elseif (in_array($rawConsentStatus, ['GRANTED', 'APPROVED', 'ACTIVE'], true)) { diff --git a/app/Libraries/Abdm/M3HiuWorkflowService.php b/app/Libraries/Abdm/M3HiuWorkflowService.php index 5604e28..ebf2116 100644 --- a/app/Libraries/Abdm/M3HiuWorkflowService.php +++ b/app/Libraries/Abdm/M3HiuWorkflowService.php @@ -998,6 +998,35 @@ class M3HiuWorkflowService ]; } + // Method 1 (per bridge gateway team, 2026-07-29): GET /v1/hiu/data/fetch + // ?request_id=... fetches data for the ONE specific consent request that + // request_id belongs to -- this is the "self"/current session's own data, + // distinct from Method 2 (consent_id, which can resolve to a different/ + // stale session sharing the same consent artifact) and Method 3 (abha_address, + // which returns unrelated historical sessions across every past consent). + // Callers opt in explicitly via fetch_by_request_id=1. + $fetchByRequestId = (int) ($clean['fetch_by_request_id'] ?? 0) === 1; + if ($fetchByRequestId) { + $requestIdOnly = trim((string) ( + $clean['abdm_consent_request_id'] + ?? $clean['consentRequestId'] + ?? $clean['consent_request_id'] + ?? '' + )); + if ($requestIdOnly === '') { + return [ + 'ok' => 0, + 'http_code' => 422, + 'error_text' => 'consent_request_id is required to fetch data by request_id (Method 1).', + ]; + } + + return [ + 'ok' => 1, + 'payload' => ['request_id' => $requestIdOnly], + ]; + } + $requestFallback = ''; if (! $requestIdGenerated) { $requestFallback = trim((string) (