fix(spec-004): Use documented CRUD on /assignments instead of undocumented /assign

Changes:
- Replace POST /assign with standard CRUD operations
- Restore strategy: DELETE existing + POST new assignments
- Graph Contract: assignments_create/update/delete_path + methods
- Handle 201 Created (POST) and 204 No Content (DELETE)
- Fail-soft: continue if individual assignment fails

Based on: Microsoft Learn Graph API docs + real-world usage patterns
This commit is contained in:
Ahmed Darrazi 2025-12-22 01:14:03 +01:00
parent 1fa15b4db2
commit 28f440718a

View File

@ -183,12 +183,18 @@ ### Restore with Group Mapping
**FR-004.11**: System MUST persist group mapping selections in RestoreRun metadata for audit and rerun purposes. **FR-004.11**: System MUST persist group mapping selections in RestoreRun metadata for audit and rerun purposes.
**FR-004.12**: When restoring assignments, system MUST: **FR-004.12**: When restoring assignments, system MUST:
1. Replace source group IDs with mapped target group IDs 1. Replace source group IDs with mapped target group IDs in assignment objects
2. Skip assignments marked "Skip" 2. Skip assignments marked "Skip" in group mapping
3. Preserve include/exclude intent and filters 3. Preserve include/exclude intent and filters
4. Call POST `/deviceManagement/configurationPolicies/{id}/assign` (not PATCH) with complete mapped assignments array (replaces all assignments atomically) 4. Execute restore via DELETE-then-CREATE pattern:
5. Handle 204 No Content or 200 OK as success - Step 1: GET existing assignments from target policy
6. Log Graph request-id and client-request-id on failure - Step 2: DELETE each existing assignment (via DELETE `/assignments/{id}`)
- Step 3: POST each new/mapped assignment (via POST `/assignments`)
5. Handle failures gracefully:
- 204 No Content on DELETE = success
- 201 Created on POST = success
- Log request-id/client-request-id on any failure
6. Continue with remaining assignments if one fails (fail-soft)
**FR-004.13**: System MUST handle assignment restore failures gracefully: **FR-004.13**: System MUST handle assignment restore failures gracefully:
- Log per-assignment outcome (success/skip/failure) - Log per-assignment outcome (success/skip/failure)
@ -274,26 +280,31 @@ ### Endpoints to Add (Production-Tested Strategies)
- Client-side filter to extract assignments - Client-side filter to extract assignments
- **Reason**: Known Graph API quirks with assignment expansion on certain template families - **Reason**: Known Graph API quirks with assignment expansion on certain template families
2. **POST** `/deviceManagement/configurationPolicies/{id}/assign` (POST only, NOT PATCH) 2. **Assignment CRUD Operations** (Standard Graph Pattern)
- Body: `{ assignments: [assignment objects] }`
- Returns: 204 No Content or 200 OK - **POST** `/deviceManagement/configurationPolicies/{id}/assignments`
- **Note**: This is an action endpoint, replaces entire assignments array - Body: Single assignment object
- Example payload: - Returns: 201 Created with assignment object
- Example:
```json ```json
{ {
"assignments": [
{
"id": "00000000-0000-0000-0000-000000000000",
"target": { "target": {
"@odata.type": "#microsoft.graph.groupAssignmentTarget", "@odata.type": "#microsoft.graph.groupAssignmentTarget",
"groupId": "abc-123-def" "groupId": "abc-123-def"
}, },
"intent": "apply" "intent": "apply"
} }
]
}
``` ```
- **PATCH** `/deviceManagement/configurationPolicies/{id}/assignments/{assignmentId}`
- Body: Assignment object (partial update)
- Returns: 200 OK with updated assignment
- **DELETE** `/deviceManagement/configurationPolicies/{id}/assignments/{assignmentId}`
- Returns: 204 No Content
- **Restore Strategy**: DELETE all existing assignments, then POST new ones (atomic via transaction pattern)
3. **POST** `/directoryObjects/getByIds` (Stable Group Resolution) 3. **POST** `/directoryObjects/getByIds` (Stable Group Resolution)
- Body: `{ "ids": ["id1", "id2"], "types": ["group"] }` - Body: `{ "ids": ["id1", "id2"], "types": ["group"] }`
- Returns: `{ value: [{ id, displayName, ... }] }` - Returns: `{ value: [{ id, displayName, ... }] }`
@ -316,11 +327,20 @@ ### Graph Contract Updates
```php ```php
'settingsCatalogPolicy' => [ 'settingsCatalogPolicy' => [
// ... existing // ... existing config
'assignments_path' => '/deviceManagement/configurationPolicies/{id}/assignments',
'assign_method' => 'POST', // Assignments CRUD (standard Graph pattern)
'assign_path' => '/deviceManagement/configurationPolicies/{id}/assign', 'assignments_list_path' => '/deviceManagement/configurationPolicies/{id}/assignments',
'assignments_create_path' => '/deviceManagement/configurationPolicies/{id}/assignments',
'assignments_create_method' => 'POST',
'assignments_update_path' => '/deviceManagement/configurationPolicies/{id}/assignments/{assignmentId}',
'assignments_update_method' => 'PATCH',
'assignments_delete_path' => '/deviceManagement/configurationPolicies/{id}/assignments/{assignmentId}',
'assignments_delete_method' => 'DELETE',
// Scope Tags
'supports_scope_tags' => true, 'supports_scope_tags' => true,
'scope_tag_field' => 'roleScopeTagIds', // array in policy payload
], ],
``` ```