feat/027-enrollment-config-subtypes #31
@ -496,7 +496,10 @@ private function buildEnrollmentNotificationBlock(array $snapshot): ?array
|
|||||||
foreach ([
|
foreach ([
|
||||||
'priority' => 'Priority',
|
'priority' => 'Priority',
|
||||||
'version' => 'Version',
|
'version' => 'Version',
|
||||||
|
'platformType' => 'Platform type',
|
||||||
'deviceEnrollmentConfigurationType' => 'Configuration type',
|
'deviceEnrollmentConfigurationType' => 'Configuration type',
|
||||||
|
'brandingOptions' => 'Branding options',
|
||||||
|
'templateType' => 'Template type',
|
||||||
'defaultLocale' => 'Default locale',
|
'defaultLocale' => 'Default locale',
|
||||||
'notificationMessageTemplateId' => 'Notification message template ID',
|
'notificationMessageTemplateId' => 'Notification message template ID',
|
||||||
] as $key => $label) {
|
] as $key => $label) {
|
||||||
@ -511,9 +514,71 @@ private function buildEnrollmentNotificationBlock(array $snapshot): ?array
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$notificationMessages = Arr::get($snapshot, 'notificationMessages');
|
$notificationTemplates = Arr::get($snapshot, 'notificationTemplates');
|
||||||
if (is_array($notificationMessages) && $notificationMessages !== []) {
|
if (is_array($notificationTemplates) && $notificationTemplates !== []) {
|
||||||
$entries[] = ['key' => 'Notification messages', 'value' => sprintf('%d item(s)', count($notificationMessages))];
|
$entries[] = ['key' => 'Notification templates', 'value' => array_values($notificationTemplates)];
|
||||||
|
}
|
||||||
|
|
||||||
|
$templateSnapshots = Arr::get($snapshot, 'notificationTemplateSnapshots');
|
||||||
|
if (is_array($templateSnapshots) && $templateSnapshots !== []) {
|
||||||
|
foreach ($templateSnapshots as $templateSnapshot) {
|
||||||
|
if (! is_array($templateSnapshot)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$channel = Arr::get($templateSnapshot, 'channel');
|
||||||
|
$channelLabel = is_string($channel) && $channel !== '' ? $channel : 'Template';
|
||||||
|
|
||||||
|
$templateId = Arr::get($templateSnapshot, 'template_id');
|
||||||
|
if (is_string($templateId) && $templateId !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} template ID", 'value' => $templateId];
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = Arr::get($templateSnapshot, 'template');
|
||||||
|
if (is_array($template) && $template !== []) {
|
||||||
|
$displayName = Arr::get($template, 'displayName');
|
||||||
|
if (is_string($displayName) && $displayName !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} template name", 'value' => $displayName];
|
||||||
|
}
|
||||||
|
|
||||||
|
$brandingOptions = Arr::get($template, 'brandingOptions');
|
||||||
|
if (is_string($brandingOptions) && $brandingOptions !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} branding options", 'value' => $brandingOptions];
|
||||||
|
}
|
||||||
|
|
||||||
|
$defaultLocale = Arr::get($template, 'defaultLocale');
|
||||||
|
if (is_string($defaultLocale) && $defaultLocale !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} default locale", 'value' => $defaultLocale];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$localizedMessages = Arr::get($templateSnapshot, 'localized_notification_messages');
|
||||||
|
if (is_array($localizedMessages) && $localizedMessages !== []) {
|
||||||
|
foreach ($localizedMessages as $localizedMessage) {
|
||||||
|
if (! is_array($localizedMessage)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$locale = Arr::get($localizedMessage, 'locale');
|
||||||
|
$localeLabel = is_string($locale) && $locale !== '' ? $locale : 'locale';
|
||||||
|
|
||||||
|
$subject = Arr::get($localizedMessage, 'subject');
|
||||||
|
if (is_string($subject) && $subject !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Subject", 'value' => $subject];
|
||||||
|
}
|
||||||
|
|
||||||
|
$messageTemplate = Arr::get($localizedMessage, 'messageTemplate');
|
||||||
|
if (is_string($messageTemplate) && $messageTemplate !== '') {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Message", 'value' => $messageTemplate];
|
||||||
|
}
|
||||||
|
|
||||||
|
$isDefault = Arr::get($localizedMessage, 'isDefault');
|
||||||
|
if (is_bool($isDefault)) {
|
||||||
|
$entries[] = ['key' => "{$channelLabel} ({$localeLabel}) Default", 'value' => $isDefault ? 'Enabled' : 'Disabled'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$assigned = Arr::get($snapshot, 'assignments');
|
$assigned = Arr::get($snapshot, 'assignments');
|
||||||
|
|||||||
@ -124,6 +124,15 @@ public function fetch(Tenant $tenant, Policy $policy, ?string $actorEmail = null
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($policy->policy_type === 'deviceEnrollmentNotificationConfiguration') {
|
||||||
|
[$payload, $metadata] = $this->hydrateEnrollmentNotificationTemplates(
|
||||||
|
tenantIdentifier: $tenantIdentifier,
|
||||||
|
tenant: $tenant,
|
||||||
|
payload: is_array($payload) ? $payload : [],
|
||||||
|
metadata: $metadata
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($response->failed()) {
|
if ($response->failed()) {
|
||||||
$reason = $this->formatGraphFailureReason($response);
|
$reason = $this->formatGraphFailureReason($response);
|
||||||
|
|
||||||
@ -607,6 +616,126 @@ private function hydrateComplianceActions(string $tenantIdentifier, Tenant $tena
|
|||||||
return [$payload, $metadata];
|
return [$payload, $metadata];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hydrate enrollment notifications with message template details.
|
||||||
|
*
|
||||||
|
* @return array{0:array,1:array}
|
||||||
|
*/
|
||||||
|
private function hydrateEnrollmentNotificationTemplates(string $tenantIdentifier, Tenant $tenant, array $payload, array $metadata): array
|
||||||
|
{
|
||||||
|
$existing = $payload['notificationTemplateSnapshots'] ?? null;
|
||||||
|
|
||||||
|
if (is_array($existing) && $existing !== []) {
|
||||||
|
$metadata['enrollment_notification_templates_hydration'] = 'embedded';
|
||||||
|
|
||||||
|
return [$payload, $metadata];
|
||||||
|
}
|
||||||
|
|
||||||
|
$templateRefs = $payload['notificationTemplates'] ?? null;
|
||||||
|
|
||||||
|
if (! is_array($templateRefs) || $templateRefs === []) {
|
||||||
|
$metadata['enrollment_notification_templates_hydration'] = 'none';
|
||||||
|
|
||||||
|
return [$payload, $metadata];
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
'tenant' => $tenantIdentifier,
|
||||||
|
'client_id' => $tenant->app_client_id,
|
||||||
|
'client_secret' => $tenant->app_client_secret,
|
||||||
|
];
|
||||||
|
|
||||||
|
$snapshots = [];
|
||||||
|
$failures = 0;
|
||||||
|
|
||||||
|
foreach ($templateRefs as $templateRef) {
|
||||||
|
if (! is_string($templateRef) || $templateRef === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
[$channel, $templateId] = $this->parseEnrollmentNotificationTemplateRef($templateRef);
|
||||||
|
|
||||||
|
if ($templateId === null) {
|
||||||
|
$failures++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$templatePath = sprintf('deviceManagement/notificationMessageTemplates/%s', urlencode($templateId));
|
||||||
|
$templateResponse = $this->graphClient->request('GET', $templatePath, $options);
|
||||||
|
|
||||||
|
if ($templateResponse->failed() || ! is_array($templateResponse->data)) {
|
||||||
|
$failures++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = Arr::except($templateResponse->data, ['@odata.context']);
|
||||||
|
|
||||||
|
$messagesPath = sprintf(
|
||||||
|
'deviceManagement/notificationMessageTemplates/%s/localizedNotificationMessages',
|
||||||
|
urlencode($templateId)
|
||||||
|
);
|
||||||
|
$messagesResponse = $this->graphClient->request('GET', $messagesPath, $options);
|
||||||
|
|
||||||
|
$messages = [];
|
||||||
|
|
||||||
|
if ($messagesResponse->failed()) {
|
||||||
|
$failures++;
|
||||||
|
} else {
|
||||||
|
$pageItems = $messagesResponse->data['value'] ?? [];
|
||||||
|
|
||||||
|
if (is_array($pageItems)) {
|
||||||
|
foreach ($pageItems as $message) {
|
||||||
|
if (is_array($message)) {
|
||||||
|
$messages[] = Arr::except($message, ['@odata.context']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$snapshots[] = [
|
||||||
|
'channel' => $channel,
|
||||||
|
'template_id' => $templateId,
|
||||||
|
'template' => $template,
|
||||||
|
'localized_notification_messages' => $messages,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($snapshots === []) {
|
||||||
|
$metadata['enrollment_notification_templates_hydration'] = 'failed';
|
||||||
|
|
||||||
|
return [$payload, $metadata];
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload['notificationTemplateSnapshots'] = $snapshots;
|
||||||
|
|
||||||
|
$metadata['enrollment_notification_templates_hydration'] = $failures > 0 ? 'partial' : 'complete';
|
||||||
|
|
||||||
|
return [$payload, $metadata];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array{0:?string,1:?string}
|
||||||
|
*/
|
||||||
|
private function parseEnrollmentNotificationTemplateRef(string $templateRef): array
|
||||||
|
{
|
||||||
|
if (! str_contains($templateRef, '_')) {
|
||||||
|
return [null, $templateRef];
|
||||||
|
}
|
||||||
|
|
||||||
|
[$channel, $templateId] = explode('_', $templateRef, 2);
|
||||||
|
|
||||||
|
$channel = trim($channel);
|
||||||
|
$templateId = trim($templateId);
|
||||||
|
|
||||||
|
if ($templateId === '') {
|
||||||
|
return [$channel !== '' ? $channel : null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$channel !== '' ? $channel : null, $templateId];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract all settingDefinitionId from settings array, including nested children.
|
* Extract all settingDefinitionId from settings array, including nested children.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -562,6 +562,9 @@
|
|||||||
'update_method' => 'PATCH',
|
'update_method' => 'PATCH',
|
||||||
'id_field' => 'id',
|
'id_field' => 'id',
|
||||||
'hydration' => 'properties',
|
'hydration' => 'properties',
|
||||||
|
'update_strip_keys' => [
|
||||||
|
'notificationTemplateSnapshots',
|
||||||
|
],
|
||||||
'assignments_list_path' => '/deviceManagement/deviceEnrollmentConfigurations/{id}/assignments',
|
'assignments_list_path' => '/deviceManagement/deviceEnrollmentConfigurations/{id}/assignments',
|
||||||
'assignments_create_path' => '/deviceManagement/deviceEnrollmentConfigurations/{id}/assign',
|
'assignments_create_path' => '/deviceManagement/deviceEnrollmentConfigurations/{id}/assign',
|
||||||
'assignments_create_method' => 'POST',
|
'assignments_create_method' => 'POST',
|
||||||
|
|||||||
@ -21,6 +21,7 @@ ## Phase 4: Implementation
|
|||||||
- [x] T008 Add contracts in `config/graph_contracts.php` (resource + type families).
|
- [x] T008 Add contracts in `config/graph_contracts.php` (resource + type families).
|
||||||
- [x] T009 Update `PolicySyncService` enrollment classification logic.
|
- [x] T009 Update `PolicySyncService` enrollment classification logic.
|
||||||
- [x] T010 Add normalizer for readable UI output (key fields per subtype).
|
- [x] T010 Add normalizer for readable UI output (key fields per subtype).
|
||||||
|
- [x] T013 Hydrate notification templates for enrollment notifications.
|
||||||
|
|
||||||
## Phase 5: Verification
|
## Phase 5: Verification
|
||||||
- [x] T011 Run targeted tests.
|
- [x] T011 Run targeted tests.
|
||||||
|
|||||||
@ -56,3 +56,95 @@
|
|||||||
$response->assertSee('Enable feature');
|
$response->assertSee('Enable feature');
|
||||||
$response->assertSee('Normalized diff');
|
$response->assertSee('Normalized diff');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('policy version detail shows enrollment notification template settings', function () {
|
||||||
|
$tenant = Tenant::create([
|
||||||
|
'tenant_id' => 'tenant-enrollment-notify',
|
||||||
|
'name' => 'Tenant Enrollment Notify',
|
||||||
|
'metadata' => [],
|
||||||
|
'is_current' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tenant->makeCurrent();
|
||||||
|
|
||||||
|
$policy = Policy::create([
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'external_id' => 'enroll-notify-1',
|
||||||
|
'policy_type' => 'deviceEnrollmentNotificationConfiguration',
|
||||||
|
'display_name' => 'Enrollment Notifications',
|
||||||
|
'platform' => 'all',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$version = PolicyVersion::create([
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'policy_id' => $policy->id,
|
||||||
|
'version_number' => 1,
|
||||||
|
'policy_type' => $policy->policy_type,
|
||||||
|
'platform' => $policy->platform,
|
||||||
|
'created_by' => 'tester@example.com',
|
||||||
|
'captured_at' => CarbonImmutable::now(),
|
||||||
|
'snapshot' => [
|
||||||
|
'@odata.type' => '#microsoft.graph.deviceEnrollmentNotificationConfiguration',
|
||||||
|
'displayName' => 'Enrollment Notifications',
|
||||||
|
'priority' => 1,
|
||||||
|
'version' => 1,
|
||||||
|
'platformType' => 'windows',
|
||||||
|
'notificationTemplates' => ['Email_email-template-1', 'Push_push-template-1'],
|
||||||
|
'notificationTemplateSnapshots' => [
|
||||||
|
[
|
||||||
|
'channel' => 'Email',
|
||||||
|
'template_id' => 'email-template-1',
|
||||||
|
'template' => [
|
||||||
|
'id' => 'email-template-1',
|
||||||
|
'displayName' => 'Email Template',
|
||||||
|
'defaultLocale' => 'en-us',
|
||||||
|
'brandingOptions' => 'none',
|
||||||
|
],
|
||||||
|
'localized_notification_messages' => [
|
||||||
|
[
|
||||||
|
'locale' => 'en-us',
|
||||||
|
'subject' => 'Email Subject',
|
||||||
|
'messageTemplate' => 'Email Body',
|
||||||
|
'isDefault' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'channel' => 'Push',
|
||||||
|
'template_id' => 'push-template-1',
|
||||||
|
'template' => [
|
||||||
|
'id' => 'push-template-1',
|
||||||
|
'displayName' => 'Push Template',
|
||||||
|
'defaultLocale' => 'en-us',
|
||||||
|
'brandingOptions' => 'none',
|
||||||
|
],
|
||||||
|
'localized_notification_messages' => [
|
||||||
|
[
|
||||||
|
'locale' => 'en-us',
|
||||||
|
'subject' => 'Push Subject',
|
||||||
|
'messageTemplate' => 'Push Body',
|
||||||
|
'isDefault' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$response = $this->actingAs($user)
|
||||||
|
->get(PolicyVersionResource::getUrl('view', ['record' => $version]).'?tab=normalized-settings');
|
||||||
|
|
||||||
|
$response->assertOk();
|
||||||
|
$response->assertSee('Enrollment notifications');
|
||||||
|
$response->assertSee('Notification templates');
|
||||||
|
$response->assertSee('Email (en-us) Subject');
|
||||||
|
$response->assertSee('Email Subject');
|
||||||
|
$response->assertSee('Email (en-us) Message');
|
||||||
|
$response->assertSee('Email Body');
|
||||||
|
$response->assertSee('Push (en-us) Subject');
|
||||||
|
$response->assertSee('Push Subject');
|
||||||
|
$response->assertSee('Push (en-us) Message');
|
||||||
|
$response->assertSee('Push Body');
|
||||||
|
});
|
||||||
|
|||||||
@ -172,6 +172,120 @@ public function request(string $method, string $path, array $options = []): Grap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class EnrollmentNotificationSnapshotGraphClient implements GraphClientInterface
|
||||||
|
{
|
||||||
|
public array $requests = [];
|
||||||
|
|
||||||
|
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
return new GraphResponse(success: true, data: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
$this->requests[] = ['getPolicy', $policyType, $policyId, $options];
|
||||||
|
|
||||||
|
if ($policyType === 'deviceEnrollmentNotificationConfiguration') {
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'payload' => [
|
||||||
|
'id' => $policyId,
|
||||||
|
'displayName' => 'Enrollment Notifications',
|
||||||
|
'@odata.type' => '#microsoft.graph.deviceEnrollmentNotificationConfiguration',
|
||||||
|
'priority' => 1,
|
||||||
|
'version' => 1,
|
||||||
|
'platformType' => 'windows',
|
||||||
|
'brandingOptions' => 'none',
|
||||||
|
'templateType' => '0',
|
||||||
|
'notificationMessageTemplateId' => '00000000-0000-0000-0000-000000000000',
|
||||||
|
'notificationTemplates' => [
|
||||||
|
'Email_email-template-1',
|
||||||
|
'Push_push-template-1',
|
||||||
|
],
|
||||||
|
'deviceEnrollmentConfigurationType' => 'enrollmentNotificationsConfiguration',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'payload' => [
|
||||||
|
'id' => $policyId,
|
||||||
|
'displayName' => 'Policy',
|
||||||
|
'@odata.type' => '#microsoft.graph.deviceConfiguration',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrganization(array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
return new GraphResponse(success: true, data: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
return new GraphResponse(success: true, data: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServicePrincipalPermissions(array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
return new GraphResponse(success: true, data: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function request(string $method, string $path, array $options = []): GraphResponse
|
||||||
|
{
|
||||||
|
$this->requests[] = [$method, $path, $options];
|
||||||
|
|
||||||
|
if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/email-template-1/localizedNotificationMessages')) {
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'value' => [
|
||||||
|
[
|
||||||
|
'id' => 'email-template-1_en-us',
|
||||||
|
'locale' => 'en-us',
|
||||||
|
'subject' => 'Email Subject',
|
||||||
|
'messageTemplate' => 'Email Body',
|
||||||
|
'isDefault' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/push-template-1/localizedNotificationMessages')) {
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'value' => [
|
||||||
|
[
|
||||||
|
'id' => 'push-template-1_en-us',
|
||||||
|
'locale' => 'en-us',
|
||||||
|
'subject' => 'Push Subject',
|
||||||
|
'messageTemplate' => 'Push Body',
|
||||||
|
'isDefault' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/email-template-1')) {
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'@odata.context' => 'https://graph.microsoft.com/beta/$metadata#deviceManagement/notificationMessageTemplates/$entity',
|
||||||
|
'id' => 'email-template-1',
|
||||||
|
'displayName' => 'Email Template',
|
||||||
|
'defaultLocale' => 'en-us',
|
||||||
|
'brandingOptions' => 'none',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($method === 'GET' && str_contains($path, 'deviceManagement/notificationMessageTemplates/push-template-1')) {
|
||||||
|
return new GraphResponse(success: true, data: [
|
||||||
|
'@odata.context' => 'https://graph.microsoft.com/beta/$metadata#deviceManagement/notificationMessageTemplates/$entity',
|
||||||
|
'id' => 'push-template-1',
|
||||||
|
'displayName' => 'Push Template',
|
||||||
|
'defaultLocale' => 'en-us',
|
||||||
|
'brandingOptions' => 'none',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GraphResponse(success: true, data: []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
it('hydrates compliance policy scheduled actions into snapshots', function () {
|
it('hydrates compliance policy scheduled actions into snapshots', function () {
|
||||||
$client = new PolicySnapshotGraphClient;
|
$client = new PolicySnapshotGraphClient;
|
||||||
app()->instance(GraphClientInterface::class, $client);
|
app()->instance(GraphClientInterface::class, $client);
|
||||||
@ -247,6 +361,47 @@ public function request(string $method, string $path, array $options = []): Grap
|
|||||||
'securityBaselinePolicy',
|
'securityBaselinePolicy',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
it('hydrates enrollment notification templates into snapshots', function () {
|
||||||
|
$client = new EnrollmentNotificationSnapshotGraphClient;
|
||||||
|
app()->instance(GraphClientInterface::class, $client);
|
||||||
|
|
||||||
|
$tenant = Tenant::factory()->create([
|
||||||
|
'tenant_id' => 'tenant-enrollment-notifications',
|
||||||
|
'app_client_id' => 'client-123',
|
||||||
|
'app_client_secret' => 'secret-123',
|
||||||
|
'is_current' => true,
|
||||||
|
]);
|
||||||
|
$tenant->makeCurrent();
|
||||||
|
|
||||||
|
$policy = Policy::factory()->create([
|
||||||
|
'tenant_id' => $tenant->id,
|
||||||
|
'external_id' => 'enroll-notify-123',
|
||||||
|
'policy_type' => 'deviceEnrollmentNotificationConfiguration',
|
||||||
|
'display_name' => 'Enrollment Notifications',
|
||||||
|
'platform' => 'all',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$service = app(PolicySnapshotService::class);
|
||||||
|
$result = $service->fetch($tenant, $policy);
|
||||||
|
|
||||||
|
expect($result)->toHaveKey('payload');
|
||||||
|
expect($result['payload'])->toHaveKey('notificationTemplateSnapshots');
|
||||||
|
expect($result['payload']['notificationTemplateSnapshots'])->toHaveCount(2);
|
||||||
|
expect($result['metadata']['enrollment_notification_templates_hydration'] ?? null)->toBe('complete');
|
||||||
|
|
||||||
|
$email = collect($result['payload']['notificationTemplateSnapshots'])->firstWhere('channel', 'Email');
|
||||||
|
expect($email)->not->toBeNull()
|
||||||
|
->and($email['template_id'] ?? null)->toBe('email-template-1')
|
||||||
|
->and($email['template']['displayName'] ?? null)->toBe('Email Template')
|
||||||
|
->and($email['localized_notification_messages'][0]['subject'] ?? null)->toBe('Email Subject');
|
||||||
|
|
||||||
|
$push = collect($result['payload']['notificationTemplateSnapshots'])->firstWhere('channel', 'Push');
|
||||||
|
expect($push)->not->toBeNull()
|
||||||
|
->and($push['template_id'] ?? null)->toBe('push-template-1')
|
||||||
|
->and($push['template']['displayName'] ?? null)->toBe('Push Template')
|
||||||
|
->and($push['localized_notification_messages'][0]['subject'] ?? null)->toBe('Push Subject');
|
||||||
|
});
|
||||||
|
|
||||||
it('filters mobile app snapshots to metadata-only keys', function () {
|
it('filters mobile app snapshots to metadata-only keys', function () {
|
||||||
$client = new PolicySnapshotGraphClient;
|
$client = new PolicySnapshotGraphClient;
|
||||||
app()->instance(GraphClientInterface::class, $client);
|
app()->instance(GraphClientInterface::class, $client);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user