## Summary - Add Spec 435 backend-only Exchange structured output envelope and target normalizer readiness helpers for transportRule, remoteDomain, and inboundConnector. - Add deterministic hash input/readiness handling and no-promotion guard coverage. - Add Spec 435 spec, plan, tasks, checklist, and implementation report artifacts. ## Validation - git diff --cached --check - cd apps/platform && php artisan test --filter=Spec435 --compact ## Product Surface / Deployment - Livewire v4 compliance unchanged. - Filament provider registration unchanged under apps/platform/bootstrap/providers.php. - Global search unchanged; no resources added or modified. - Destructive/high-impact actions: none. - Asset strategy: none; no filament:assets requirement for this slice. - Browser proof: N/A - no rendered UI surface changed. - Deployment impact: no env vars, migrations, queues, scheduler, storage, assets, or Dokploy runtime behavior changes. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #502
74 lines
2.0 KiB
PHP
74 lines
2.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\TenantConfiguration;
|
|
|
|
final class ExchangePowerShellHashInputBuilder
|
|
{
|
|
/**
|
|
* @param array<string, mixed> $normalizedPayload
|
|
* @return array<string, mixed>
|
|
*/
|
|
public function build(
|
|
string $resourceType,
|
|
string $sourceSurface,
|
|
string $commandContractName,
|
|
string $commandContractVersion,
|
|
string $payloadShapeVersion,
|
|
string $normalizerVersion,
|
|
array $normalizedPayload,
|
|
): array {
|
|
return $this->canonicalize([
|
|
'resource_type' => $resourceType,
|
|
'source_surface' => $sourceSurface,
|
|
'command_contract_name' => $commandContractName,
|
|
'command_contract_version' => $commandContractVersion,
|
|
'payload_shape_version' => $payloadShapeVersion,
|
|
'normalizer_version' => $normalizerVersion,
|
|
'normalized_payload' => $normalizedPayload,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $hashInput
|
|
*/
|
|
public function hash(array $hashInput): string
|
|
{
|
|
return hash('sha256', $this->canonicalJson($hashInput));
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $hashInput
|
|
*/
|
|
public function canonicalJson(array $hashInput): string
|
|
{
|
|
return json_encode($this->canonicalize($hashInput), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|
|
}
|
|
|
|
private function canonicalize(mixed $value): mixed
|
|
{
|
|
if (! is_array($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (array_is_list($value)) {
|
|
return array_map(fn (mixed $item): mixed => $this->canonicalize($item), $value);
|
|
}
|
|
|
|
$normalized = [];
|
|
|
|
foreach ($value as $key => $nestedValue) {
|
|
if ($nestedValue === null) {
|
|
continue;
|
|
}
|
|
|
|
$normalized[(string) $key] = $this->canonicalize($nestedValue);
|
|
}
|
|
|
|
ksort($normalized, SORT_NATURAL | SORT_FLAG_CASE);
|
|
|
|
return $normalized;
|
|
}
|
|
}
|