- Match self-serve legacy test double invoice signature. - Wait for the edge gateway integration database before bootstrapping schema.
570 lines
21 KiB
PHP
570 lines
21 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use classes\db;
|
|
use classes\edge_gateway_manager;
|
|
use classes\edge_gateway_operation_service;
|
|
use Predis\Client as PredisClient;
|
|
use Tests\Support\Api\ApiCleanup;
|
|
use Tests\Support\Api\ApiFixtures;
|
|
use Tests\Support\Api\ApiSchemaBootstrap;
|
|
|
|
require_once dirname(__DIR__, 2) . '/Support/Api/ApiCleanup.php';
|
|
require_once dirname(__DIR__, 2) . '/Support/Api/ApiFixtures.php';
|
|
require_once dirname(__DIR__, 2) . '/Support/Api/ApiSchemaBootstrap.php';
|
|
|
|
app_require('classes/db.php');
|
|
app_require('classes/edge_gateway_manager.php');
|
|
app_require('classes/edge_gateway_operation_service.php');
|
|
|
|
it('persists install-session updates and derives gateway runtime status from heartbeats', function (): void {
|
|
$context = edge_gateway_integration_context();
|
|
|
|
try {
|
|
$department = $context['fixtures']->createDepartment([
|
|
'name' => 'Edge Integration Install Department',
|
|
]);
|
|
|
|
$token = $context['manager']->createInstallToken((int)$department['id'], 'Integration Install Token');
|
|
$claimTokenId = (int)($token['claim_token_id'] ?? 0);
|
|
|
|
$context['manager']->reportInstallTokenStatus((string)$token['token'], [
|
|
'status' => 'FAILED',
|
|
'step' => 'DOWNLOAD_FAILED',
|
|
'message' => 'The installer could not download the runtime bundle.',
|
|
'diagnostics' => [
|
|
'diag-1',
|
|
'diag-2',
|
|
'diag-3',
|
|
'diag-4',
|
|
'diag-5',
|
|
'diag-6',
|
|
'diag-7',
|
|
'diag-8',
|
|
],
|
|
]);
|
|
|
|
$failedStatus = $context['manager']->getInstallTokenStatus($claimTokenId);
|
|
|
|
expect($failedStatus)
|
|
->toHaveKey('status', 'FAILED')
|
|
->toHaveKey('step', 'DOWNLOAD_FAILED')
|
|
->toHaveKey('last_error', 'The installer could not download the runtime bundle.')
|
|
->and($failedStatus['diagnostics'] ?? [])
|
|
->toBeArray()
|
|
->toHaveCount(6);
|
|
|
|
$claimed = $context['manager']->claimGateway((string)$token['token'], 'edge-integration-host', 'php-agent-v1', [
|
|
'source' => 'integration-test',
|
|
]);
|
|
|
|
$gatewayId = (int)($claimed['gateway']['id'] ?? 0);
|
|
$agentToken = (string)($claimed['agent_token'] ?? '');
|
|
expect($gatewayId)->toBeGreaterThan(0)
|
|
->and($agentToken)->not->toBe('');
|
|
|
|
$claimedStatus = $context['manager']->getInstallTokenStatus($claimTokenId);
|
|
expect($claimedStatus)
|
|
->toHaveKey('status', 'CLAIMED')
|
|
->toHaveKey('gateway_id', $gatewayId)
|
|
->toHaveKey('last_error', null);
|
|
|
|
edge_gateway_integration_set_heartbeat_age($context['mysqli'], $context['redis'], $gatewayId, edge_gateway_manager::HEARTBEAT_DEGRADED_AFTER_SECONDS + 1);
|
|
$degraded = $context['manager']->getGateway($gatewayId);
|
|
expect($degraded)->toHaveKey('status', 'DEGRADED');
|
|
|
|
edge_gateway_integration_set_heartbeat_age($context['mysqli'], $context['redis'], $gatewayId, edge_gateway_manager::HEARTBEAT_OFFLINE_AFTER_SECONDS + 1);
|
|
$offline = $context['manager']->getGateway($gatewayId);
|
|
expect($offline)->toHaveKey('status', 'OFFLINE');
|
|
|
|
$context['manager']->recordHeartbeat($gatewayId, $agentToken, [
|
|
'status' => 'ONLINE',
|
|
'hostname' => 'edge-integration-host',
|
|
'metadata' => [
|
|
'system_metrics' => [
|
|
'cpu_percent' => 44,
|
|
],
|
|
],
|
|
]);
|
|
|
|
$online = $context['manager']->getGateway($gatewayId);
|
|
expect($online)
|
|
->toHaveKey('status', 'ONLINE')
|
|
->and($online['metadata']['system_metrics']['cpu_percent'] ?? null)
|
|
->toBe(44);
|
|
} finally {
|
|
$context['cleanup']->run();
|
|
}
|
|
});
|
|
|
|
it('assembles tasks, logs, statistics, operations, commands, and shell lifecycle state from persisted records', function (): void {
|
|
$context = edge_gateway_integration_context();
|
|
|
|
try {
|
|
$department = $context['fixtures']->createDepartment([
|
|
'name' => 'Edge Integration Runtime Department',
|
|
]);
|
|
$user = $context['fixtures']->createUser([
|
|
'display_name' => 'Edge Integration User',
|
|
]);
|
|
|
|
$token = $context['manager']->createInstallToken((int)$department['id'], 'Integration Runtime Token', (int)$user['id']);
|
|
$claimed = $context['manager']->claimGateway((string)$token['token'], 'edge-runtime-host', 'php-agent-v1');
|
|
$gatewayId = (int)($claimed['gateway']['id'] ?? 0);
|
|
$agentToken = (string)($claimed['agent_token'] ?? '');
|
|
|
|
$operation = $context['operations']->queueOperation(
|
|
$gatewayId,
|
|
edge_gateway_operation_service::TYPE_DISCOVERY,
|
|
['inventory' => edge_gateway_integration_inventory('runtime')],
|
|
(int)$user['id']
|
|
);
|
|
$operationId = (int)($operation['id'] ?? 0);
|
|
expect($operationId)->toBeGreaterThan(0);
|
|
|
|
$claimedOperation = $context['operations']->claimNextOperation($gatewayId, $agentToken, 0, 'integration-agent-1');
|
|
expect($claimedOperation)
|
|
->toBeArray()
|
|
->toHaveKey('status', 'IN_PROGRESS');
|
|
|
|
$context['operations']->appendAgentOperationEvent($gatewayId, $operationId, $agentToken, [
|
|
'level' => 'INFO',
|
|
'code' => 'DISCOVERY_RUNNING',
|
|
'message' => 'Integration discovery is executing.',
|
|
'context' => [
|
|
'progress' => 70,
|
|
],
|
|
]);
|
|
|
|
$context['operations']->completeAgentOperation($gatewayId, $operationId, $agentToken, [
|
|
'ok' => true,
|
|
'result' => [
|
|
'inventory' => edge_gateway_integration_inventory('completed'),
|
|
],
|
|
]);
|
|
|
|
$job = $context['fixtures']->createEdgeCommandJob([
|
|
'gateway_id' => $gatewayId,
|
|
'command_type' => 'GET_RELAY_STATUS',
|
|
'request' => [
|
|
'relayId' => 'relay-main',
|
|
],
|
|
'requested_by' => (int)$user['id'],
|
|
]);
|
|
$jobId = (int)($job['id'] ?? 0);
|
|
|
|
$polledCommand = $context['manager']->pollCommand($gatewayId, $agentToken, 0);
|
|
expect($polledCommand)
|
|
->toBeArray()
|
|
->toHaveKey('id', $jobId)
|
|
->toHaveKey('command_type', 'GET_RELAY_STATUS');
|
|
|
|
$commandResult = $context['manager']->submitCommandResult($gatewayId, $jobId, $agentToken, true, [
|
|
'relayId' => 'relay-main',
|
|
'online' => true,
|
|
]);
|
|
expect($commandResult)
|
|
->toHaveKey('acknowledged', true)
|
|
->and($commandResult['job']['status'] ?? null)
|
|
->toBe('COMPLETED');
|
|
|
|
$context['manager']->recordBrokerPresence(
|
|
$gatewayId,
|
|
'connected',
|
|
'broker-connection-1',
|
|
null,
|
|
['transport' => 'ws']
|
|
);
|
|
|
|
$shellSession = $context['manager']->createShellSession(
|
|
$gatewayId,
|
|
(int)$user['id'],
|
|
'Integration shell session',
|
|
120,
|
|
40,
|
|
'/opt/truckwash-edge-agent'
|
|
);
|
|
$shellToken = (string)($shellSession['token'] ?? '');
|
|
expect($shellToken)->not->toBe('');
|
|
expect($shellSession['diagnostics']['broker_presence']['connection_id'] ?? null)
|
|
->toBe('broker-connection-1');
|
|
|
|
$validatedShell = $context['manager']->validateShellSessionToken($shellToken);
|
|
expect($validatedShell)->toHaveKey('status', 'PENDING');
|
|
|
|
$openedShell = $context['manager']->markShellSessionOpened($shellToken, 'shell-connection-1');
|
|
expect($openedShell)->toHaveKey('status', 'OPEN');
|
|
|
|
$closedShell = $context['manager']->closeShellSessionByToken(
|
|
$shellToken,
|
|
"edge-shell-output\n",
|
|
'agent_exit',
|
|
[
|
|
'message' => 'Integration shell completed.',
|
|
'code' => 0,
|
|
'stage' => 'shell_active',
|
|
]
|
|
);
|
|
expect($closedShell)
|
|
->toHaveKey('status', 'COMPLETED')
|
|
->and($closedShell['transcript'] ?? null)
|
|
->toBe("edge-shell-output\n")
|
|
->and($closedShell['metadata']['close_reason'] ?? null)
|
|
->toBe('agent_exit')
|
|
->and($closedShell['metadata']['close_stage'] ?? null)
|
|
->toBe('shell_active');
|
|
|
|
$context['manager']->appendGatewayLogEntry(
|
|
$gatewayId,
|
|
'Integration log line',
|
|
'INFO',
|
|
'agent',
|
|
'BROKER',
|
|
['source' => 'integration']
|
|
);
|
|
$context['manager']->appendRelayTransportLog(
|
|
(int)$department['id'],
|
|
'/v2/devices/api/set/switch',
|
|
['id' => 'M-7', 'on' => true, 'toggle_after' => 3],
|
|
[['id' => 'M-7', 'online' => true, 'on' => true]],
|
|
'cloud',
|
|
null,
|
|
[
|
|
'module' => 'selfserve',
|
|
'reason' => 'Integration relay start',
|
|
'relay_name' => 'Roskilde Maskine',
|
|
'relay_role' => 'MACHINE',
|
|
'customer_number' => 700123,
|
|
'actor' => [
|
|
'admin_user_id' => 42,
|
|
],
|
|
]
|
|
);
|
|
|
|
$context['manager']->recordTelemetryFromBroker($gatewayId, [
|
|
'status' => 'ONLINE',
|
|
'metadata' => [
|
|
'system_metrics' => [
|
|
'cpu_percent' => 17,
|
|
'memory_mb' => 256,
|
|
],
|
|
],
|
|
'inventory' => edge_gateway_integration_inventory('telemetry'),
|
|
]);
|
|
|
|
$tasks = $context['manager']->buildGatewayTasksPage($gatewayId);
|
|
$logs = $context['manager']->buildGatewayLogsPage($gatewayId);
|
|
$statistics = $context['manager']->buildGatewayStatisticsPage($gatewayId);
|
|
|
|
expect($tasks['operations'] ?? [])
|
|
->toBeArray()
|
|
->not->toBeEmpty()
|
|
->and(($tasks['operations'][0]['status'] ?? null))
|
|
->toBe('COMPLETED');
|
|
expect($tasks['recent_commands'] ?? [])
|
|
->toBeArray()
|
|
->not->toBeEmpty()
|
|
->and(($tasks['recent_commands'][0]['status'] ?? null))
|
|
->toBe('COMPLETED');
|
|
|
|
expect(edge_gateway_integration_messages($logs['log_entries'] ?? []))
|
|
->toContain('Integration log line');
|
|
expect(edge_gateway_integration_messages($logs['relay_logs'] ?? []))
|
|
->toContain('MACHINE ON Roskilde Maskine handled by cloud via CLOUD');
|
|
expect($logs['relay_logs'][0]['context']['module_responsible'] ?? null)
|
|
->toBe('selfserve')
|
|
->and($logs['relay_logs'][0]['context']['description'] ?? null)
|
|
->toBe('MACHINE ON Roskilde Maskine handled by cloud via CLOUD')
|
|
->and($logs['relay_logs'][0]['context']['relay_name'] ?? null)
|
|
->toBe('Roskilde Maskine')
|
|
->and($logs['relay_logs'][0]['context']['relay_role'] ?? null)
|
|
->toBe('MACHINE')
|
|
->and($logs['relay_logs'][0]['context']['associated']['customer_number'] ?? null)
|
|
->toBe(700123)
|
|
->and($logs['relay_logs'][0]['context']['associated']['admin_user_id'] ?? null)
|
|
->toBe(42)
|
|
->and($logs['relay_logs'][0]['context']['reason'] ?? null)
|
|
->toBe('Integration relay start')
|
|
->and($logs['relay_logs'][0]['context']['handler'] ?? null)
|
|
->toBe('cloud')
|
|
->and($logs['relay_logs'][0]['context']['signal']['request']['id'] ?? null)
|
|
->toBe('M-7')
|
|
->and($logs['relay_logs'][0]['context']['response']['on'] ?? null)
|
|
->toBeTrue();
|
|
expect(edge_gateway_integration_messages($logs['timeline'] ?? []))
|
|
->toContain('Integration discovery is executing.');
|
|
expect(array_values(array_filter($logs['timeline'] ?? [], static fn(array $entry): bool => ($entry['type'] ?? null) === 'relay')))
|
|
->not->toBeEmpty();
|
|
expect($logs['shell_sessions'] ?? [])
|
|
->toBeArray()
|
|
->not->toBeEmpty()
|
|
->and(($logs['shell_sessions'][0]['transcript'] ?? null))
|
|
->toBe("edge-shell-output\n");
|
|
|
|
expect($statistics['system_metrics']['cpu_percent'] ?? null)
|
|
->toBe(17);
|
|
expect($statistics['gateway']['inventory'] ?? [])
|
|
->toBeArray()
|
|
->not->toBeEmpty();
|
|
} finally {
|
|
$context['cleanup']->run();
|
|
}
|
|
});
|
|
|
|
it('persists gateway cutover relay bindings used by self-serve Shelly dispatch', function (): void {
|
|
$context = edge_gateway_integration_context();
|
|
|
|
try {
|
|
$department = $context['fixtures']->createDepartment([
|
|
'name' => 'Edge Integration Self Serve Relay Department',
|
|
]);
|
|
$user = $context['fixtures']->createUser([
|
|
'display_name' => 'Edge Integration Relay User',
|
|
]);
|
|
|
|
$token = $context['manager']->createInstallToken((int)$department['id'], 'Integration Relay Token', (int)$user['id']);
|
|
$claimed = $context['manager']->claimGateway((string)$token['token'], 'edge-relay-host', 'php-agent-v1');
|
|
$gatewayId = (int)($claimed['gateway']['id'] ?? 0);
|
|
|
|
$mode = $context['manager']->setDepartmentTransportMode(
|
|
(int)$department['id'],
|
|
edge_gateway_manager::TRANSPORT_MODE_GATEWAY,
|
|
(int)$user['id']
|
|
);
|
|
$bindings = $context['manager']->setRelayBindings($gatewayId, [[
|
|
'relay_id' => 'relay-machine',
|
|
'device_id' => 'device-machine',
|
|
'local_ip' => '10.50.60.70',
|
|
'channel' => 0,
|
|
'metadata' => [
|
|
'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL,
|
|
],
|
|
]], (int)$user['id']);
|
|
$resolved = $context['manager']->resolveRelayBinding((int)$department['id'], 'relay-machine');
|
|
|
|
expect($mode)->toHaveKey('transport_mode', edge_gateway_manager::TRANSPORT_MODE_GATEWAY)
|
|
->and($bindings)->toHaveCount(1)
|
|
->and($resolved)->toMatchArray([
|
|
'gateway_id' => $gatewayId,
|
|
'department_id' => (int)$department['id'],
|
|
'relay_id' => 'relay-machine',
|
|
'device_id' => 'device-machine',
|
|
'local_ip' => '10.50.60.70',
|
|
'channel' => 0,
|
|
'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL,
|
|
])
|
|
->and((array)($resolved['metadata'] ?? []))
|
|
->toHaveKey('fallback_mode', edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL)
|
|
->and($context['manager']->getDepartmentTransportMode((int)$department['id']))
|
|
->toBe(edge_gateway_manager::TRANSPORT_MODE_GATEWAY);
|
|
} finally {
|
|
$context['cleanup']->run();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @return array{cleanup:ApiCleanup,fixtures:ApiFixtures,manager:edge_gateway_manager,operations:edge_gateway_operation_service,mysqli:mysqli,redis:?PredisClient}
|
|
*/
|
|
function edge_gateway_integration_context(): array
|
|
{
|
|
if (!integration_enabled()) {
|
|
test()->markTestSkipped('Set RUN_INTEGRATION_TESTS=1 to run edge gateway integration tests.');
|
|
}
|
|
|
|
static $bootstrapped = null;
|
|
|
|
if ($bootstrapped === null) {
|
|
$dbConfig = edge_gateway_integration_db_config();
|
|
$GLOBALS['CONFIG_DB'] = $dbConfig;
|
|
$GLOBALS['response'] = new class {
|
|
public function internal_server_error(string $message): void
|
|
{
|
|
throw new RuntimeException($message);
|
|
}
|
|
};
|
|
|
|
$db = edge_gateway_integration_wait_for_db($dbConfig);
|
|
$GLOBALS['db'] = $db;
|
|
|
|
$mysqli = $db->conn();
|
|
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
|
$mysqli->set_charset('utf8mb4');
|
|
|
|
(new ApiSchemaBootstrap($mysqli))->ensureSchema();
|
|
|
|
$bootstrapped = [
|
|
'mysqli' => $mysqli,
|
|
'redis' => edge_gateway_integration_redis_client(),
|
|
];
|
|
}
|
|
|
|
$cleanup = new ApiCleanup();
|
|
|
|
return [
|
|
'cleanup' => $cleanup,
|
|
'fixtures' => new ApiFixtures($bootstrapped['mysqli'], $bootstrapped['redis'], $cleanup),
|
|
'manager' => new edge_gateway_manager(),
|
|
'operations' => new edge_gateway_operation_service(),
|
|
'mysqli' => $bootstrapped['mysqli'],
|
|
'redis' => $bootstrapped['redis'],
|
|
];
|
|
}
|
|
|
|
function edge_gateway_integration_wait_for_db(array $dbConfig): db
|
|
{
|
|
$deadline = microtime(true) + 60;
|
|
$lastError = null;
|
|
|
|
do {
|
|
try {
|
|
$db = new db($dbConfig);
|
|
$db->connect();
|
|
return $db;
|
|
} catch (RuntimeException $exception) {
|
|
$lastError = $exception;
|
|
usleep(500000);
|
|
}
|
|
} while (microtime(true) < $deadline);
|
|
|
|
throw $lastError ?? new RuntimeException('Database connection failed before a connection attempt completed.');
|
|
}
|
|
|
|
/**
|
|
* @return array{host:string,user:string,password:string,database:string,port:int}
|
|
*/
|
|
function edge_gateway_integration_db_config(): array
|
|
{
|
|
$target = strtolower(trim((string)(getenv('CONFIG_DB_TARGET') ?: 'live')));
|
|
if ($target !== 'debug') {
|
|
$target = 'live';
|
|
}
|
|
|
|
$host = edge_gateway_integration_config_value('CONFIG_DB_HOST', 'CONFIG_DB_DEBUG_HOST', $target);
|
|
$user = edge_gateway_integration_config_value('CONFIG_DB_USER', 'CONFIG_DB_DEBUG_USER', $target);
|
|
$password = edge_gateway_integration_config_value('CONFIG_DB_PASSWORD', 'CONFIG_DB_DEBUG_PASSWORD', $target);
|
|
$database = edge_gateway_integration_config_value('CONFIG_DB_DATABASE', 'CONFIG_DB_DEBUG_DATABASE', $target);
|
|
$port = (int)(edge_gateway_integration_config_value('CONFIG_DB_PORT', 'CONFIG_DB_DEBUG_PORT', $target) ?: '3306');
|
|
|
|
if ($host === '' || $user === '' || $database === '') {
|
|
test()->markTestSkipped('Edge gateway integration tests require configured database environment variables.');
|
|
}
|
|
|
|
return [
|
|
'host' => $host,
|
|
'user' => $user,
|
|
'password' => $password,
|
|
'database' => $database,
|
|
'port' => $port > 0 ? $port : 3306,
|
|
];
|
|
}
|
|
|
|
function edge_gateway_integration_redis_client(): ?PredisClient
|
|
{
|
|
$target = strtolower(trim((string)(getenv('CONFIG_DB_TARGET') ?: 'live')));
|
|
if ($target !== 'debug') {
|
|
$target = 'live';
|
|
}
|
|
|
|
$host = edge_gateway_integration_config_value('REDIS_CONFIG_HOST', 'REDIS_CONFIG_DEBUG_HOST', $target);
|
|
if ($host === '') {
|
|
return null;
|
|
}
|
|
|
|
$parameters = [
|
|
'scheme' => 'tcp',
|
|
'host' => $host,
|
|
'port' => (int)(edge_gateway_integration_config_value('REDIS_CONFIG_PORT', 'REDIS_CONFIG_DEBUG_PORT', $target) ?: '6379'),
|
|
'database' => (int)(edge_gateway_integration_config_value('REDIS_CONFIG_DATABASE', 'REDIS_CONFIG_DEBUG_DATABASE', $target) ?: '0'),
|
|
'password' => edge_gateway_integration_config_value('REDIS_CONFIG_PASSWORD', 'REDIS_CONFIG_DEBUG_PASSWORD', $target),
|
|
];
|
|
|
|
$user = edge_gateway_integration_config_value('REDIS_CONFIG_USER', 'REDIS_CONFIG_DEBUG_USER', $target);
|
|
if ($user !== '') {
|
|
$parameters['username'] = $user;
|
|
}
|
|
|
|
return new PredisClient($parameters);
|
|
}
|
|
|
|
function edge_gateway_integration_config_value(string $liveKey, string $debugKey, string $target): string
|
|
{
|
|
$liveValue = trim((string)(getenv($liveKey) ?: ''));
|
|
$debugValue = trim((string)(getenv($debugKey) ?: ''));
|
|
|
|
if ($target === 'debug' && $debugValue !== '') {
|
|
return $debugValue;
|
|
}
|
|
|
|
return $liveValue;
|
|
}
|
|
|
|
function edge_gateway_integration_set_heartbeat_age(mysqli $mysqli, ?PredisClient $redis, int $gatewayId, int $secondsAgo): void
|
|
{
|
|
$result = $mysqli->query("SELECT last_heartbeat_at, updated_at, created_at FROM edge_gateways WHERE id = " . (int)$gatewayId . " LIMIT 1");
|
|
$row = $result instanceof mysqli_result ? $result->fetch_assoc() : null;
|
|
if ($result instanceof mysqli_result) {
|
|
$result->free();
|
|
}
|
|
|
|
$referenceTimestamp = strtotime((string)($row['last_heartbeat_at'] ?? $row['updated_at'] ?? $row['created_at'] ?? ''));
|
|
if ($referenceTimestamp === false || $referenceTimestamp <= 0) {
|
|
$referenceTimestamp = time();
|
|
}
|
|
|
|
$timestamp = date('Y-m-d H:i:s', $referenceTimestamp - max(0, $secondsAgo));
|
|
$escaped = $mysqli->real_escape_string($timestamp);
|
|
$mysqli->query("UPDATE edge_gateways SET last_heartbeat_at = '{$escaped}', status = 'ONLINE' WHERE id = " . (int)$gatewayId);
|
|
|
|
if ($redis !== null) {
|
|
foreach ([
|
|
'obj_prop:*:' . $gatewayId . ':status',
|
|
'obj_prop:*:' . $gatewayId . ':last_heartbeat_at',
|
|
'obj_prop:*:' . $gatewayId . ':updated_at',
|
|
'edge_gateway:view:v1:*',
|
|
] as $pattern) {
|
|
$keys = $redis->keys($pattern);
|
|
if (is_array($keys) && $keys !== []) {
|
|
$redis->del($keys);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function edge_gateway_integration_inventory(string $suffix): array
|
|
{
|
|
return [[
|
|
'device_id' => 'integration-' . $suffix,
|
|
'local_ip' => '10.50.60.70',
|
|
'model' => 'TruckWash Integration Gateway',
|
|
'channel_count' => 1,
|
|
'online' => true,
|
|
'capabilities' => [
|
|
'gateway_management_v2' => true,
|
|
],
|
|
'metadata' => [
|
|
'hostname' => 'integration-' . $suffix,
|
|
],
|
|
]];
|
|
}
|
|
|
|
/**
|
|
* @param mixed $rows
|
|
* @return array<int, string>
|
|
*/
|
|
function edge_gateway_integration_messages(mixed $rows): array
|
|
{
|
|
if (!is_array($rows)) {
|
|
return [];
|
|
}
|
|
|
|
$messages = [];
|
|
foreach ($rows as $row) {
|
|
if (is_array($row) && isset($row['message']) && is_string($row['message'])) {
|
|
$messages[] = $row['message'];
|
|
}
|
|
}
|
|
|
|
return $messages;
|
|
}
|