Skip to content

Commit d67836f

Browse files
authored
fix: added helpers for stating and more unit tests (#1261)
# Pull Request Description ## Summary [Provide a brief description of the changes in this PR] ### Issue Reference Fixes #[Issue Number] ### Motivation and Context - Why is this change needed? - What problem does it solve? - If it fixes an open issue, please link to the issue here ### Dependencies - List any dependencies that are required for this change - Include any configuration changes needed - Note any version updates required ## Type of Change Please mark the relevant option with an `x`: - [ ] 🐛 Bug fix (non-breaking change which fixes an issue) - [ ] ✨ New feature (non-breaking change which adds functionality) - [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] 📝 Documentation update (Wiki/README/Code comments) - [ ] ♻️ Refactor (code improvement without functional changes) - [ ] 🎨 Style update (formatting, renaming) - [ ] 🔧 Configuration change - [ ] 📦 Dependency update ## Testing - [ ] I have added unit tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have tested this code in the following browsers/environments: [list environments] ## Quality Checklist - [ ] I have reviewed my own code before requesting review - [ ] I have verified there are no other open Pull Requests for the same update/change - [ ] All CI/CD pipelines pass without errors or warnings - [ ] My code follows the established style guidelines of this project - [ ] I have added necessary documentation (if appropriate) - [ ] I have commented my code, particularly in complex areas - [ ] I have made corresponding changes to the README and other relevant documentation - [ ] My changes generate no new warnings - [ ] I have performed a self-review of my own code - [ ] My code is properly formatted according to project standards ## Screenshots/Recordings (if appropriate) [Add screenshots or recordings that demonstrate the changes] ## Additional Notes [Add any additional information that might be helpful for reviewers]
2 parents 77de500 + 15a0033 commit d67836f

File tree

13 files changed

+2109
-1262
lines changed

13 files changed

+2109
-1262
lines changed

internal/services/common/convert/map_to_framework.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,42 @@ func MapToFrameworkStringList(ctx context.Context, data map[string]any, key stri
143143
}
144144
return types.ListNull(types.StringType)
145145
}
146+
147+
// MapToFrameworkStringSetFromNumbers extracts a numeric slice from a map, converts to strings,
148+
// and returns a Terraform Framework string set. Handles int, int32, int64, float64, and string types.
149+
// Returns empty set for empty arrays, null only if key doesn't exist.
150+
func MapToFrameworkStringSetFromNumbers(ctx context.Context, data map[string]any, key string) types.Set {
151+
value, exists := data[key]
152+
if !exists {
153+
return types.SetNull(types.StringType)
154+
}
155+
156+
rawSlice, ok := value.([]any)
157+
if !ok {
158+
return types.SetNull(types.StringType)
159+
}
160+
161+
strings := make([]string, 0, len(rawSlice))
162+
for _, item := range rawSlice {
163+
switch v := item.(type) {
164+
case float64:
165+
strings = append(strings, fmt.Sprintf("%.0f", v))
166+
case int:
167+
strings = append(strings, fmt.Sprintf("%d", v))
168+
case int32:
169+
strings = append(strings, fmt.Sprintf("%d", v))
170+
case int64:
171+
strings = append(strings, fmt.Sprintf("%d", v))
172+
case string:
173+
strings = append(strings, v)
174+
}
175+
}
176+
177+
set, diags := types.SetValueFrom(ctx, types.StringType, strings)
178+
if diags.HasError() {
179+
tflog.Error(ctx, fmt.Sprintf("Failed to convert numbers to string set: %v", diags))
180+
return types.SetNull(types.StringType)
181+
}
182+
183+
return set
184+
}

internal/services/resources/device_management/graph_beta/autopatch_groups/construct.go

Lines changed: 231 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,23 @@ import (
66
"fmt"
77
"strconv"
88

9-
"github.com/hashicorp/terraform-plugin-framework/types"
9+
"github.com/deploymenttheory/terraform-provider-microsoft365/internal/services/common/convert"
1010
"github.com/hashicorp/terraform-plugin-log/tflog"
1111
)
1212

13+
// mapTypeStringToInt converts type strings to integers for API requests
14+
// "Device" = 0, "None" = 1
15+
func mapTypeStringToInt(typeStr string) int {
16+
switch typeStr {
17+
case "None":
18+
return 1
19+
case "Device":
20+
return 0
21+
default:
22+
return 0 // Default to Device
23+
}
24+
}
25+
1326
// constructResource constructs a JSON request body for the Autopatch Groups API
1427
func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel) (map[string]any, error) {
1528
tflog.Debug(ctx, fmt.Sprintf("Constructing %s resource from Terraform configuration", ResourceName))
@@ -38,7 +51,8 @@ func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel)
3851
groupMap["id"] = group.Id.ValueString()
3952
}
4053
if !group.Type.IsNull() && !group.Type.IsUnknown() {
41-
groupMap["type"] = group.Type.ValueString()
54+
// Convert string to integer for API (POST uses integers)
55+
groupMap["type"] = mapTypeStringToInt(group.Type.ValueString())
4256
}
4357
globalGroupsAPI = append(globalGroupsAPI, groupMap)
4458
}
@@ -84,7 +98,8 @@ func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel)
8498
userGroupMap["name"] = userGroup.Name.ValueString()
8599
}
86100
if !userGroup.Type.IsNull() && !userGroup.Type.IsUnknown() {
87-
userGroupMap["type"] = userGroup.Type.ValueInt32()
101+
// Convert string to integer for API (POST uses integers)
102+
userGroupMap["type"] = mapTypeStringToInt(userGroup.Type.ValueString())
88103
}
89104
userGroupsAPI = append(userGroupsAPI, userGroupMap)
90105
}
@@ -95,66 +110,10 @@ func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel)
95110

96111
// Deployment Group Policy Settings
97112
if group.DeploymentGroupPolicySettings != nil {
98-
policyMap := make(map[string]any)
99-
100-
if !group.DeploymentGroupPolicySettings.AadGroupName.IsNull() && !group.DeploymentGroupPolicySettings.AadGroupName.IsUnknown() {
101-
policyMap["aadGroupName"] = group.DeploymentGroupPolicySettings.AadGroupName.ValueString()
102-
}
103-
if !group.DeploymentGroupPolicySettings.IsUpdateSettingsModified.IsNull() && !group.DeploymentGroupPolicySettings.IsUpdateSettingsModified.IsUnknown() {
104-
policyMap["isUpdateSettingsModified"] = group.DeploymentGroupPolicySettings.IsUpdateSettingsModified.ValueBool()
113+
policyMap, err := constructDeploymentGroupPolicySettings(ctx, group.DeploymentGroupPolicySettings)
114+
if err != nil {
115+
return nil, fmt.Errorf("error constructing deployment group policy settings: %w", err)
105116
}
106-
107-
// Device Configuration Settings
108-
if group.DeploymentGroupPolicySettings.DeviceConfigurationSetting != nil {
109-
deviceConfigMap := make(map[string]any)
110-
111-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.PolicyId.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.PolicyId.IsUnknown() {
112-
deviceConfigMap["policyId"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.PolicyId.ValueString()
113-
}
114-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.UpdateBehavior.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.UpdateBehavior.IsUnknown() {
115-
deviceConfigMap["updateBehavior"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.UpdateBehavior.ValueString()
116-
}
117-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.NotificationSetting.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.NotificationSetting.IsUnknown() {
118-
deviceConfigMap["notificationSetting"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.NotificationSetting.ValueString()
119-
}
120-
121-
// Quality Deployment Settings
122-
if group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings != nil {
123-
qualityMap := make(map[string]any)
124-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deadline.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deadline.IsUnknown() {
125-
qualityMap["deadline"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deadline.ValueInt32()
126-
}
127-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deferral.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deferral.IsUnknown() {
128-
qualityMap["deferral"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.Deferral.ValueInt32()
129-
}
130-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.GracePeriod.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.GracePeriod.IsUnknown() {
131-
qualityMap["gracePeriod"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.QualityDeploymentSettings.GracePeriod.ValueInt32()
132-
}
133-
deviceConfigMap["qualityDeploymentSettings"] = qualityMap
134-
}
135-
136-
// Feature Deployment Settings
137-
if group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings != nil {
138-
featureMap := make(map[string]any)
139-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deadline.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deadline.IsUnknown() {
140-
featureMap["deadline"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deadline.ValueInt32()
141-
}
142-
if !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deferral.IsNull() && !group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deferral.IsUnknown() {
143-
featureMap["deferral"] = group.DeploymentGroupPolicySettings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deferral.ValueInt32()
144-
}
145-
deviceConfigMap["featureDeploymentSettings"] = featureMap
146-
}
147-
148-
// Add required null fields from API example
149-
deviceConfigMap["updateFrequencyUI"] = nil
150-
deviceConfigMap["installDays"] = nil
151-
deviceConfigMap["installTime"] = nil
152-
deviceConfigMap["activeHourEndTime"] = nil
153-
deviceConfigMap["activeHourStartTime"] = nil
154-
155-
policyMap["deviceConfigurationSetting"] = deviceConfigMap
156-
}
157-
158117
groupMap["deploymentGroupPolicySettings"] = policyMap
159118
}
160119

@@ -178,21 +137,22 @@ func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel)
178137
requestBody["enableDriverUpdate"] = true // Default from API example
179138
}
180139

140+
// Scope Tags - convert from string set to int array for API
181141
if !data.ScopeTags.IsNull() && !data.ScopeTags.IsUnknown() {
182-
var scopeTags []types.String
183-
data.ScopeTags.ElementsAs(ctx, &scopeTags, false)
184-
185-
scopeTagsAPI := make([]int32, 0, len(scopeTags))
186-
for _, tag := range scopeTags {
187-
if !tag.IsNull() && !tag.IsUnknown() {
188-
if tagInt, err := strconv.Atoi(tag.ValueString()); err == nil {
189-
scopeTagsAPI = append(scopeTagsAPI, int32(tagInt))
190-
}
142+
var scopeTagStrings []string
143+
convert.FrameworkToGraphStringSet(ctx, data.ScopeTags, func(tags []string) {
144+
scopeTagStrings = tags
145+
})
146+
147+
scopeTagsAPI := make([]int, 0, len(scopeTagStrings))
148+
for _, tagStr := range scopeTagStrings {
149+
if tagInt, err := strconv.Atoi(tagStr); err == nil {
150+
scopeTagsAPI = append(scopeTagsAPI, tagInt)
191151
}
192152
}
193153
requestBody["scopeTags"] = scopeTagsAPI
194154
} else {
195-
requestBody["scopeTags"] = []int32{0} // Default
155+
requestBody["scopeTags"] = []int{0} // Default
196156
}
197157

198158
// Enabled Content Types
@@ -213,3 +173,202 @@ func constructResource(ctx context.Context, data *AutopatchGroupsResourceModel)
213173

214174
return requestBody, nil
215175
}
176+
177+
// constructDeploymentGroupPolicySettings constructs the deployment group policy settings
178+
func constructDeploymentGroupPolicySettings(ctx context.Context, settings *DeploymentGroupPolicySettings) (map[string]any, error) {
179+
policyMap := make(map[string]any)
180+
181+
convert.FrameworkToGraphString(settings.AadGroupName, func(val *string) {
182+
if val != nil {
183+
policyMap["aadGroupName"] = *val
184+
}
185+
})
186+
187+
convert.FrameworkToGraphBool(settings.IsUpdateSettingsModified, func(val *bool) {
188+
if val != nil {
189+
policyMap["isUpdateSettingsModified"] = *val
190+
}
191+
})
192+
193+
// Device Configuration Settings
194+
if settings.DeviceConfigurationSetting != nil {
195+
deviceConfigMap := make(map[string]any)
196+
197+
convert.FrameworkToGraphString(settings.DeviceConfigurationSetting.PolicyId, func(val *string) {
198+
if val != nil {
199+
deviceConfigMap["policyId"] = *val
200+
}
201+
})
202+
203+
convert.FrameworkToGraphString(settings.DeviceConfigurationSetting.UpdateBehavior, func(val *string) {
204+
if val != nil {
205+
deviceConfigMap["updateBehavior"] = *val
206+
}
207+
})
208+
209+
convert.FrameworkToGraphString(settings.DeviceConfigurationSetting.NotificationSetting, func(val *string) {
210+
if val != nil {
211+
deviceConfigMap["notificationSetting"] = *val
212+
}
213+
})
214+
215+
// Quality Deployment Settings
216+
if settings.DeviceConfigurationSetting.QualityDeploymentSettings != nil {
217+
qualityMap := make(map[string]any)
218+
convert.FrameworkToGraphInt32(settings.DeviceConfigurationSetting.QualityDeploymentSettings.Deadline, func(val *int32) {
219+
if val != nil {
220+
qualityMap["deadline"] = *val
221+
}
222+
})
223+
convert.FrameworkToGraphInt32(settings.DeviceConfigurationSetting.QualityDeploymentSettings.Deferral, func(val *int32) {
224+
if val != nil {
225+
qualityMap["deferral"] = *val
226+
}
227+
})
228+
convert.FrameworkToGraphInt32(settings.DeviceConfigurationSetting.QualityDeploymentSettings.GracePeriod, func(val *int32) {
229+
if val != nil {
230+
qualityMap["gracePeriod"] = *val
231+
}
232+
})
233+
deviceConfigMap["qualityDeploymentSettings"] = qualityMap
234+
}
235+
236+
// Feature Deployment Settings
237+
if settings.DeviceConfigurationSetting.FeatureDeploymentSettings != nil {
238+
featureMap := make(map[string]any)
239+
convert.FrameworkToGraphInt32(settings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deadline, func(val *int32) {
240+
if val != nil {
241+
featureMap["deadline"] = *val
242+
}
243+
})
244+
convert.FrameworkToGraphInt32(settings.DeviceConfigurationSetting.FeatureDeploymentSettings.Deferral, func(val *int32) {
245+
if val != nil {
246+
featureMap["deferral"] = *val
247+
}
248+
})
249+
deviceConfigMap["featureDeploymentSettings"] = featureMap
250+
}
251+
252+
// Add required null fields from API example
253+
deviceConfigMap["updateFrequencyUI"] = nil
254+
deviceConfigMap["installDays"] = nil
255+
deviceConfigMap["installTime"] = nil
256+
deviceConfigMap["activeHourEndTime"] = nil
257+
deviceConfigMap["activeHourStartTime"] = nil
258+
259+
policyMap["deviceConfigurationSetting"] = deviceConfigMap
260+
}
261+
262+
// DNF Update Cloud Setting
263+
if settings.DnfUpdateCloudSetting != nil {
264+
dnfMap := make(map[string]any)
265+
convert.FrameworkToGraphString(settings.DnfUpdateCloudSetting.PolicyId, func(val *string) {
266+
if val != nil {
267+
dnfMap["policyId"] = *val
268+
}
269+
})
270+
convert.FrameworkToGraphString(settings.DnfUpdateCloudSetting.ApprovalType, func(val *string) {
271+
if val != nil {
272+
dnfMap["approvalType"] = *val
273+
}
274+
})
275+
convert.FrameworkToGraphInt32(settings.DnfUpdateCloudSetting.DeploymentDeferralInDays, func(val *int32) {
276+
if val != nil {
277+
dnfMap["deploymentDeferralInDays"] = *val
278+
} else {
279+
dnfMap["deploymentDeferralInDays"] = nil
280+
}
281+
})
282+
policyMap["dnfUpdateCloudSetting"] = dnfMap
283+
}
284+
285+
// Office DCv2 Setting
286+
if settings.OfficeDCv2Setting != nil {
287+
officeMap := make(map[string]any)
288+
convert.FrameworkToGraphString(settings.OfficeDCv2Setting.PolicyId, func(val *string) {
289+
if val != nil {
290+
officeMap["policyId"] = *val
291+
}
292+
})
293+
convert.FrameworkToGraphInt32(settings.OfficeDCv2Setting.Deadline, func(val *int32) {
294+
if val != nil {
295+
officeMap["deadline"] = *val
296+
}
297+
})
298+
convert.FrameworkToGraphInt32(settings.OfficeDCv2Setting.Deferral, func(val *int32) {
299+
if val != nil {
300+
officeMap["deferral"] = *val
301+
}
302+
})
303+
convert.FrameworkToGraphBool(settings.OfficeDCv2Setting.HideUpdateNotifications, func(val *bool) {
304+
if val != nil {
305+
officeMap["hideUpdateNotifications"] = *val
306+
}
307+
})
308+
convert.FrameworkToGraphString(settings.OfficeDCv2Setting.TargetChannel, func(val *string) {
309+
if val != nil {
310+
officeMap["targetChannel"] = *val
311+
}
312+
})
313+
convert.FrameworkToGraphBool(settings.OfficeDCv2Setting.EnableAutomaticUpdate, func(val *bool) {
314+
if val != nil {
315+
officeMap["enableAutomaticUpdate"] = *val
316+
}
317+
})
318+
convert.FrameworkToGraphBool(settings.OfficeDCv2Setting.HideEnableDisableUpdate, func(val *bool) {
319+
if val != nil {
320+
officeMap["hideEnableDisableUpdate"] = *val
321+
}
322+
})
323+
convert.FrameworkToGraphBool(settings.OfficeDCv2Setting.EnableOfficeMgmt, func(val *bool) {
324+
if val != nil {
325+
officeMap["enableOfficeMgmt"] = *val
326+
}
327+
})
328+
convert.FrameworkToGraphString(settings.OfficeDCv2Setting.UpdatePath, func(val *string) {
329+
if val != nil {
330+
officeMap["updatePath"] = *val
331+
}
332+
})
333+
policyMap["officeDCv2Setting"] = officeMap
334+
}
335+
336+
// Edge DCv2 Setting
337+
if settings.EdgeDCv2Setting != nil {
338+
edgeMap := make(map[string]any)
339+
convert.FrameworkToGraphString(settings.EdgeDCv2Setting.PolicyId, func(val *string) {
340+
if val != nil {
341+
edgeMap["policyId"] = *val
342+
}
343+
})
344+
convert.FrameworkToGraphString(settings.EdgeDCv2Setting.TargetChannel, func(val *string) {
345+
if val != nil {
346+
edgeMap["targetChannel"] = *val
347+
}
348+
})
349+
policyMap["edgeDCv2Setting"] = edgeMap
350+
}
351+
352+
// Feature Update Anchor Cloud Setting
353+
if settings.FeatureUpdateAnchorCloudSetting != nil {
354+
featureAnchorMap := make(map[string]any)
355+
convert.FrameworkToGraphString(settings.FeatureUpdateAnchorCloudSetting.TargetOSVersion, func(val *string) {
356+
if val != nil {
357+
featureAnchorMap["targetOSVersion"] = *val
358+
}
359+
})
360+
convert.FrameworkToGraphBool(settings.FeatureUpdateAnchorCloudSetting.InstallLatestWindows10OnWindows11IneligibleDevice, func(val *bool) {
361+
if val != nil {
362+
featureAnchorMap["installLatestWindows10OnWindows11IneligibleDevice"] = *val
363+
}
364+
})
365+
convert.FrameworkToGraphString(settings.FeatureUpdateAnchorCloudSetting.PolicyId, func(val *string) {
366+
if val != nil {
367+
featureAnchorMap["policyId"] = *val
368+
}
369+
})
370+
policyMap["featureUpdateAnchorCloudSetting"] = featureAnchorMap
371+
}
372+
373+
return policyMap, nil
374+
}

0 commit comments

Comments
 (0)