M3 HIU: track every discovered consent artifact id per consent_request in new abdm_hiu_consent_artifacts registry, and re-poll each individually every cycle (merged fetch-by-request-id alone does not keep returning already-established artifact sessions)

This commit is contained in:
DevSoftTech Server 2026-07-28 23:47:19 +05:30
parent e783a57c4d
commit e803809506
2 changed files with 208 additions and 0 deletions

View File

@ -0,0 +1,76 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
/**
* Tracks every distinct ABDM consent ARTIFACT id discovered under a consent
* request. Needed because a multi-facility M3 consent produces one artifact
* per linked HIP facility, and the bridge's merged "fetch by consent_request_id"
* call does not reliably keep returning sessions for artifacts that were
* already individually resolved earlier (verified 2026-07-28: a previously
* "received" artifact stopped appearing in the merged sessions list, while
* still returning real data when queried directly by its own artifact id).
* So HMS must remember every artifact id it has ever seen for a consent
* request and keep polling each one individually, in addition to the merged
* request-id call (which is what discovers brand-new sibling artifacts).
*/
class CreateAbdmHiuConsentArtifacts extends Migration
{
public function up(): void
{
$this->forge->addField([
'id' => [
'type' => 'BIGINT',
'constraint' => 20,
'unsigned' => true,
'auto_increment' => true,
],
'consent_request_id' => [
'type' => 'VARCHAR',
'constraint' => 180,
],
'artifact_id' => [
'type' => 'VARCHAR',
'constraint' => 180,
],
'abha_address' => [
'type' => 'VARCHAR',
'constraint' => 120,
'null' => true,
],
'hfr_id' => [
'type' => 'VARCHAR',
'constraint' => 60,
'null' => true,
],
'last_status' => [
'type' => 'VARCHAR',
'constraint' => 40,
'null' => true,
],
'last_polled_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['consent_request_id', 'artifact_id'], 'uniq_request_artifact');
$this->forge->addKey('abha_address');
$this->forge->createTable('abdm_hiu_consent_artifacts', true);
}
public function down(): void
{
$this->forge->dropTable('abdm_hiu_consent_artifacts', true);
}
}

View File

@ -277,6 +277,46 @@ class M3HiuWorkflowService
} else {
$summary['failed']++;
}
// Merged-by-request-id fetch discovers NEW sibling artifacts, but
// does not reliably keep returning sessions for artifacts already
// established earlier (verified: an already-"received" artifact
// stopped appearing in the merged list while still returning real
// data when queried by its own artifact id). So remember every
// artifact id ever seen for this consent request, and re-poll each
// one individually every cycle too, for full coverage.
$singleArtifactId = trim((string) (
$consentResult['abdm_consent_artifact_id'] ?? ''
));
$fetchSessions = is_array($dataResult['sessions'] ?? null) ? $dataResult['sessions'] : [];
$this->recordDiscoveredArtifacts(
$resolvedConsentId,
$fetchSessions,
$payload['abha_address'],
$payload['hfr_id']
);
if ($singleArtifactId !== '' && ! $this->isGatewayRequestIdPattern($singleArtifactId)) {
$this->rememberArtifact($resolvedConsentId, $singleArtifactId, $payload['abha_address'], $payload['hfr_id']);
}
foreach ($this->getKnownArtifactIds($resolvedConsentId) as $artifactId) {
if ($artifactId === '' || $artifactId === $resolvedConsentId) {
continue;
}
$artifactPayload = $payload;
$artifactPayload['consent_id'] = $artifactId;
$artifactPayload['consent_request_id'] = $artifactId;
$artifactPayload['abdm_consent_artifact_id'] = $artifactId;
$artifactPayload['abdm_consent_request_id'] = $artifactId;
$artifactResult = $this->runOperation('data_fetch', $artifactPayload);
if ((int) ($artifactResult['ok'] ?? 0) === 1) {
$summary['data_updates']++;
$this->markArtifactPolled($resolvedConsentId, $artifactId, (string) ($artifactResult['consent_status'] ?? ''));
} else {
$summary['failed']++;
}
}
} else {
$summary['failed']++;
}
@ -285,6 +325,98 @@ class M3HiuWorkflowService
return $summary;
}
/**
* Extracts and upserts every distinct artifact id found in a data_fetch
* result's sessions[] array (each session's consent_id is typically
* "artifactId:internalRequestId" or a bare artifact id) into the
* abdm_hiu_consent_artifacts registry, so future poll cycles keep
* checking each artifact individually.
*/
private function recordDiscoveredArtifacts(string $consentRequestId, array $sessions, string $abhaAddress, string $hfrId): void
{
foreach ($sessions as $session) {
if (! is_array($session)) {
continue;
}
$rawConsentId = trim((string) ($session['consent_id'] ?? ''));
if ($rawConsentId === '') {
continue;
}
$artifactId = strpos($rawConsentId, ':') !== false
? trim(explode(':', $rawConsentId)[0])
: $rawConsentId;
if ($artifactId === '' || $this->isGatewayRequestIdPattern($artifactId)) {
continue;
}
$this->rememberArtifact($consentRequestId, $artifactId, $abhaAddress, $hfrId, (string) ($session['status'] ?? ''));
}
}
private function rememberArtifact(string $consentRequestId, string $artifactId, string $abhaAddress, string $hfrId, string $status = ''): void
{
if (! $this->db->tableExists('abdm_hiu_consent_artifacts') || $consentRequestId === '' || $artifactId === '') {
return;
}
$now = Time::now('Asia/Kolkata')->toDateTimeString();
$existing = $this->db->table('abdm_hiu_consent_artifacts')
->where('consent_request_id', $consentRequestId)
->where('artifact_id', $artifactId)
->get(1)
->getRowArray();
if (! empty($existing)) {
$update = ['updated_at' => $now];
if ($status !== '') {
$update['last_status'] = $status;
}
$this->db->table('abdm_hiu_consent_artifacts')->where('id', (int) $existing['id'])->update($update);
return;
}
$this->db->table('abdm_hiu_consent_artifacts')->insert([
'consent_request_id' => $consentRequestId,
'artifact_id' => $artifactId,
'abha_address' => $abhaAddress !== '' ? $abhaAddress : null,
'hfr_id' => $hfrId !== '' ? $hfrId : null,
'last_status' => $status !== '' ? $status : null,
'created_at' => $now,
'updated_at' => $now,
]);
}
private function markArtifactPolled(string $consentRequestId, string $artifactId, string $status): void
{
if (! $this->db->tableExists('abdm_hiu_consent_artifacts')) {
return;
}
$now = Time::now('Asia/Kolkata')->toDateTimeString();
$update = ['last_polled_at' => $now, 'updated_at' => $now];
if ($status !== '') {
$update['last_status'] = $status;
}
$this->db->table('abdm_hiu_consent_artifacts')
->where('consent_request_id', $consentRequestId)
->where('artifact_id', $artifactId)
->update($update);
}
private function getKnownArtifactIds(string $consentRequestId): array
{
if (! $this->db->tableExists('abdm_hiu_consent_artifacts') || $consentRequestId === '') {
return [];
}
$rows = $this->db->table('abdm_hiu_consent_artifacts')
->select('artifact_id')
->where('consent_request_id', $consentRequestId)
->get()
->getResultArray();
return array_values(array_unique(array_map(
static fn (array $row): string => trim((string) ($row['artifact_id'] ?? '')),
$rows
)));
}
public function listTimeline(array $filters, int $limit = 200): array
{
if (! $this->db->tableExists('abdm_hiu_workflows')) {