generated from deploymenttheory/Template
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21 from deploymenttheory/feature-scaffolding
Feature scaffolding - refactor the CRUD function patterns
- Loading branch information
Showing
9 changed files
with
403 additions
and
367 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package common | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" | ||
) | ||
|
||
// IsNotFoundError checks if the given error is an OData error indicating that a resource was not found. | ||
// The function first verifies if the error is not nil. Then, it attempts to cast the error to an ODataError | ||
// type from the Microsoft Graph SDK. If the casting is successful, the function retrieves the main error | ||
// details using the GetErrorEscaped method of the ODataError struct. It then checks if the error code or | ||
// message contains indications of a "not found" error. | ||
// | ||
// Specifically, the function looks for the error codes "request_resourcenotfound" and "resourcenotfound" | ||
// (case-insensitive), or a message containing the phrase "not found" (case-insensitive). If any of these | ||
// conditions are met, the function returns true, indicating that the error is a "not found" error. | ||
// Otherwise, it returns false. | ||
// | ||
// The ODataError struct is part of the Microsoft Graph SDK and includes various methods and properties | ||
// to handle API errors. The main error details are encapsulated in a nested structure that provides | ||
// additional context, such as error codes and descriptive messages. | ||
// | ||
// Usage: | ||
// | ||
// if common.IsNotFoundError(err) { | ||
// // Handle the "not found" error case | ||
// } | ||
// | ||
// Parameters: | ||
// | ||
// err - The error to check. | ||
// | ||
// Returns: | ||
// | ||
// bool - True if the error indicates that a resource was not found, otherwise false. | ||
func IsNotFoundError(err error) bool { | ||
if err == nil { | ||
return false | ||
} | ||
|
||
odataErr, ok := err.(*odataerrors.ODataError) | ||
if !ok { | ||
return false | ||
} | ||
|
||
mainError := odataErr.GetErrorEscaped() | ||
if mainError != nil { | ||
if code := mainError.GetCode(); code != nil { | ||
switch strings.ToLower(*code) { | ||
case "request_resourcenotfound", "resourcenotfound": | ||
return true | ||
} | ||
} | ||
|
||
if message := mainError.GetMessage(); message != nil { | ||
if strings.Contains(strings.ToLower(*message), "not found") { | ||
return true | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
184 changes: 184 additions & 0 deletions
184
internal/resources/deviceandappmanagement/beta/assignmentFilter/crud.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package graphBetaAssignmentFilter | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/deploymenttheory/terraform-provider-microsoft365/internal/resources/common" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
"github.com/hashicorp/terraform-plugin-log/tflog" | ||
) | ||
|
||
// Create handles the Create operation. | ||
func (r *AssignmentFilterResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { | ||
var data AssignmentFilterResourceModel | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Starting creation of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
createTimeout, diags := data.Timeouts.Create(ctx, 30*time.Second) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
ctx, cancel := context.WithTimeout(ctx, createTimeout) | ||
defer cancel() | ||
|
||
requestBody, err := constructResource(ctx, &data) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"Error constructing assignment filter", | ||
fmt.Sprintf("Could not construct resource: %s_%s: %s", r.ProviderTypeName, r.TypeName, err.Error()), | ||
) | ||
return | ||
} | ||
|
||
assignmentFilter, err := r.client.DeviceManagement().AssignmentFilters().Post(ctx, requestBody, nil) | ||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"Error creating assignment filter", | ||
fmt.Sprintf("Could not create assignment filter: %s", err.Error()), | ||
) | ||
return | ||
} | ||
|
||
data.ID = types.StringValue(*assignmentFilter.GetId()) | ||
|
||
r.isCreate = true | ||
|
||
readResp := resource.ReadResponse{ | ||
State: resp.State, | ||
} | ||
r.Read(ctx, resource.ReadRequest{State: resp.State}, &readResp) | ||
resp.Diagnostics.Append(readResp.Diagnostics...) | ||
|
||
r.isCreate = false | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Finished creation of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
} | ||
|
||
// Read handles the read operation and stating. | ||
func (r *AssignmentFilterResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { | ||
var data AssignmentFilterResourceModel | ||
|
||
diags := req.State.Get(ctx, &data) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
readTimeout, diags := data.Timeouts.Read(ctx, 30*time.Second) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
ctx, cancel := context.WithTimeout(ctx, readTimeout) | ||
defer cancel() | ||
|
||
remoteResource, err := r.client.DeviceManagement().AssignmentFilters().ByDeviceAndAppManagementAssignmentFilterId(data.ID.ValueString()).Get(ctx, nil) | ||
if err != nil { | ||
if common.IsNotFoundError(err) && !r.isCreate { | ||
resp.Diagnostics.AddWarning( | ||
"Resource Not Found", | ||
fmt.Sprintf("The resource: %s_%s with ID %s was not found and will be removed from the state.", r.ProviderTypeName, r.TypeName, data.ID.ValueString()), | ||
) | ||
resp.State.RemoveResource(ctx) | ||
return | ||
} | ||
resp.Diagnostics.AddError( | ||
"Error reading assignment filter", | ||
fmt.Sprintf("Could not read resource: %s_%s: %s", r.ProviderTypeName, r.TypeName, err.Error()), | ||
) | ||
return | ||
} | ||
|
||
mapRemoteStateToTerraform(&data, remoteResource) | ||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
} | ||
|
||
// Update handles the Update operation. | ||
func (r *AssignmentFilterResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { | ||
var data AssignmentFilterResourceModel | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Starting Update of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
|
||
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
updateTimeout, diags := data.Timeouts.Update(ctx, 30*time.Second) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
ctx, cancel := context.WithTimeout(ctx, updateTimeout) | ||
defer cancel() | ||
|
||
requestBody, err := constructResource(ctx, &data) | ||
|
||
if err != nil { | ||
resp.Diagnostics.AddError( | ||
"Error constructing assignment filter", | ||
fmt.Sprintf("Could not construct resource: %s_%s: %s", r.ProviderTypeName, r.TypeName, err.Error()), | ||
) | ||
return | ||
} | ||
|
||
_, err = r.client.DeviceManagement().AssignmentFilters().ByDeviceAndAppManagementAssignmentFilterId(data.ID.ValueString()).Patch(ctx, requestBody, nil) | ||
if err != nil { | ||
if common.IsNotFoundError(err) && !r.isCreate { | ||
resp.Diagnostics.AddWarning( | ||
"Resource Not Found", | ||
fmt.Sprintf("The resource: %s_%s with ID %s was not found and will be removed from the state.", r.ProviderTypeName, r.TypeName, data.ID.ValueString()), | ||
) | ||
resp.State.RemoveResource(ctx) | ||
return | ||
} | ||
resp.Diagnostics.AddError( | ||
"Error reading assignment filter", | ||
fmt.Sprintf("Could not update resource: %s_%s: %s", r.ProviderTypeName, r.TypeName, err.Error()), | ||
) | ||
return | ||
} | ||
|
||
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Finished Update of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
} | ||
|
||
// Delete handles the Delete operation. | ||
func (r *AssignmentFilterResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { | ||
var data AssignmentFilterResourceModel | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Starting deletion of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
|
||
resp.Diagnostics.Append(req.State.Get(ctx, &data)...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
deleteTimeout, diags := data.Timeouts.Delete(ctx, 30*time.Second) | ||
resp.Diagnostics.Append(diags...) | ||
if resp.Diagnostics.HasError() { | ||
return | ||
} | ||
ctx, cancel := context.WithTimeout(ctx, deleteTimeout) | ||
defer cancel() | ||
|
||
err := r.client.DeviceManagement().AssignmentFilters().ByDeviceAndAppManagementAssignmentFilterId(data.ID.ValueString()).Delete(ctx, nil) | ||
if err != nil { | ||
resp.Diagnostics.AddError(fmt.Sprintf("Client error when deleting %s_%s", r.ProviderTypeName, r.TypeName), err.Error()) | ||
return | ||
} | ||
|
||
tflog.Debug(ctx, fmt.Sprintf("Completed deletion of resource: %s_%s", r.ProviderTypeName, r.TypeName)) | ||
|
||
resp.State.RemoveResource(ctx) | ||
} |
Oops, something went wrong.