Create real Queue counters when specialty modules activate.
Deploy Ladill Care / deploy (push) Successful in 1m39s

Wire module activation to the Queue create/upsert APIs, persist
queue and counter UUIDs on Care stubs, and soft-deactivate on
module off so embedded service counters work without manual setup.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-07-17 16:03:16 +00:00
co-authored by Cursor
parent dec282d25d
commit cd020512c3
5 changed files with 401 additions and 20 deletions
+191 -9
View File
@@ -4,6 +4,7 @@ namespace App\Services\Queue;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class QueueClient
{
@@ -100,6 +101,79 @@ class QueueClient
return (array) ($response->json('data') ?? []);
}
/**
* @return list<array<string, mixed>>
*/
public function branches(string $owner): array
{
$response = $this->http($owner)->get($this->base().'/branches');
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public function ensureBranch(string $owner, array $payload): array
{
$response = $this->http($owner)->post($this->base().'/branches', $payload);
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public function ensureQueue(string $owner, array $payload): array
{
$response = $this->http($owner)->post($this->base().'/queues', $payload);
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public function ensureCounter(string $owner, array $payload): array
{
$response = $this->http($owner)->post($this->base().'/counters', $payload);
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* Soft-deactivate a Queue counter without destroying history.
*
* @return array<string, mixed>
*/
public function updateCounter(string $owner, string $counterUuid, array $payload): array
{
$response = $this->http($owner)->patch($this->base().'/counters/'.$counterUuid, $payload);
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* Soft-deactivate a Queue service queue without destroying history.
*
* @return array<string, mixed>
*/
public function updateQueue(string $owner, string $queueUuid, array $payload): array
{
$response = $this->http($owner)->patch($this->base().'/queues/'.$queueUuid, $payload);
$response->throw();
return (array) ($response->json('data') ?? []);
}
/**
* @return array<string, mixed>
*/
@@ -124,8 +198,8 @@ class QueueClient
}
/**
* Best-effort specialty queue sync. Care integration API is read/console-only today,
* so stubs remain Care-side until Queue exposes create-queue for service tokens.
* Create or link real Queue queues/counters for Care specialty stubs.
* Idempotent via external_key (and Queue-side name+branch fallback).
*
* @param list<array<string, mixed>> $stubs
* @return list<array<string, mixed>>
@@ -137,20 +211,128 @@ class QueueClient
}
try {
$existing = collect($this->counters($owner))
->flatMap(fn ($c) => $c['queues'] ?? [])
->map(fn ($q) => strtolower((string) ($q['name'] ?? '')))
->filter()
->all();
if (! $this->isLinked($owner)) {
return $stubs;
}
} catch (\Throwable) {
return $stubs;
}
foreach ($stubs as $i => $stub) {
$name = strtolower((string) ($stub['name'] ?? ''));
$stubs[$i]['synced'] = $name !== '' && in_array($name, $existing, true);
if (! ($stub['active'] ?? true)) {
continue;
}
try {
$stubs[$i] = $this->ensureSpecialtyStub($owner, $stub);
} catch (\Throwable $e) {
Log::warning('queue.specialty_stub_sync_failed', [
'owner' => $owner,
'stub' => $stub['name'] ?? null,
'branch' => $stub['branch_name'] ?? null,
'message' => $e->getMessage(),
]);
$stubs[$i]['synced'] = false;
}
}
return $stubs;
}
/**
* Soft-deactivate Queue resources linked on Care stubs (leaves history intact).
*
* @param list<array<string, mixed>> $stubs
* @return list<array<string, mixed>>
*/
public function deactivateSpecialtyQueueStubs(string $owner, array $stubs): array
{
if (! $this->configured() || $stubs === []) {
return $stubs;
}
foreach ($stubs as $i => $stub) {
try {
if (! empty($stub['counter_uuid'])) {
$this->updateCounter($owner, (string) $stub['counter_uuid'], ['is_active' => false]);
}
if (! empty($stub['queue_uuid'])) {
$this->updateQueue($owner, (string) $stub['queue_uuid'], ['is_active' => false]);
}
} catch (\Throwable $e) {
Log::warning('queue.specialty_stub_deactivate_failed', [
'owner' => $owner,
'counter_uuid' => $stub['counter_uuid'] ?? null,
'queue_uuid' => $stub['queue_uuid'] ?? null,
'message' => $e->getMessage(),
]);
}
$stubs[$i]['active'] = false;
}
return $stubs;
}
/**
* @param array<string, mixed> $stub
* @return array<string, mixed>
*/
protected function ensureSpecialtyStub(string $owner, array $stub): array
{
$branchName = trim((string) ($stub['branch_name'] ?? ''));
$name = trim((string) ($stub['name'] ?? ''));
$prefix = trim((string) ($stub['prefix'] ?? 'A'));
$module = (string) ($stub['module'] ?? 'specialty');
$careBranchId = (string) ($stub['branch_id'] ?? '0');
if ($branchName === '' || $name === '') {
$stub['synced'] = false;
return $stub;
}
$queueKey = (string) ($stub['queue_external_key'] ?? "care:specialty:{$module}:queue:{$careBranchId}");
$counterKey = (string) ($stub['counter_external_key'] ?? "care:specialty:{$module}:counter:{$careBranchId}");
try {
$this->ensureBranch($owner, ['name' => $branchName]);
} catch (RequestException $e) {
// Free Queue plans may already be at branch cap — continue if branch already exists.
if ($e->response?->status() !== 422) {
throw $e;
}
$existing = collect($this->branches($owner))->first(
fn ($b) => strcasecmp((string) ($b['name'] ?? ''), $branchName) === 0
);
if (! $existing) {
throw $e;
}
}
$queue = $this->ensureQueue($owner, [
'name' => $name,
'prefix' => $prefix !== '' ? $prefix : 'A',
'branch_name' => $branchName,
'strategy' => 'fifo',
'external_key' => $queueKey,
'is_active' => true,
]);
$counter = $this->ensureCounter($owner, [
'name' => $name.' counter',
'branch_name' => $branchName,
'queue_uuids' => array_values(array_filter([(string) ($queue['uuid'] ?? '')])),
'external_key' => $counterKey,
'is_active' => true,
]);
$stub['queue_external_key'] = $queueKey;
$stub['counter_external_key'] = $counterKey;
$stub['queue_uuid'] = $queue['uuid'] ?? null;
$stub['counter_uuid'] = $counter['uuid'] ?? null;
$stub['synced'] = ! empty($stub['queue_uuid']) && ! empty($stub['counter_uuid']);
return $stub;
}
}