Skip to content

Commit 04eda94

Browse files
authored
fix(windows_esp): resolve app validation pagination limit by querying apps individually (#889)
1 parent 1141f5c commit 04eda94

File tree

2 files changed

+96
-91
lines changed

2 files changed

+96
-91
lines changed

internal/services/resources/device_management/graph_beta/windows_enrollment_status_page/mocks/responders.go

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,56 +32,34 @@ func (m *WindowsEnrollmentStatusPageMock) RegisterMocks() {
3232
mockState.enrollmentStatusPages = make(map[string]map[string]any)
3333
mockState.Unlock()
3434

35-
// Mock the mobile apps endpoint for validation
36-
httpmock.RegisterResponder("GET", `=~^https://graph\.microsoft\.com/beta/deviceAppManagement/mobileApps.*`, func(req *http.Request) (*http.Response, error) {
37-
// Return mock mobile apps that include the test app IDs used in unit tests
38-
mockApps := map[string]any{
39-
"@odata.context": "https://graph.microsoft.com/beta/$metadata#deviceAppManagement/mobileApps",
40-
"@odata.count": 5,
41-
"value": []any{
42-
map[string]any{
43-
"@odata.type": "#microsoft.graph.win32LobApp",
44-
"id": "12345678-1234-1234-1234-123456789012",
45-
"displayName": "Test App 1",
46-
"description": "Test application 1 for unit testing",
47-
"publisher": "Test Publisher",
48-
"publishingState": "published",
49-
},
50-
map[string]any{
51-
"@odata.type": "#microsoft.graph.winGetApp",
52-
"id": "87654321-4321-4321-4321-210987654321",
53-
"displayName": "Test App 2",
54-
"description": "Test application 2 for unit testing",
55-
"publisher": "Test Publisher",
56-
"publishingState": "published",
57-
},
58-
map[string]any{
59-
"@odata.type": "#microsoft.graph.win32LobApp",
60-
"id": "e4938228-aab3-493b-a9d5-8250aa8e9d55",
61-
"displayName": "Test App 3",
62-
"description": "Test application 3 for unit testing",
63-
"publisher": "Test Publisher",
64-
"publishingState": "published",
65-
},
66-
map[string]any{
67-
"@odata.type": "#microsoft.graph.win32LobApp",
68-
"id": "e83d36e1-3ff2-4567-90d9-940919184ad5",
69-
"displayName": "Test App 4",
70-
"description": "Test application 4 for unit testing",
71-
"publisher": "Test Publisher",
72-
"publishingState": "published",
73-
},
74-
map[string]any{
75-
"@odata.type": "#microsoft.graph.win32LobApp",
76-
"id": "cd4486df-05cc-42bd-8c34-67ac20e10166",
77-
"displayName": "Test App 5",
78-
"description": "Test application 5 for unit testing",
79-
"publisher": "Test Publisher",
80-
"publishingState": "published",
81-
},
82-
},
35+
// Define mock apps used in tests
36+
mockApps := map[string]string{
37+
"12345678-1234-1234-1234-123456789012": "#microsoft.graph.win32LobApp",
38+
"87654321-4321-4321-4321-210987654321": "#microsoft.graph.winGetApp",
39+
"e4938228-aab3-493b-a9d5-8250aa8e9d55": "#microsoft.graph.win32LobApp",
40+
"e83d36e1-3ff2-4567-90d9-940919184ad5": "#microsoft.graph.win32LobApp",
41+
"cd4486df-05cc-42bd-8c34-67ac20e10166": "#microsoft.graph.win32LobApp",
42+
}
43+
44+
// Mock individual mobile app lookup by ID
45+
httpmock.RegisterResponder("GET", `=~^https://graph\.microsoft\.com/beta/deviceAppManagement/mobileApps/[0-9a-fA-F-]+$`, func(req *http.Request) (*http.Response, error) {
46+
parts := strings.Split(req.URL.Path, "/")
47+
appId := parts[len(parts)-1]
48+
49+
if odataType, ok := mockApps[appId]; ok {
50+
return httpmock.NewJsonResponse(200, map[string]any{
51+
"@odata.type": odataType,
52+
"id": appId,
53+
})
8354
}
84-
return httpmock.NewJsonResponse(200, mockApps)
55+
56+
// Return 404 for unknown app IDs
57+
return httpmock.NewJsonResponse(404, map[string]any{
58+
"error": map[string]any{
59+
"code": "ResourceNotFound",
60+
"message": "The requested resource does not exist.",
61+
},
62+
})
8563
})
8664

8765
httpmock.RegisterResponder("GET", "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations", func(req *http.Request) (*http.Response, error) {
@@ -318,6 +296,16 @@ func (m *WindowsEnrollmentStatusPageMock) RegisterErrorMocks() {
318296
mockState.enrollmentStatusPages = make(map[string]map[string]any)
319297
mockState.Unlock()
320298

299+
// Mock individual mobile app lookup by ID for error scenarios - always return 404
300+
httpmock.RegisterResponder("GET", `=~^https://graph\.microsoft\.com/beta/deviceAppManagement/mobileApps/[0-9a-fA-F-]+$`, func(req *http.Request) (*http.Response, error) {
301+
return httpmock.NewJsonResponse(404, map[string]any{
302+
"error": map[string]any{
303+
"code": "ResourceNotFound",
304+
"message": "The requested resource does not exist.",
305+
},
306+
})
307+
})
308+
321309
httpmock.RegisterResponder("GET", "https://graph.microsoft.com/beta/deviceManagement/deviceEnrollmentConfigurations", func(req *http.Request) (*http.Response, error) {
322310
jsonStr, _ := helpers.ParseJSONFile("../tests/responses/validate_get/get_windows_enrollment_status_pages_list.json")
323311
var responseObj map[string]any

internal/services/resources/device_management/graph_beta/windows_enrollment_status_page/validate.go

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/hashicorp/terraform-plugin-framework/types"
1010
"github.com/hashicorp/terraform-plugin-log/tflog"
1111
msgraphbetasdk "github.com/microsoftgraph/msgraph-beta-sdk-go"
12-
"github.com/microsoftgraph/msgraph-beta-sdk-go/deviceappmanagement"
12+
graphmodels "github.com/microsoftgraph/msgraph-beta-sdk-go/models"
1313
)
1414

1515
// validateRequest validates the entire request payload
@@ -48,60 +48,77 @@ func validateSelectedMobileAppIds(ctx context.Context, client *msgraphbetasdk.Gr
4848
return nil
4949
}
5050

51-
// Get all Windows app types from Microsoft Graph
52-
filter := "isof('microsoft.graph.windowsAppX') or isof('microsoft.graph.windowsMobileMSI') or isof('microsoft.graph.windowsUniversalAppX') or isof('microsoft.graph.officeSuiteApp') or isof('microsoft.graph.windowsMicrosoftEdgeApp') or isof('microsoft.graph.winGetApp') or isof('microsoft.graph.win32LobApp') or isof('microsoft.graph.win32CatalogApp')"
53-
orderby := "displayname"
54-
top := int32(250)
55-
56-
requestConfig := &deviceappmanagement.MobileAppsRequestBuilderGetRequestConfiguration{
57-
QueryParameters: &deviceappmanagement.MobileAppsRequestBuilderGetQueryParameters{
58-
Filter: &filter,
59-
Orderby: []string{orderby},
60-
Top: &top,
61-
},
62-
}
63-
64-
mobileApps, err := client.
65-
DeviceAppManagement().
66-
MobileApps().
67-
Get(ctx, requestConfig)
68-
69-
if err != nil {
70-
tflog.Error(ctx, "Failed to retrieve mobile apps for validation", map[string]any{
71-
"error": err.Error(),
72-
})
73-
return fmt.Errorf("failed to validate mobile app IDs: unable to retrieve available apps from Microsoft Graph")
74-
}
51+
for _, appId := range appIdStrings {
52+
appIdValue := appId.ValueString()
7553

76-
// Create a map of valid app IDs for quick lookup
77-
validAppIds := make(map[string]string) // ID -> DisplayName
78-
validAppTypes := make(map[string]string) // ID -> AppType
54+
// Query the specific app by ID
55+
app, err := client.
56+
DeviceAppManagement().
57+
MobileApps().
58+
ByMobileAppId(appIdValue).
59+
Get(ctx, nil)
60+
61+
if err != nil {
62+
tflog.Error(ctx, "Failed to retrieve mobile app for validation", map[string]any{
63+
"appId": appIdValue,
64+
"error": err.Error(),
65+
})
66+
return fmt.Errorf("supplied app ID '%s' does not match any valid Windows app types. Valid app types include: windowsAppX, windowsMobileMSI, windowsUniversalAppX, officeSuiteApp, windowsMicrosoftEdgeApp, winGetApp, win32LobApp, win32CatalogApp", appIdValue)
67+
}
7968

80-
if mobileApps.GetValue() != nil {
81-
for _, app := range mobileApps.GetValue() {
82-
if app.GetId() != nil && app.GetDisplayName() != nil && app.GetOdataType() != nil {
83-
validAppIds[*app.GetId()] = *app.GetDisplayName()
84-
validAppTypes[*app.GetId()] = *app.GetOdataType()
69+
// Validate the app type using SDK type assertions
70+
isValidType := false
71+
var appTypeName string
72+
73+
switch app.(type) {
74+
case *graphmodels.WindowsAppX:
75+
isValidType = true
76+
appTypeName = "windowsAppX"
77+
case *graphmodels.WindowsMobileMSI:
78+
isValidType = true
79+
appTypeName = "windowsMobileMSI"
80+
case *graphmodels.WindowsUniversalAppX:
81+
isValidType = true
82+
appTypeName = "windowsUniversalAppX"
83+
case *graphmodels.OfficeSuiteApp:
84+
isValidType = true
85+
appTypeName = "officeSuiteApp"
86+
case *graphmodels.WindowsMicrosoftEdgeApp:
87+
isValidType = true
88+
appTypeName = "windowsMicrosoftEdgeApp"
89+
case *graphmodels.WinGetApp:
90+
isValidType = true
91+
appTypeName = "winGetApp"
92+
case *graphmodels.Win32LobApp:
93+
isValidType = true
94+
appTypeName = "win32LobApp"
95+
case *graphmodels.Win32CatalogApp:
96+
isValidType = true
97+
appTypeName = "win32CatalogApp"
98+
default:
99+
if odataType := app.GetOdataType(); odataType != nil {
100+
appTypeName = *odataType
101+
} else {
102+
appTypeName = "unknown"
85103
}
86104
}
87-
}
88105

89-
// Validate each provided app ID
90-
for _, appId := range appIdStrings {
91-
appIdValue := appId.ValueString()
92-
displayName, exists := validAppIds[appIdValue]
106+
if !isValidType {
107+
return fmt.Errorf("supplied app ID '%s' has type '%s' which is not a valid Windows app type. Valid app types include: windowsAppX, windowsMobileMSI, windowsUniversalAppX, officeSuiteApp, windowsMicrosoftEdgeApp, winGetApp, win32LobApp, win32CatalogApp", appIdValue, appTypeName)
108+
}
93109

94-
if !exists {
95-
return fmt.Errorf("supplied app ID '%s' does not match any valid Windows app types. Valid app types include: windowsAppX, windowsMobileMSI, windowsUniversalAppX, officeSuiteApp, windowsMicrosoftEdgeApp, winGetApp, win32LobApp, win32CatalogApp", appIdValue)
110+
displayName := ""
111+
if app.GetDisplayName() != nil {
112+
displayName = *app.GetDisplayName()
96113
}
97114

98115
tflog.Debug(ctx, "Validated mobile app", map[string]any{
99116
"appId": appIdValue,
100117
"displayName": displayName,
101-
"appType": validAppTypes[appIdValue],
118+
"appType": appTypeName,
102119
})
103120
}
104121

105-
tflog.Debug(ctx, fmt.Sprintf("Validated %d mobile app IDs against %d available Windows apps", len(appIdStrings), len(validAppIds)))
122+
tflog.Debug(ctx, fmt.Sprintf("Successfully validated %d mobile app IDs", len(appIdStrings)))
106123
return nil
107124
}

0 commit comments

Comments
 (0)