Skip to content

Commit 74165da

Browse files
authored
Merge pull request #63 from atlassian/release-from-bitbucket
Release from bitbucket
2 parents 8477f3c + 184c518 commit 74165da

28 files changed

+339
-18
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## v1.1.8
2+
3+
#### Product:
4+
5+
- Add `delete_default_actions` attribute in the API Integration resource. Fix Order attribute problem on Notification Policy and Alert Policy resources. Handle external deletion of resources by setting their state to Deleted on Read operations. This ensures that the provider can handle resources that have been deleted outside of Terraform, preventing state inconsistencies.
16
## v1.1.7
27

38
#### Product:

bitbucket-pipelines.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ definitions:
3838
- golangci-lint run -v
3939
- step: &replicate-to-github
4040
name: Replicate main branch to GitHub
41+
image: python:3.7
4142
script:
42-
- git fetch --unshallow && git checkout main && git pull
43+
- pip install git-filter-repo
44+
- git fetch --unshallow --all && git checkout main && git pull
4345
- git remote rename origin bitbucket
44-
- git remote add origin [email protected]:atlassian/terraform-provider-atlassian-operations.git
45-
- git pull --rebase
46+
- git remote add github [email protected]:atlassian/terraform-provider-atlassian-operations.git
4647
- git checkout -b release-from-bitbucket
47-
- git push --set-upstream origin release-from-bitbucket --force
48+
- git pull --rebase --strategy-option=ours github main
49+
- git filter-repo --commit-callback 'commit.committer_name = commit.author_name; commit.committer_email = commit.author_email;' --refs $(git merge-base github/main release-from-bitbucket)...HEAD --force
50+
- git push --set-upstream github release-from-bitbucket --force
4851

4952
pipelines:
5053
default:

docs/data-sources/team.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Team data source
2626

2727
### Read-Only
2828

29+
- `delete_default_resources` (Boolean) Set to true to remove default escalation and schedule for newly created team. Be careful its also changes that team routing rule to None. That means you have to define routing rule as well. Defaults to false.
2930
- `description` (String) A detailed description of the team's purpose, responsibilities, and scope of operations.
3031
- `display_name` (String) The human-readable name of the team as it appears in the Atlassian interface.
3132
- `member` (Attributes Set) The set of users who are members of this team. Each member has their own role and permissions. (see [below for nested schema](#nestedatt--member))

docs/resources/api_integration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ description: |-
2222

2323
### Optional
2424

25+
- `delete_default_actions` (Boolean) Set to true to remove default actions for this API integration. This is useful for custom integrations where default actions are not applicable. Defaults to false.
2526
- `enabled` (Boolean) Whether the API integration is enabled. When disabled, the integration will not process any requests. Defaults to false.
2627
- `team_id` (String) The ID of the team that owns this API integration. Cannot be changed after creation.
2728
- `type_specific_properties` (String) JSON object containing integration-specific configuration properties. The schema depends on the integration type.

internal/dto/alert_policy_dto.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,16 @@ type ActionDto struct {
6262
Type string `json:"type"`
6363
Parameters map[string]interface{} `json:"parameters"`
6464
}
65+
66+
type BaseAlertPolicyDto struct {
67+
ID string `json:"id,omitempty"`
68+
Type string `json:"type"`
69+
Name string `json:"name"`
70+
Enabled bool `json:"enabled"`
71+
Order float64 `json:"order,omitempty"`
72+
}
73+
74+
type AlertPolicyListDto struct {
75+
Values []BaseAlertPolicyDto `json:"values"`
76+
Links LinksDto `json:"links"`
77+
}

internal/dto/integration_action_dto.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,15 @@ type ActionMappingDto struct {
3434
Type string `json:"type"`
3535
Parameter map[string]interface{} `json:"parameter,omitempty"`
3636
}
37+
38+
type BaseIntegrationActionDto struct {
39+
ID string `json:"id,omitempty"`
40+
Type string `json:"type"`
41+
Name string `json:"name"`
42+
Direction string `json:"direction"`
43+
Domain string `json:"domain"`
44+
}
45+
type IntegrationActionListDto struct {
46+
Values []BaseIntegrationActionDto `json:"values"`
47+
Links LinksDto `json:"links"`
48+
}

internal/dto/notification_policy_dto.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,16 @@ type NotificationConditionDto struct {
7474
ExpectedValue string `json:"expectedValue"`
7575
Order int `json:"order,omitempty"`
7676
}
77+
78+
type BaseNotificationPolicyDto struct {
79+
ID string `json:"id,omitempty"`
80+
Type string `json:"type"`
81+
Name string `json:"name"`
82+
Enabled bool `json:"enabled"`
83+
Order float64 `json:"order,omitempty"`
84+
}
85+
86+
type NotificationPolicyListDto struct {
87+
Values []BaseNotificationPolicyDto `json:"values"`
88+
Links LinksDto `json:"links"`
89+
}

internal/provider/alert_policy_resource.go

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package provider
33
import (
44
"context"
55
"fmt"
6+
"net/url"
67
"strings"
78

89
"github.com/atlassian/terraform-provider-atlassian-operations/internal/dto"
@@ -113,8 +114,9 @@ func (r *AlertPolicyResource) Create(ctx context.Context, req resource.CreateReq
113114
return
114115
}
115116

117+
order := getAlertPolicyOrder(ctx, r.clientConfiguration, data.TeamID.ValueString(), alertPolicyDto.ID)
116118
// Update state with response
117-
result, _ := AlertPolicyDtoToModel(ctx, alertPolicyDto)
119+
result, _ := AlertPolicyDtoToModel(ctx, order, alertPolicyDto)
118120
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
119121
}
120122

@@ -149,6 +151,12 @@ func (r *AlertPolicyResource) Read(ctx context.Context, req resource.ReadRequest
149151
return
150152
}
151153

154+
if httpResp.GetStatusCode() == 404 {
155+
resp.State.RemoveResource(ctx)
156+
157+
return
158+
}
159+
152160
if httpResp.IsError() {
153161
statusCode := httpResp.GetStatusCode()
154162
errorResponse := httpResp.GetErrorBody()
@@ -168,7 +176,9 @@ func (r *AlertPolicyResource) Read(ctx context.Context, req resource.ReadRequest
168176
return
169177
}
170178

171-
result, _ := AlertPolicyDtoToModel(ctx, &alertPolicyDto)
179+
order := getAlertPolicyOrder(ctx, r.clientConfiguration, data.TeamID.ValueString(), alertPolicyDto.ID)
180+
181+
result, _ := AlertPolicyDtoToModel(ctx, order, &alertPolicyDto)
172182
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
173183
}
174184

@@ -222,8 +232,8 @@ func (r *AlertPolicyResource) Update(ctx context.Context, req resource.UpdateReq
222232
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to update alert policy, got error: %s", err))
223233
return
224234
}
225-
226-
result, _ := AlertPolicyDtoToModel(ctx, alertPolicyDto)
235+
order := getAlertPolicyOrder(ctx, r.clientConfiguration, data.TeamID.ValueString(), alertPolicyDto.ID)
236+
result, _ := AlertPolicyDtoToModel(ctx, order, alertPolicyDto)
227237
resp.Diagnostics.Append(resp.State.Set(ctx, result)...)
228238
}
229239

@@ -287,3 +297,72 @@ func (r *AlertPolicyResource) ImportState(ctx context.Context, req resource.Impo
287297
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("team_id"), idParts[1])...)
288298
}
289299
}
300+
301+
func getAlertPolicyOrder(ctx context.Context, configuration dto.AtlassianOpsProviderModel, teamId string, alertPolicyId string) int64 {
302+
// list alert policies find the one we just created, and get its order value
303+
listAlertPoliciesResponse := &dto.AlertPolicyListDto{}
304+
baseURL := fmt.Sprintf("/v1/teams/%s/policies", teamId)
305+
queryParams := map[string]string{
306+
"type": "alert",
307+
}
308+
var order int64
309+
doneLooping := false
310+
311+
for !doneLooping {
312+
req := httpClientHelpers.
313+
GenerateJsmOpsClientRequest(configuration).
314+
JoinBaseUrl(baseURL).
315+
Method(httpClient.GET).
316+
SetBodyParseObject(&listAlertPoliciesResponse).
317+
SetQueryParams(queryParams)
318+
319+
httpResp, err := req.Send()
320+
321+
if httpResp == nil {
322+
tflog.Error(ctx, "Client Error. Unable to list alert policies, got nil response")
323+
return 0
324+
}
325+
if httpResp.IsError() {
326+
statusCode := httpResp.GetStatusCode()
327+
errorResponse := httpResp.GetErrorBody()
328+
if errorResponse != nil {
329+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to list alert policies, status code: %d. Got response: %s", statusCode, *errorResponse))
330+
} else {
331+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to list alert policies, got http response: %d", statusCode))
332+
}
333+
return 0
334+
}
335+
if err != nil {
336+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to list alert policies, got error: %s", err))
337+
return 0
338+
}
339+
340+
for _, policy := range listAlertPoliciesResponse.Values {
341+
if policy.ID == alertPolicyId {
342+
order = int64(policy.Order)
343+
return order
344+
}
345+
}
346+
347+
if listAlertPoliciesResponse.Links.Next == "" {
348+
doneLooping = true
349+
} else {
350+
nextURL := listAlertPoliciesResponse.Links.Next
351+
parsedURL, err := url.Parse(nextURL)
352+
if err != nil {
353+
tflog.Error(ctx, fmt.Sprintf("Client Error. Unable to parse next URL, got error: %s", err))
354+
return 0
355+
}
356+
urlValues := parsedURL.Query()
357+
queryParams = make(map[string]string)
358+
for key, values := range urlValues {
359+
if len(values) > 0 {
360+
queryParams[key] = values[0]
361+
}
362+
}
363+
baseURL = parsedURL.Path
364+
listAlertPoliciesResponse = &dto.AlertPolicyListDto{}
365+
}
366+
}
367+
return order
368+
}

internal/provider/alert_policy_resource_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ resource "atlassian-operations_alert_policy" "test" {
5656
type = "alert"
5757
enabled = true
5858
message = "Test alert message"
59+
order = 1
5960
6061
filter = {
6162
type = "match-any-condition"

internal/provider/api_integration_resource.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ func (r *ApiIntegrationResource) Create(ctx context.Context, req resource.Create
104104
return
105105
}
106106

107+
if data.DeleteDefaultActions.ValueBool() {
108+
// List default actions using the API Integration ID then using delete action endpoint delete each action
109+
err = listDefaultActionsAndDelete(r.clientConfiguration, dtoObj.Id)
110+
if err != nil {
111+
tflog.Warn(ctx, fmt.Sprintf("Error deleting default actions for API integration: %s", err))
112+
resp.Diagnostics.AddWarning("Error Deleting Default Actions", fmt.Sprintf("Unable to delete default actions for API integration: %s", err))
113+
}
114+
}
115+
107116
data = ApiIntegrationDtoToModel(dtoObj, data)
108117

109118
tflog.Trace(ctx, "Created the ApiIntegrationResource")
@@ -113,6 +122,36 @@ func (r *ApiIntegrationResource) Create(ctx context.Context, req resource.Create
113122
tflog.Trace(ctx, "Saved the ApiIntegrationResource into Terraform state")
114123
}
115124

125+
func listDefaultActionsAndDelete(configuration dto.AtlassianOpsProviderModel, integrationId string) error {
126+
defaultActions := dto.IntegrationActionListDto{}
127+
httpResp, err := httpClientHelpers.
128+
GenerateJsmOpsClientRequest(configuration).
129+
JoinBaseUrl(fmt.Sprintf("v1/integrations/%s/actions", integrationId)).
130+
Method(httpClient.GET).
131+
SetBodyParseObject(&defaultActions).
132+
Send()
133+
if err != nil {
134+
return fmt.Errorf("unable to list default actions, got error: %s couldn't delete default actions automatically. Please delete it through UI", err)
135+
}
136+
if httpResp.IsError() {
137+
return fmt.Errorf("unable to list default actions, couldn't delete default actions automatically, got http response: %d. Please delete it through UI", httpResp.GetStatusCode())
138+
}
139+
for _, action := range defaultActions.Values {
140+
httpResp, err := httpClientHelpers.
141+
GenerateJsmOpsClientRequest(configuration).
142+
JoinBaseUrl(fmt.Sprintf("v1/integrations/%s/actions/%s", integrationId, action.ID)).
143+
Method(httpClient.DELETE).
144+
Send()
145+
if err != nil {
146+
return fmt.Errorf("unable to delete default action %s, got error: %s couldn't delete default actions automatically. Please delete it through UI", action.ID, err)
147+
}
148+
if httpResp.IsError() {
149+
return fmt.Errorf("unable to delete default action %s, couldn't delete default actions automatically, got http response: %d. Please delete it through UI", action.Name, httpResp.GetStatusCode())
150+
}
151+
}
152+
return nil
153+
}
154+
116155
func (r *ApiIntegrationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
117156
var data dataModels.ApiIntegrationModel
118157

@@ -132,6 +171,10 @@ func (r *ApiIntegrationResource) Read(ctx context.Context, req resource.ReadRequ
132171
if httpResp == nil {
133172
tflog.Error(ctx, "Client Error. Unable to read api integration, got nil response")
134173
resp.Diagnostics.AddError("Client Error", "Unable to read api integration, got nil response")
174+
} else if httpResp.GetStatusCode() == 404 {
175+
resp.State.RemoveResource(ctx)
176+
177+
return
135178
} else if httpResp.IsError() {
136179
statusCode := httpResp.GetStatusCode()
137180
errorResponse := httpResp.GetErrorBody()

0 commit comments

Comments
 (0)