Skip to content

Commit 48cc423

Browse files
author
Marcin Belczewski
committed
feat: implement email contact
1 parent 9c4f788 commit 48cc423

13 files changed

+698
-184
lines changed

internal/service/notifications/notification_configuration.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ import (
1616
"github.com/hashicorp/terraform-plugin-framework/path"
1717
"github.com/hashicorp/terraform-plugin-framework/resource"
1818
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
19-
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
2019
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
21-
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
2220
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
2321
"github.com/hashicorp/terraform-plugin-framework/types"
2422
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
@@ -81,10 +79,8 @@ func (r *resourceNotificationConfiguration) Schema(ctx context.Context, req reso
8179
names.AttrStatus: schema.StringAttribute{
8280
CustomType: fwtypes.StringEnumType[awstypes.NotificationConfigurationStatus](),
8381
Computed: true,
84-
PlanModifiers: []planmodifier.String{
85-
stringplanmodifier.UseStateForUnknown(),
86-
},
87-
}, names.AttrTags: tftags.TagsAttribute(),
82+
},
83+
names.AttrTags: tftags.TagsAttribute(),
8884
names.AttrTagsAll: tftags.TagsAttributeComputedOnly(),
8985
},
9086
}

internal/service/notifications/notification_configuration_test.go

-7
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ import (
2727

2828
func TestAccNotificationsNotificationConfiguration_basic(t *testing.T) {
2929
ctx := acctest.Context(t)
30-
if testing.Short() {
31-
t.Skip("skipping long-running test in short mode")
32-
}
3330

3431
var notificationconfiguration notifications.GetNotificationConfigurationOutput
3532
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
@@ -72,9 +69,6 @@ func TestAccNotificationsNotificationConfiguration_basic(t *testing.T) {
7269

7370
func TestAccNotificationsNotificationConfiguration_disappears(t *testing.T) {
7471
ctx := acctest.Context(t)
75-
if testing.Short() {
76-
t.Skip("skipping long-running test in short mode")
77-
}
7872

7973
var notificationconfiguration notifications.GetNotificationConfigurationOutput
8074
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
@@ -263,7 +257,6 @@ resource "aws_notifications_notification_configuration" "test" {
263257
name = %[1]q
264258
description = %[2]q
265259
aggregation_duration = %[3]q
266-
267260
tags = {
268261
%[4]q = %[5]q
269262
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package notificationscontacts
5+
6+
import (
7+
"context"
8+
"errors"
9+
10+
"github.com/YakDriver/regexache"
11+
"github.com/aws/aws-sdk-go-v2/aws"
12+
"github.com/aws/aws-sdk-go-v2/service/notificationscontacts"
13+
awstypes "github.com/aws/aws-sdk-go-v2/service/notificationscontacts/types"
14+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
15+
"github.com/hashicorp/terraform-plugin-framework/path"
16+
"github.com/hashicorp/terraform-plugin-framework/resource"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
18+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
19+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
20+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
21+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
22+
"github.com/hashicorp/terraform-plugin-framework/types"
23+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
24+
"github.com/hashicorp/terraform-provider-aws/internal/conns"
25+
"github.com/hashicorp/terraform-provider-aws/internal/create"
26+
"github.com/hashicorp/terraform-provider-aws/internal/errs"
27+
"github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag"
28+
"github.com/hashicorp/terraform-provider-aws/internal/framework"
29+
"github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
30+
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
31+
"github.com/hashicorp/terraform-provider-aws/internal/sweep"
32+
sweepfw "github.com/hashicorp/terraform-provider-aws/internal/sweep/framework"
33+
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
34+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
35+
"github.com/hashicorp/terraform-provider-aws/names"
36+
)
37+
38+
// Function annotations are used for resource registration to the Provider. DO NOT EDIT.
39+
// @FrameworkResource("aws_notificationscontacts_email_contact", name="Email Contact")
40+
// @Tags(identifierAttribute="arn")
41+
func newResourceEmailContact(_ context.Context) (resource.ResourceWithConfigure, error) {
42+
r := &resourceEmailContact{}
43+
44+
return r, nil
45+
}
46+
47+
const (
48+
ResNameEmailContact = "Email Contact"
49+
)
50+
51+
type resourceEmailContact struct {
52+
framework.ResourceWithConfigure
53+
framework.WithNoOpUpdate[resourceEmailContactModel]
54+
}
55+
56+
func (r *resourceEmailContact) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
57+
resp.Schema = schema.Schema{
58+
Attributes: map[string]schema.Attribute{
59+
names.AttrARN: framework.ARNAttributeComputedOnly(),
60+
"email_address": schema.StringAttribute{
61+
Required: true,
62+
Validators: []validator.String{
63+
stringvalidator.LengthBetween(6, 254),
64+
stringvalidator.RegexMatches(regexache.MustCompile(`(.+)@(.+)`), ""),
65+
},
66+
PlanModifiers: []planmodifier.String{
67+
stringplanmodifier.RequiresReplace(),
68+
},
69+
},
70+
names.AttrName: schema.StringAttribute{
71+
Required: true,
72+
Validators: []validator.String{
73+
stringvalidator.LengthBetween(1, 64),
74+
stringvalidator.RegexMatches(regexache.MustCompile(`[\w.~-]+`), ""),
75+
},
76+
PlanModifiers: []planmodifier.String{
77+
stringplanmodifier.RequiresReplace(),
78+
},
79+
},
80+
names.AttrStatus: schema.StringAttribute{
81+
CustomType: fwtypes.StringEnumType[awstypes.EmailContactStatus](),
82+
Computed: true,
83+
Default: stringdefault.StaticString(string(awstypes.EmailContactStatusInactive)),
84+
},
85+
names.AttrTags: tftags.TagsAttribute(),
86+
names.AttrTagsAll: tftags.TagsAttributeComputedOnly(),
87+
},
88+
}
89+
}
90+
91+
func (r *resourceEmailContact) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
92+
conn := r.Meta().NotificationsContactsClient(ctx)
93+
94+
var plan resourceEmailContactModel
95+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
96+
if resp.Diagnostics.HasError() {
97+
return
98+
}
99+
100+
var input notificationscontacts.CreateEmailContactInput
101+
resp.Diagnostics.Append(flex.Expand(ctx, plan, &input)...)
102+
if resp.Diagnostics.HasError() {
103+
return
104+
}
105+
input.Tags = getTagsIn(ctx)
106+
107+
out, err := conn.CreateEmailContact(ctx, &input)
108+
if err != nil {
109+
resp.Diagnostics.AddError(
110+
create.ProblemStandardMessage(names.NotificationsContacts, create.ErrActionCreating, ResNameEmailContact, plan.EmailAddress.String(), err),
111+
err.Error(),
112+
)
113+
return
114+
}
115+
if out == nil || out.Arn == nil {
116+
resp.Diagnostics.AddError(
117+
create.ProblemStandardMessage(names.NotificationsContacts, create.ErrActionCreating, ResNameEmailContact, plan.EmailAddress.String(), nil),
118+
errors.New("empty output").Error(),
119+
)
120+
return
121+
}
122+
resp.Diagnostics.Append(flex.Flatten(ctx, out, &plan)...)
123+
if resp.Diagnostics.HasError() {
124+
return
125+
}
126+
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
127+
128+
var activateIn notificationscontacts.SendActivationCodeInput
129+
activateIn.Arn = out.Arn
130+
_, err = conn.SendActivationCode(ctx, &activateIn)
131+
if err != nil {
132+
resp.Diagnostics.AddError(
133+
create.ProblemStandardMessage(names.NotificationsContacts, "activating", ResNameEmailContact, plan.EmailAddress.String(), err),
134+
err.Error(),
135+
)
136+
return
137+
}
138+
}
139+
140+
func (r *resourceEmailContact) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
141+
conn := r.Meta().NotificationsContactsClient(ctx)
142+
143+
var state resourceEmailContactModel
144+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
145+
if resp.Diagnostics.HasError() {
146+
return
147+
}
148+
149+
out, err := findEmailContactByARN(ctx, conn, state.ARN.ValueString())
150+
if tfresource.NotFound(err) {
151+
resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err))
152+
resp.State.RemoveResource(ctx)
153+
return
154+
}
155+
if err != nil {
156+
resp.Diagnostics.AddError(
157+
create.ProblemStandardMessage(names.NotificationsContacts, create.ErrActionReading, ResNameEmailContact, state.ARN.String(), err),
158+
err.Error(),
159+
)
160+
return
161+
}
162+
163+
resp.Diagnostics.Append(flex.Flatten(ctx, out, &state)...)
164+
state.EmailAddress = flex.StringToFramework(ctx, out.Address)
165+
if resp.Diagnostics.HasError() {
166+
return
167+
}
168+
169+
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
170+
}
171+
172+
func (r *resourceEmailContact) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
173+
conn := r.Meta().NotificationsContactsClient(ctx)
174+
175+
var state resourceEmailContactModel
176+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
177+
if resp.Diagnostics.HasError() {
178+
return
179+
}
180+
181+
input := notificationscontacts.DeleteEmailContactInput{
182+
Arn: state.ARN.ValueStringPointer(),
183+
}
184+
185+
_, err := conn.DeleteEmailContact(ctx, &input)
186+
if err != nil {
187+
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
188+
return
189+
}
190+
191+
resp.Diagnostics.AddError(
192+
create.ProblemStandardMessage(names.NotificationsContacts, create.ErrActionDeleting, ResNameEmailContact, state.ARN.String(), err),
193+
err.Error(),
194+
)
195+
return
196+
}
197+
}
198+
199+
func (r *resourceEmailContact) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
200+
resource.ImportStatePassthroughID(ctx, path.Root(names.AttrARN), req, resp)
201+
}
202+
203+
func findEmailContactByARN(ctx context.Context, conn *notificationscontacts.Client, arn string) (*awstypes.EmailContact, error) {
204+
input := notificationscontacts.GetEmailContactInput{
205+
Arn: aws.String(arn),
206+
}
207+
208+
out, err := conn.GetEmailContact(ctx, &input)
209+
if err != nil {
210+
if errs.IsA[*awstypes.ResourceNotFoundException](err) {
211+
return nil, &retry.NotFoundError{
212+
LastError: err,
213+
LastRequest: &input,
214+
}
215+
}
216+
217+
return nil, err
218+
}
219+
220+
if out == nil || out.EmailContact == nil {
221+
return nil, tfresource.NewEmptyResultError(&input)
222+
}
223+
224+
return out.EmailContact, nil
225+
}
226+
227+
type resourceEmailContactModel struct {
228+
ARN types.String `tfsdk:"arn"`
229+
EmailAddress types.String `tfsdk:"email_address"`
230+
Name types.String `tfsdk:"name"`
231+
Status fwtypes.StringEnum[awstypes.EmailContactStatus] `tfsdk:"status"`
232+
Tags tftags.Map `tfsdk:"tags"`
233+
TagsAll tftags.Map `tfsdk:"tags_all"`
234+
}
235+
236+
func sweepEmailContacts(ctx context.Context, client *conns.AWSClient) ([]sweep.Sweepable, error) {
237+
input := notificationscontacts.ListEmailContactsInput{}
238+
conn := client.NotificationsContactsClient(ctx)
239+
var sweepResources []sweep.Sweepable
240+
241+
pages := notificationscontacts.NewListEmailContactsPaginator(conn, &input)
242+
for pages.HasMorePages() {
243+
page, err := pages.NextPage(ctx)
244+
if err != nil {
245+
return nil, err
246+
}
247+
248+
for _, v := range page.EmailContacts {
249+
sweepResources = append(sweepResources, sweepfw.NewSweepResource(newResourceEmailContact, client,
250+
sweepfw.NewAttribute(names.AttrID, aws.ToString(v.Arn))),
251+
)
252+
}
253+
}
254+
255+
return sweepResources, nil
256+
}

0 commit comments

Comments
 (0)