Skip to content

Commit 311f275

Browse files
authored
Refactor Sentry Project resource (#228)
* Refactor tests * Refactor resourceSentryProjectUpdate
1 parent b5d9226 commit 311f275

10 files changed

+278
-150
lines changed

docs/resources/project.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ resource "sentry_project" "default" {
4141
- `platform` (String) The optional platform for this project.
4242
- `resolve_age` (Number) Hours in which an issue is automatically resolve if not seen after this amount of time.
4343
- `slug` (String) The optional slug for this project.
44-
- `team` (String, Deprecated) The slug of the team to create the project for. One of 'team' or 'teams' must be set.
45-
- `teams` (Set of String) The slugs of the teams to create the project for. One of 'team' or 'teams' must be set.
44+
- `team` (String, Deprecated) The slug of the team to create the project for. **Deprecated** Use `teams` instead.
45+
- `teams` (Set of String) The slugs of the teams to create the project for.
4646

4747
### Read-Only
4848

sentry/data_source_sentry_issue_alert_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func TestAccSentryIssueAlertDataSource_basic(t *testing.T) {
6161
}
6262

6363
func testAccSentryIssueAlertDataSourceConfig(teamName, projectName, alertName string) string {
64-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
64+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
6565
resource "sentry_issue_alert" "test" {
6666
organization = sentry_project.test.organization
6767
project = sentry_project.test.id

sentry/data_source_sentry_key_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func testAccCheckSentryKeyDataSourceID(n string) resource.TestCheckFunc {
9494
}
9595

9696
func testAccSentryKeyDataSourceConfig(teamName, projectName string) string {
97-
return testAccSentryProjectConfig(teamName, projectName) + `
97+
return testAccSentryProjectConfig_team(teamName, projectName) + `
9898
data "sentry_key" "test" {
9999
organization = sentry_project.test.organization
100100
project = sentry_project.test.id
@@ -104,7 +104,7 @@ data "sentry_key" "test" {
104104

105105
// Testing first parameter
106106
func testAccSentryKeyDataSourceConfig_first(teamName, projectName string) string {
107-
return testAccSentryProjectConfig(teamName, projectName) + `
107+
return testAccSentryProjectConfig_team(teamName, projectName) + `
108108
resource "sentry_key" "test_2" {
109109
organization = sentry_project.test.organization
110110
project = sentry_project.test.id
@@ -123,7 +123,7 @@ data "sentry_key" "test" {
123123

124124
// Testing name parameter
125125
func testAccSentryKeyDataSourceConfig_name(teamName, projectName, keyName string) string {
126-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
126+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
127127
resource "sentry_key" "test_2" {
128128
organization = sentry_project.test.organization
129129
project = sentry_project.test.id

sentry/data_source_sentry_metric_alert_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func TestAccSentryMetricAlertDataSource_basic(t *testing.T) {
6969
}
7070

7171
func testAccSentryMetricAlertDataSourceConfig(teamName, projectName, alertName string) string {
72-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
72+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
7373
resource "sentry_metric_alert" "test" {
7474
organization = sentry_project.test.organization
7575
project = sentry_project.test.id

sentry/provider.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func NewProvider(version string) func() *schema.Provider {
2222
Description: "The authentication token used to connect to Sentry. The value can be sourced from " +
2323
"the `SENTRY_AUTH_TOKEN` environment variable.",
2424
Type: schema.TypeString,
25-
Required: true,
25+
Optional: true,
2626
DefaultFunc: schema.MultiEnvDefaultFunc([]string{"SENTRY_AUTH_TOKEN", "SENTRY_TOKEN"}, nil),
2727
Sensitive: true,
2828
},

sentry/resource_sentry_issue_alert_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ func testAccCheckSentryIssueAlertExists(n string, alertID *string) resource.Test
221221
}
222222

223223
func testAccSentryIssueAlertConfig(teamName, projectName, alertName string) string {
224-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
224+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
225225
resource "sentry_issue_alert" "test" {
226226
organization = sentry_project.test.organization
227227
project = sentry_project.test.id

sentry/resource_sentry_key_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func testAccSentryKeyImportStateIdFunc(n string) resource.ImportStateIdFunc {
179179
}
180180

181181
func testAccSentryKeyConfig(teamName, projectName, keyName string) string {
182-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
182+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
183183
resource "sentry_key" "test" {
184184
organization = sentry_project.test.organization
185185
project = sentry_project.test.id
@@ -189,7 +189,7 @@ resource "sentry_key" "test" {
189189
}
190190

191191
func testAccSentryKeyConfig_rateLimit(teamName, projectName, keyName, rateLimitWindow, rateLimitCount string) string {
192-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
192+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
193193
resource "sentry_key" "test" {
194194
organization = sentry_project.test.organization
195195
project = sentry_project.test.id

sentry/resource_sentry_metric_alert_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func testAccCheckSentryMetricAlertExists(n string, gotAlertID *string) resource.
115115
}
116116

117117
func testAccSentryMetricAlertConfig(teamName, projectName, alertName string) string {
118-
return testAccSentryProjectConfig(teamName, projectName) + fmt.Sprintf(`
118+
return testAccSentryProjectConfig_team(teamName, projectName) + fmt.Sprintf(`
119119
resource "sentry_metric_alert" "test" {
120120
organization = sentry_project.test.organization
121121
project = sentry_project.test.id

sentry/resource_sentry_project.go

+63-60
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sentry
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67

78
"github.com/hashicorp/go-multierror"
@@ -31,19 +32,20 @@ func resourceSentryProject() *schema.Resource {
3132
Required: true,
3233
},
3334
"team": {
34-
Description: "The slug of the team to create the project for. One of 'team' or 'teams' must be set.",
35-
Type: schema.TypeString,
36-
Deprecated: "To be replaced by 'teams' in a future release.",
37-
Optional: true,
35+
Description: "The slug of the team to create the project for. **Deprecated** Use `teams` instead.",
36+
Type: schema.TypeString,
37+
Deprecated: "Use `teams` instead.",
38+
ConflictsWith: []string{"teams"},
39+
Optional: true,
3840
},
3941
"teams": {
40-
Description: "The slugs of the teams to create the project for. One of 'team' or 'teams' must be set.",
42+
Description: "The slugs of the teams to create the project for.",
4143
Type: schema.TypeSet,
4244
Elem: &schema.Schema{
4345
Type: schema.TypeString,
4446
},
45-
ExactlyOneOf: []string{"team"},
46-
Optional: true,
47+
ConflictsWith: []string{"team"},
48+
Optional: true,
4749
},
4850
"name": {
4951
Description: "The name for the project.",
@@ -124,32 +126,42 @@ func resourceSentryProjectCreate(ctx context.Context, d *schema.ResourceData, me
124126
client := meta.(*sentry.Client)
125127

126128
org := d.Get("organization").(string)
127-
team := d.Get("team").(string)
128-
var teams []interface{}
129-
if team == "" {
129+
130+
team, teamOk := d.GetOk("team")
131+
teams, teamsOk := d.GetOk("teams")
132+
if !teamOk && !teamsOk {
133+
return diag.FromErr(errors.New("one of team or teams must be configured"))
134+
}
135+
136+
var initialTeam string
137+
if teamOk {
138+
initialTeam = team.(string)
139+
} else {
130140
// Since `Set.List()` produces deterministic ordering, `teams[0]` should always
131141
// resolve to the same value given the same `teams`.
132-
teams = d.Get("teams").(*schema.Set).List()
133-
team = teams[0].(string)
142+
// Pick the first team when creating the project.
143+
initialTeam = teams.(*schema.Set).List()[0].(string)
134144
}
145+
135146
params := &sentry.CreateProjectParams{
136147
Name: d.Get("name").(string),
137148
Slug: d.Get("slug").(string),
138149
}
139150

140151
tflog.Debug(ctx, "Creating Sentry project", map[string]interface{}{
141-
"team": team,
142-
"teams": teams,
143-
"org": org,
152+
"team": team,
153+
"teams": teams,
154+
"org": org,
155+
"initialTeam": initialTeam,
144156
})
145-
proj, _, err := client.Projects.Create(ctx, org, team, params)
157+
proj, _, err := client.Projects.Create(ctx, org, initialTeam, params)
146158
if err != nil {
147159
return diag.FromErr(err)
148160
}
149161
tflog.Debug(ctx, "Created Sentry project", map[string]interface{}{
150162
"projectSlug": proj.Slug,
151163
"projectID": proj.ID,
152-
"team": team,
164+
"team": initialTeam,
153165
"org": org,
154166
})
155167

@@ -177,31 +189,11 @@ func resourceSentryProjectRead(ctx context.Context, d *schema.ResourceData, meta
177189
"org": org,
178190
})
179191

180-
setTeams := func() error {
181-
if len(proj.Teams) <= 1 {
182-
return multierror.Append(
183-
d.Set("team", proj.Team.Slug),
184-
d.Set("teams", nil),
185-
)
186-
}
187-
188-
teams := make([]string, len(proj.Teams))
189-
for i, team := range proj.Teams {
190-
teams[i] = *team.Slug
191-
}
192-
193-
return multierror.Append(
194-
d.Set("team", nil),
195-
d.Set("teams", teams),
196-
)
197-
}
198-
199192
d.SetId(proj.Slug)
200193
retErr := multierror.Append(
201194
d.Set("organization", proj.Organization.Slug),
202195
d.Set("name", proj.Name),
203196
d.Set("slug", proj.Slug),
204-
setTeams(),
205197
d.Set("platform", proj.Platform),
206198
d.Set("internal_id", proj.ID),
207199
d.Set("is_public", proj.IsPublic),
@@ -213,6 +205,15 @@ func resourceSentryProjectRead(ctx context.Context, d *schema.ResourceData, meta
213205
d.Set("resolve_age", proj.ResolveAge),
214206
d.Set("project_id", proj.ID), // Deprecated
215207
)
208+
if _, ok := d.GetOk("team"); ok {
209+
retErr = multierror.Append(retErr, d.Set("team", proj.Team.Slug))
210+
} else {
211+
teams := make([]string, 0, len(proj.Teams))
212+
for _, team := range proj.Teams {
213+
teams = append(teams, *team.Slug)
214+
}
215+
retErr = multierror.Append(retErr, d.Set("teams", flattenStringSet(teams)))
216+
}
216217

217218
// TODO: Project options
218219

@@ -285,35 +286,37 @@ func resourceSentryProjectUpdate(ctx context.Context, d *schema.ResourceData, me
285286

286287
// Ensure old teams and new teams do not overlap.
287288
for newTeam := range newTeams {
288-
if _, exists := oldTeams[newTeam]; exists {
289-
delete(oldTeams, newTeam)
290-
}
289+
delete(oldTeams, newTeam)
291290
}
292291

293-
tflog.Debug(ctx, "Adding teams to project", map[string]interface{}{
294-
"org": org,
295-
"project": project,
296-
"teamsToAdd": newTeams,
297-
})
292+
if len(newTeams) > 0 {
293+
tflog.Debug(ctx, "Adding teams to project", map[string]interface{}{
294+
"org": org,
295+
"project": project,
296+
"teamsToAdd": newTeams,
297+
})
298298

299-
for newTeam := range newTeams {
300-
_, _, err = client.Projects.AddTeam(ctx, org, project, newTeam)
301-
if err != nil {
302-
return diag.FromErr(err)
299+
for newTeam := range newTeams {
300+
_, _, err = client.Projects.AddTeam(ctx, org, project, newTeam)
301+
if err != nil {
302+
return diag.FromErr(err)
303+
}
303304
}
304305
}
305306

306-
tflog.Debug(ctx, "Removing teams from project", map[string]interface{}{
307-
"org": org,
308-
"project": project,
309-
"teamsToRemove": oldTeams,
310-
})
311-
312-
for oldTeam := range oldTeams {
313-
resp, err := client.Projects.RemoveTeam(ctx, org, project, oldTeam)
314-
if err != nil {
315-
if resp.Response.StatusCode != http.StatusNotFound {
316-
return diag.FromErr(err)
307+
if len(oldTeams) > 0 {
308+
tflog.Debug(ctx, "Removing teams from project", map[string]interface{}{
309+
"org": org,
310+
"project": project,
311+
"teamsToRemove": oldTeams,
312+
})
313+
314+
for oldTeam := range oldTeams {
315+
resp, err := client.Projects.RemoveTeam(ctx, org, project, oldTeam)
316+
if err != nil {
317+
if resp.Response.StatusCode != http.StatusNotFound {
318+
return diag.FromErr(err)
319+
}
317320
}
318321
}
319322
}

0 commit comments

Comments
 (0)