Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
'@Symfony' => true,
'declare_strict_types' => true,
'global_namespace_import' => ['import_classes' => true],
// PHP 7.4 : pas de virgule finale dans les listes de paramètres (PHP 8.0+).
'trailing_comma_in_multiline' => [
'elements' => ['arrays', 'arguments', 'match'],
],
])
->setFinder($finder);
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Changelog

## 0.0.9 — 2026-06-11

### Breaking changes

- **Correlation key** : `tenantId` remplacé par `instanceId` partout dans le bundle (DTO, snapshots, état opérationnel, consommation, espace disque local).
- **`OrchestrationCommand`** : `tenantId` supprimé du corps ; `correlationId` devient optionnel. Clés requises : `operation`, `appId`, `instanceId`, `idempotencyKey`, `occurredAt`.
- **`ConsumptionWebhookEvent`** : champ JSON `tenantId` → `instanceId`.
- **`ManagedInstanceResourceSnapshot`** : champ JSON `tenantId` → `instanceId` ; getter `instanceId()`.
- **Stores / managers** : paramètres et méthodes `findByTenantId()` → `findByInstanceId()` ; `load($instanceId)`, `save($instanceId, …)`, etc.
- **`InstanceOperationalStateValidator`** : exige `instance.instanceId` ; garde-fou `expected_tenant_id` supprimé (utiliser `expected_instance_id`).
- **CLI** : `--tenant-id` → `--instance-id` (`consumption:push`, `orchestration:simulate`).

### Migration

- Renommer les fichiers locaux `snapshots/{tenantId}.json` → `snapshots/{instanceId}.json` (et équivalents operational-state / receipts).
- Mettre à jour les payloads AM et les appels `pushResourceConsumption($instanceId, …)`.

## 0.0.8

Versions antérieures : voir tags Git.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "application-manager-tools/am-driver",
"description": "Symfony bundle and framework-agnostic core to integrate managed applications with Application Manager (orchestration, consumption, operational state).",
"type": "symfony-bundle",
"version": "0.0.8",
"version": "0.0.9",
"license": "MIT",
"keywords": ["application-manager", "orchestration", "symfony-bundle"],
"require": {
Expand Down
3 changes: 2 additions & 1 deletion docs/ECARTS-AM.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Alignement sur le **back AM réel** (`ApplicationManager/ApplicationManager/`) e
| `DESTROY_INSTANCE` | Émis par AM | **Non implémenté** v1 : HTTP 400 + callback `FAILED` (message explicite) |
| Route commandes | Une URL par `targetId` dans `ManagedAppIntegration` sur l’agrégat App (voir ADR0002) | **Une route** POST routée par `operation` (variante cahier § 5) |
| Paramètres Symfony `am_driver.config.<key>` | Requis par routes/services bundle | Enregistrés par `ConfigurationParameters` à l’activation du bundle |
| Lecture snapshot externe | — | `ResourceSnapshotStoreInterface::findByTenantId()` (= `load()`) |
| Lecture snapshot externe | — | `ResourceSnapshotStoreInterface::findByInstanceId()` (= `load()`) |
| Clé de corrélation locale | `instanceId` (AM) | `instanceId` uniquement (plus de `tenantId` dans les DTO / snapshots v0.0.9+) |
| Sonde route récepteur | — | `ConnectivityProbeInterface` + `HttpOrchestrationConnectivityProbe` (optionnel) |

## Politiques produit (à documenter par l’app hôte)
Expand Down
2 changes: 1 addition & 1 deletion docs/INTEGRATION-SAME-APP.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Le `OrchestrationCommandProcessor` du bundle envoie les callbacks AM après succ
use ApplicationManagerTools\AmDriver\Core\Snapshot\FileResourceSnapshotStore;

// service injecté par le bundle
$snapshot = $store->findByTenantId($tenantId); // ou load()
$snapshot = $store->findByInstanceId($instanceId); // ou load()
```

## Espace disque tenant (handlers STOP/START locaux)
Expand Down
2 changes: 1 addition & 1 deletion docs/INTEGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ Le bundle enregistre le tableau `am_driver.config` **et**, pour chaque clé de `
$publisher->pushResourceConsumption($tenantId, 'seats');
$publisher->flushPendingToAm($tenantId);
$snapshotManager->recordMeasurement($tenantId, 'seats', 12);
$stored = $resourceSnapshotStore->findByTenantId($tenantId); // lecture externe (alias de load)
$stored = $resourceSnapshotStore->findByInstanceId($instanceId); // lecture externe (alias de load)
```

## Même application Symfony qu’Application Manager
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Console/Command/CallbackSendCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$client = new AmApiClient(HttpClient::create(), new AmApiClientConfig(
(string) $input->getOption('am-url'),
'unused-consumption-token',
(string) $input->getOption('token')
(string) $input->getOption('token'),
));

$request = new OrchestrationCallbackRequest(
(string) $input->getOption('idempotency-key'),
CallbackStatus::fromString((string) $input->getOption('status')),
$input->getOption('message') ? (string) $input->getOption('message') : null
$input->getOption('message') ? (string) $input->getOption('message') : null,
);

$response = $client->reportOrchestrationCallback($request);
Expand Down
10 changes: 5 additions & 5 deletions src/Bridge/Console/Command/ConsumptionPushCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected function configure(): void
$this
->setName('consumption:push')
->setDescription('Push a consumption event to Application Manager')
->addOption('tenant-id', null, InputOption::VALUE_REQUIRED, 'tenantId')
->addOption('instance-id', null, InputOption::VALUE_REQUIRED, 'instanceId')
->addOption('resource-key', null, InputOption::VALUE_REQUIRED, 'resourceKey')
->addOption('value', null, InputOption::VALUE_REQUIRED, 'Measured value')
->addOption('source', null, InputOption::VALUE_REQUIRED, 'source', 'am-driver-cli')
Expand All @@ -36,7 +36,7 @@ protected function configure(): void
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
foreach (['tenant-id', 'resource-key', 'value', 'am-url', 'token'] as $required) {
foreach (['instance-id', 'resource-key', 'value', 'am-url', 'token'] as $required) {
if (!$input->getOption($required)) {
$io->error(sprintf('Missing --%s', $required));

Expand All @@ -55,15 +55,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$client = new AmApiClient(HttpClient::create(), new AmApiClientConfig(
(string) $input->getOption('am-url'),
(string) $input->getOption('token'),
'unused-callback-token'
'unused-callback-token',
));

$event = new ConsumptionWebhookEvent(
(string) $input->getOption('tenant-id'),
(string) $input->getOption('instance-id'),
(string) $input->getOption('resource-key'),
$value,
(string) $occurredAt,
(string) $input->getOption('source')
(string) $input->getOption('source'),
);

$response = $client->pushConsumption($event);
Expand Down
2 changes: 0 additions & 2 deletions src/Bridge/Console/Command/OrchestrationSimulateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ protected function configure(): void
->addOption('base-url', null, InputOption::VALUE_REQUIRED, 'Receptacle base URL', 'http://127.0.0.1:8099')
->addOption('path', null, InputOption::VALUE_REQUIRED, 'Command path', '/internal/am/orchestration/commands')
->addOption('token', null, InputOption::VALUE_REQUIRED, 'X-Orchestration-Command-Token', 'dev-command-token')
->addOption('tenant-id', null, InputOption::VALUE_REQUIRED, 'tenantId', 'am_ten_10000000-0000-4000-8000-000000000001')
->addOption('instance-id', null, InputOption::VALUE_REQUIRED, 'instanceId', 'am_ins_10000000-0000-4000-8000-000000000001')
->addOption('app-id', null, InputOption::VALUE_REQUIRED, 'appId', 'am_app_10000000-0000-4000-8000-000000000001')
->addOption('target-id', null, InputOption::VALUE_REQUIRED, 'targetId', 'local-receptacle');
Expand Down Expand Up @@ -54,7 +53,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'targetId' => (string) $input->getOption('target-id'),
'appId' => (string) $input->getOption('app-id'),
'instanceId' => $instanceId,
'tenantId' => (string) $input->getOption('tenant-id'),
'correlationId' => 'cli_'.bin2hex(random_bytes(8)),
'idempotencyKey' => $instanceId.':'.strtolower($operationKey).'_instance:v1',
'occurredAt' => (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format(DateTimeInterface::ATOM),
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Console/Command/ServeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$orchestrationPath,
$host,
$port,
$statePath
$statePath,
));

$cmd = sprintf(
Expand All @@ -72,7 +72,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
escapeshellarg($host),
escapeshellarg($port),
escapeshellarg($docRoot),
escapeshellarg($router)
escapeshellarg($router),
);

passthru($cmd, $exitCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public function __invoke(Request $request): JsonResponse

return new JsonResponse(
['accepted' => true, 'alreadyProcessed' => $result['alreadyProcessed']],
$result['httpStatus']
$result['httpStatus'],
);
} catch (ValidationException $e) {
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
Expand Down
1 change: 0 additions & 1 deletion src/Bridge/Symfony/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultNull()
->info('Override full path; default is derived from route_prefix.')
->end()
->scalarNode('expected_tenant_id')->defaultNull()->end()
->scalarNode('expected_instance_id')->defaultNull()->end()
->end();

Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Factory/AmApiClientConfigFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static function create(array $config): AmApiClientConfig
(string) $config['orchestration_callback_token'],
(float) $config['http_timeout'],
(int) $config['consumption_max_retries'],
(int) $config['consumption_retry_delay_ms']
(int) $config['consumption_retry_delay_ms'],
);
}
}
2 changes: 1 addition & 1 deletion src/Bridge/Symfony/Factory/ConsumptionPublisherFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class ConsumptionPublisherFactory
public static function create(
AmApiClientInterface $amApiClient,
ResourceSnapshotManager $snapshotManager,
array $config,
array $config
): ConsumptionPublisher {
return new ConsumptionPublisher($amApiClient, $snapshotManager, (string) $config['source']);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,16 @@ public static function create(
OperationalStateReceiptStoreInterface $receiptStore,
?ResourceSnapshotManager $snapshotManager,
?OperationalStateReceiverInterface $receiver,
array $config,
array $config
): OperationalStateProcessor {
$expectedTenant = $config['expected_tenant_id'] ?? null;
$expectedInstance = $config['expected_instance_id'] ?? null;

return new OperationalStateProcessor(
$store,
$receiptStore,
$snapshotManager,
$receiver,
\is_string($expectedTenant) && '' !== $expectedTenant ? $expectedTenant : null,
\is_string($expectedInstance) && '' !== $expectedInstance ? $expectedInstance : null
\is_string($expectedInstance) && '' !== $expectedInstance ? $expectedInstance : null,
);
}
}
6 changes: 3 additions & 3 deletions src/Core/Cli/ReceptacleBootstrapFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,17 @@ public static function createKernel(array $config): ReceptacleHttpKernel
new LoggingStopInstanceHandler($log),
new LoggingStartInstanceHandler($log),
new FileIdempotencyStore($dataDir.'/idempotency'),
new NoopAmApiClient()
new NoopAmApiClient(),
),
new OperationalStateProcessor(
new FileOperationalStateStore($dataDir.'/operational-state'),
new FileOperationalStateReceiptStore($dataDir.'/operational-state-receipts'),
new ResourceSnapshotManager(new FileResourceSnapshotStore($dataDir.'/snapshots', $config['source']))
new ResourceSnapshotManager(new FileResourceSnapshotStore($dataDir.'/snapshots', $config['source'])),
),
$config['orchestration_path'],
$config['operational_state_path'],
$config['token_command'],
$config['token_state']
$config['token_state'],
);
}

Expand Down
14 changes: 8 additions & 6 deletions src/Core/Cli/ReceptacleHttpKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function __construct(
string $orchestrationPath,
string $operationalStatePath,
string $orchestrationToken,
string $operationalStateToken,
string $operationalStateToken
) {
$this->orchestrationProcessor = $orchestrationProcessor;
$this->operationalStateProcessor = $operationalStateProcessor;
Expand All @@ -59,12 +59,14 @@ public function handle(string $method, string $uri, string $body, array $headers
{
$path = parse_url($uri, PHP_URL_PATH) ?: '/';

if ('POST' === $method && $path === $this->orchestrationPath) {
return $this->handleOrchestration($body, $headers);
}
if ('POST' === $method) {
if ($path === $this->orchestrationPath) {
return $this->handleOrchestration($body, $headers);
}

if ('POST' === $method && $path === $this->operationalStatePath) {
return $this->handleOperationalState($body, $headers);
if ($path === $this->operationalStatePath) {
return $this->handleOperationalState($body, $headers);
}
}

if ('GET' === $method && '/' === $path) {
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Cli/ReceptacleServerRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static function dispatch(): void
$_SERVER['REQUEST_METHOD'] ?? 'GET',
$_SERVER['REQUEST_URI'] ?? '/',
file_get_contents('php://input') ?: '',
$headers
$headers,
);

http_response_code($status);
Expand Down
22 changes: 11 additions & 11 deletions src/Core/Dto/ConsumptionWebhookEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
final class ConsumptionWebhookEvent
{
/** @var string */
private $tenantId;
private $instanceId;

/** @var string */
private $resourceKey;
Expand All @@ -28,13 +28,13 @@ final class ConsumptionWebhookEvent
* @param string|int|float $value
*/
public function __construct(
string $tenantId,
string $instanceId,
string $resourceKey,
$value,
string $occurredAt,
string $source,
string $source
) {
$this->tenantId = $tenantId;
$this->instanceId = $instanceId;
$this->resourceKey = $resourceKey;
$this->value = $value;
$this->occurredAt = $occurredAt;
Expand All @@ -46,8 +46,8 @@ public function __construct(
*/
public static function fromArray(array $data): self
{
JsonPayloadValidator::requireKeys($data, ['tenantId', 'resourceKey', 'value', 'occurredAt', 'source']);
JsonPayloadValidator::requireNonEmptyString($data, 'tenantId');
JsonPayloadValidator::requireKeys($data, ['instanceId', 'resourceKey', 'value', 'occurredAt', 'source']);
JsonPayloadValidator::requireNonEmptyString($data, 'instanceId');
JsonPayloadValidator::requireNonEmptyString($data, 'resourceKey');
JsonPayloadValidator::requireNonEmptyString($data, 'occurredAt');
JsonPayloadValidator::requireNonEmptyString($data, 'source');
Expand All @@ -57,17 +57,17 @@ public static function fromArray(array $data): self
}

return new self(
(string) $data['tenantId'],
(string) $data['instanceId'],
(string) $data['resourceKey'],
$data['value'],
(string) $data['occurredAt'],
(string) $data['source']
(string) $data['source'],
);
}

public function tenantId(): string
public function instanceId(): string
{
return $this->tenantId;
return $this->instanceId;
}

public function resourceKey(): string
Expand Down Expand Up @@ -99,7 +99,7 @@ public function source(): string
public function toArray(): array
{
return [
'tenantId' => $this->tenantId,
'instanceId' => $this->instanceId,
'resourceKey' => $this->resourceKey,
'value' => $this->value,
'occurredAt' => $this->occurredAt,
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Dto/OrchestrationCallbackRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static function fromArray(array $data): self
return new self(
(string) $data['idempotencyKey'],
CallbackStatus::fromString((string) $data['status']),
$message
$message,
);
}

Expand Down
Loading
Loading