Skip to content

Commit 535d512

Browse files
mawasileCopilot
andauthored
fix: handle 404 status as deleted resource instead of fatal error (#1011)
* fix: handle 404 status as deleted resource instead of fatal error * Update internal/services/licensing/api_licensing.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update internal/services/environment/api_environment.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: add changelog entry for handling 404 status as deleted resource --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 871c7ed commit 535d512

File tree

8 files changed

+41
-40
lines changed

8 files changed

+41
-40
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: fixed
2+
body: Handle 404 status on environment read/delete as deleted resource instead of fatal error
3+
time: 2025-12-16T12:13:54.883340024Z
4+
custom:
5+
Issue: "1006"

internal/services/authorization/api_user.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package authorization
55

66
import (
77
"context"
8-
"errors"
98
"fmt"
109
"net/http"
1110
"net/url"
@@ -476,10 +475,9 @@ func (client *client) getEnvironment(ctx context.Context, environmentId string)
476475
apiUrl.RawQuery = values.Encode()
477476

478477
env := environmentIdDto{}
479-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
478+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
480479
if err != nil {
481-
var httpError *customerrors.UnexpectedHttpStatusCodeError
482-
if errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
480+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
483481
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("environment %s not found", environmentId))
484482
}
485483
return nil, err

internal/services/dlp_policy/api_dlp_policy.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package dlp_policy
55

66
import (
77
"context"
8-
"errors"
98
"fmt"
109
"net/http"
1110
"net/url"
@@ -67,10 +66,9 @@ func (client *client) GetPolicy(ctx context.Context, name string) (*dlpPolicyMod
6766
Path: fmt.Sprintf("providers/PowerPlatform.Governance/v2/policies/%s", name),
6867
}
6968
policy := dlpPolicyDto{}
70-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &policy)
69+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &policy)
7170
if err != nil {
72-
var httpError *customerrors.UnexpectedHttpStatusCodeError
73-
if errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
71+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
7472
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("DLP Policy '%s' not found", name))
7573
}
7674
return nil, err

internal/services/environment/api_environment.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,9 @@ func (client *Client) GetEnvironment(ctx context.Context, environmentId string)
255255
apiUrl.RawQuery = values.Encode()
256256

257257
env := EnvironmentDto{}
258-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
258+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
259259
if err != nil {
260-
var httpError *customerrors.UnexpectedHttpStatusCodeError
261-
if errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
260+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
262261
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("environment '%s' not found", environmentId))
263262
}
264263
return nil, err
@@ -294,7 +293,11 @@ func (client *Client) deleteEnvironmentWithRetry(ctx context.Context, environmen
294293
Message: "Deleted using Power Platform Terraform Provider",
295294
}
296295

297-
response, err := client.Api.Execute(ctx, nil, "DELETE", apiUrl.String(), nil, environmentDelete, []int{http.StatusNoContent, http.StatusAccepted, http.StatusConflict, http.StatusNotFound}, nil)
296+
expectedStatusCodes := []int{http.StatusNoContent, http.StatusAccepted, http.StatusConflict, http.StatusNotFound}
297+
response, err := client.Api.Execute(ctx, nil, "DELETE", apiUrl.String(), nil, environmentDelete, expectedStatusCodes, nil)
298+
if err != nil {
299+
return err
300+
}
298301

299302
// Handle HTTP 404 case - if the environment is not found, consider it already deleted
300303
if response != nil && response.HttpResponse.StatusCode == http.StatusNotFound {
@@ -313,11 +316,6 @@ func (client *Client) deleteEnvironmentWithRetry(ctx context.Context, environmen
313316
return client.deleteEnvironmentWithRetry(ctx, environmentId, retryCount+1)
314317
}
315318

316-
var httpError *customerrors.UnexpectedHttpStatusCodeError
317-
if errors.As(err, &httpError) {
318-
return fmt.Errorf("unexpected HTTP Status %s; Body: %s", httpError.StatusText, httpError.Body)
319-
}
320-
321319
tflog.Debug(ctx, "Environment Deletion Operation HTTP Status: '"+response.HttpResponse.Status+"'")
322320
tflog.Debug(ctx, "Waiting for environment deletion operation to complete")
323321

internal/services/licensing/api_licensing.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package licensing
55

66
import (
77
"context"
8-
"errors"
98
"fmt"
109
"net/http"
1110
"net/url"
@@ -55,13 +54,15 @@ func (client *Client) GetBillingPolicy(ctx context.Context, billingId string) (*
5554
apiUrl.RawQuery = values.Encode()
5655

5756
policy := BillingPolicyDto{}
58-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &policy)
57+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &policy)
5958

60-
var httpError *customerrors.UnexpectedHttpStatusCodeError
61-
if err != nil && errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
62-
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("Billing Policy with ID '%s' not found", billingId))
59+
if err != nil {
60+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
61+
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("Billing Policy with ID '%s' not found", billingId))
62+
}
63+
return nil, err
6364
}
64-
return &policy, err
65+
return &policy, nil
6566
}
6667

6768
func (client *Client) CreateBillingPolicy(ctx context.Context, policyToCreate billingPolicyCreateDto) (*BillingPolicyDto, error) {
@@ -147,10 +148,9 @@ func (client *Client) GetEnvironmentsForBillingPolicy(ctx context.Context, billi
147148
apiUrl.RawQuery = values.Encode()
148149

149150
billingPolicyEnvironments := BillingPolicyEnvironmentsArrayResponseDto{}
150-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &billingPolicyEnvironments)
151+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &billingPolicyEnvironments)
151152
if err != nil {
152-
var httpError *customerrors.UnexpectedHttpStatusCodeError
153-
if errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
153+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
154154
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("Billing Policy with ID '%s' not found", billingId))
155155
}
156156
return nil, err

internal/services/rest/api_rest.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/hashicorp/terraform-plugin-framework/types"
1414
"github.com/hashicorp/terraform-plugin-log/tflog"
1515
"github.com/microsoft/terraform-provider-power-platform/internal/api"
16-
"github.com/microsoft/terraform-provider-power-platform/internal/customerrors"
1716
)
1817

1918
func newWebApiClient(apiClient *api.Client) client {
@@ -47,28 +46,27 @@ func (client *client) SendOperation(ctx context.Context, operation *DataverseWeb
4746
expectedStatusCodes = []int{http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent}
4847
}
4948

50-
res, err := client.ExecuteApiRequest(ctx, operation.Scope.ValueStringPointer(), url, method, body, headers, expectedStatusCodes)
51-
var unexpected customerrors.UnexpectedHttpStatusCodeError
52-
if errors.As(err, &unexpected) {
49+
resp, err := client.ExecuteApiRequest(ctx, operation.Scope.ValueStringPointer(), url, method, body, headers, expectedStatusCodes)
50+
if resp != nil && resp.HttpResponse != nil && resp.HttpResponse.StatusCode >= 400 {
5351
return types.ObjectUnknown(map[string]attr.Type{
5452
"body": types.StringType,
55-
}), unexpected
53+
}), err
5654
}
5755

58-
if res != nil && res.HttpResponse != nil {
59-
tflog.Trace(ctx, fmt.Sprintf("SendOperation Response: %v", res.BodyAsBytes))
60-
tflog.Trace(ctx, fmt.Sprintf("SendOperation Response Status: %v", res.HttpResponse.Status))
56+
if resp != nil && resp.HttpResponse != nil {
57+
tflog.Trace(ctx, fmt.Sprintf("SendOperation Response: %v", resp.BodyAsBytes))
58+
tflog.Trace(ctx, fmt.Sprintf("SendOperation Response Status: %v", resp.HttpResponse.Status))
6159
}
6260

6361
output := map[string]attr.Value{
6462
"body": types.StringNull(),
6563
}
6664

67-
if res == nil && err != nil {
65+
if resp == nil && err != nil {
6866
output["body"] = types.StringValue(err.Error())
6967
} else {
70-
if len(res.BodyAsBytes) > 0 {
71-
output["body"] = types.StringValue(string(res.BodyAsBytes))
68+
if len(resp.BodyAsBytes) > 0 {
69+
output["body"] = types.StringValue(string(resp.BodyAsBytes))
7270
}
7371
}
7472
o := types.ObjectValueMust(map[string]attr.Type{

internal/services/rest/resource_rest_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ func TestUnitTestRest_Validate_Create(t *testing.T) {
206206
return httpmock.NewStringResponse(http.StatusCreated, httpmock.File("tests/resource/Web_Api_Validate_Create/post_account.json").String()), nil
207207
})
208208

209+
httpmock.RegisterResponder("DELETE", `https://00000000-0000-0000-0000-000000000001.crm4.dynamics.com/api/data/v9.2/accounts(00000000-0000-0000-0000-000000000001)`,
210+
func(req *http.Request) (*http.Response, error) {
211+
return httpmock.NewStringResponse(http.StatusNoContent, ""), nil
212+
})
213+
209214
resource.Test(t, resource.TestCase{
210215
IsUnitTest: true,
211216
ProtoV6ProviderFactories: mocks.TestUnitTestProtoV6ProviderFactories,

internal/services/solution/api_solution.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,9 @@ func (client *Client) getEnvironment(ctx context.Context, environmentId string)
396396
apiUrl.RawQuery = values.Encode()
397397

398398
env := environmentIdDto{}
399-
_, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
399+
resp, err := client.Api.Execute(ctx, nil, "GET", apiUrl.String(), nil, nil, []int{http.StatusOK}, &env)
400400
if err != nil {
401-
var httpError *customerrors.UnexpectedHttpStatusCodeError
402-
if errors.As(err, &httpError) && httpError.StatusCode == http.StatusNotFound {
401+
if resp != nil && resp.HttpResponse.StatusCode == http.StatusNotFound {
403402
return nil, customerrors.WrapIntoProviderError(err, customerrors.ErrorCode(constants.ERROR_OBJECT_NOT_FOUND), fmt.Sprintf("environment %s not found", environmentId))
404403
}
405404
return nil, err

0 commit comments

Comments
 (0)