normalizer->supports($canonicalType); } /** * @param array $payload */ public function canBuild(string $canonicalType, array $payload): bool { if (! $this->supports($canonicalType)) { return false; } $normalized = $this->normalizer->normalize($canonicalType, $payload); return ($normalized['supported'] ?? false) === true; } /** * @param array $payload * @param array $context * @return array|null */ public function build(string $canonicalType, array $payload, array $context = []): ?array { if (! $this->supports($canonicalType)) { return null; } $normalized = $this->normalizer->normalize($canonicalType, $payload); if (($normalized['supported'] ?? false) !== true) { return null; } return match ($canonicalType) { 'transportRule' => $this->transportRuleSummary($normalized, $context), 'acceptedDomain' => $this->acceptedDomainSummary($normalized, $context), 'appPermissionPolicy' => $this->appPermissionPolicySummary($normalized, $context), 'meetingPolicy' => $this->meetingPolicySummary($normalized, $context), default => null, }; } /** * @param array $normalized * @param array $context * @return array */ private function transportRuleSummary(array $normalized, array $context): array { return $this->baseSummary('Transport rule', $normalized, $context, [ ['label' => 'Display name', 'value' => $normalized['display_name'] ?? 'Unnamed transport rule'], ['label' => 'Enabled/state', 'value' => $normalized['enabled_state'] ?? null], ['label' => 'Priority / order', 'value' => $normalized['priority_order'] ?? null], ['label' => 'Mode / enforcement', 'value' => $normalized['mode'] ?? null], ['label' => 'Conditions', 'value' => $this->settingSummary($normalized['conditions'] ?? [])], ['label' => 'Actions', 'value' => $this->settingSummary($normalized['actions'] ?? [])], ['label' => 'Exceptions', 'value' => $this->settingSummary($normalized['exceptions'] ?? [])], ]); } /** * @param array $normalized * @param array $context * @return array */ private function acceptedDomainSummary(array $normalized, array $context): array { return $this->baseSummary('Accepted domain', $normalized, $context, [ ['label' => 'Domain name', 'value' => $normalized['domain_name'] ?? 'Unnamed accepted domain'], ['label' => 'Domain type', 'value' => $normalized['domain_type'] ?? null], ['label' => 'Default domain', 'value' => $normalized['is_default'] ?? null], ['label' => 'State', 'value' => $normalized['state'] ?? null], ], displayName: $normalized['domain_name'] ?? null, state: $normalized['state'] ?? null); } /** * @param array $normalized * @param array $context * @return array */ private function appPermissionPolicySummary(array $normalized, array $context): array { return $this->baseSummary('Teams app permission policy', $normalized, $context, [ ['label' => 'Display name', 'value' => $normalized['display_name'] ?? 'Unnamed app permission policy'], ['label' => 'Policy mode', 'value' => $normalized['policy_mode'] ?? null], ['label' => 'Allowed apps', 'value' => $this->appSummary($normalized['allowed_apps'] ?? [])], ['label' => 'Blocked apps', 'value' => $this->appSummary($normalized['blocked_apps'] ?? [])], ['label' => 'Assignments / targets', 'value' => $this->settingSummary($normalized['targets'] ?? [])], ], state: $normalized['policy_mode'] ?? null); } /** * @param array $normalized * @param array $context * @return array */ private function meetingPolicySummary(array $normalized, array $context): array { return $this->baseSummary('Teams meeting policy', $normalized, $context, [ ['label' => 'Display name', 'value' => $normalized['display_name'] ?? 'Unnamed meeting policy'], ['label' => 'State', 'value' => $normalized['state'] ?? null], ['label' => 'External / anonymous access', 'value' => $this->settingSummary($normalized['external_access'] ?? [])], ['label' => 'Recording / transcription', 'value' => $this->settingSummary($normalized['recording_transcription'] ?? [])], ['label' => 'Lobby / admission', 'value' => $this->settingSummary($normalized['lobby_admission'] ?? [])], ['label' => 'Content sharing', 'value' => $this->settingSummary($normalized['content_sharing'] ?? [])], ], state: $normalized['state'] ?? null); } /** * @param array $normalized * @param array $context * @param list $fields * @return array */ private function baseSummary( string $resourceType, array $normalized, array $context, array $fields, ?string $displayName = null, ?string $state = null, ): array { return [ 'resource_type' => $resourceType, 'display_name' => $displayName ?? $this->displayName($normalized, $resourceType), 'state' => $state, 'summary_fields' => array_values(array_filter( array_map(fn (array $field): array => [ 'label' => $field['label'], 'value' => $this->summaryValue($field['value']), ], $fields), static fn (array $field): bool => filled($field['value'] ?? null), )), 'targets' => [], 'conditions' => [], 'claim_state' => $this->stringContext($context, 'claim_state'), 'identity_state' => $this->stringContext($context, 'identity_state'), 'last_captured' => $this->stringContext($context, 'last_captured'), 'unsupported_fields' => data_get($normalized, 'diagnostics.unsupported_fields', []), 'redacted_fields' => data_get($normalized, 'diagnostics.redacted_fields', []), ]; } /** * @param array $normalized */ private function displayName(array $normalized, string $fallback): string { foreach (['display_name', 'domain_name'] as $key) { $value = $normalized[$key] ?? null; if (is_scalar($value) && trim((string) $value) !== '') { return trim((string) $value); } } return 'Unnamed '.$fallback; } private function summaryValue(mixed $value): ?string { if ($value === null || $value === '' || $value === []) { return null; } if (is_bool($value)) { return $value ? 'yes' : 'no'; } if (is_scalar($value)) { $value = trim((string) $value); return $value !== '' ? $value : null; } if (is_array($value)) { return $this->stringify($value); } return null; } /** * @param array $settings */ private function settingSummary(array $settings): ?string { if ($settings === []) { return null; } $parts = []; foreach ($settings as $key => $value) { if ($this->containsRedacted($value)) { continue; } $value = $this->summaryValue($value); if ($value === null) { continue; } $parts[] = str((string) $key)->replace('_', ' ')->headline()->toString().': '.$value; } return $parts === [] ? null : implode('; ', $parts); } private function containsRedacted(mixed $value): bool { if ($value === '[redacted]') { return true; } if (! is_array($value)) { return false; } foreach ($value as $nestedValue) { if ($this->containsRedacted($nestedValue)) { return true; } } return false; } /** * @param list> $apps */ private function appSummary(array $apps): ?string { if ($apps === []) { return null; } $parts = []; foreach ($apps as $app) { if (! is_array($app)) { continue; } $display = $this->summaryValue($app['display_name'] ?? null); if ($display !== null) { $parts[] = $display; } } $parts = array_values(array_filter($parts)); return $parts === [] ? null : implode(', ', $parts); } private function stringify(mixed $value): string { if (is_bool($value)) { return $value ? 'yes' : 'no'; } if (is_scalar($value)) { return (string) $value; } return json_encode($value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } /** * @param array $context */ private function stringContext(array $context, string $key): ?string { $value = $context[$key] ?? null; if ($value instanceof \BackedEnum) { return (string) $value->value; } if (! is_scalar($value)) { return null; } $value = trim((string) $value); return $value !== '' ? $value : null; } }