Skip to content

Commit d36e3b8

Browse files
jramosfJavier Ramos
authored andcommitted
Add table_aws_ses_suppressed_destination
1 parent 54bcfd7 commit d36e3b8

File tree

6 files changed

+419
-6
lines changed

6 files changed

+419
-6
lines changed

aws/plugin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
674674
"aws_ses_domain_identity": tableAwsSESDomainIdentity(ctx),
675675
"aws_ses_email_identity": tableAwsSESEmailIdentity(ctx),
676676
"aws_ses_template": tableAwsSESTemplate(ctx),
677+
"aws_sesv2_suppressed_destination": tableAwsSesV2SuppressedDestination(ctx),
677678
"aws_sfn_state_machine_execution_history": tableAwsStepFunctionsStateMachineExecutionHistory(ctx),
678679
"aws_sfn_state_machine_execution": tableAwsStepFunctionsStateMachineExecution(ctx),
679680
"aws_sfn_state_machine": tableAwsStepFunctionsStateMachine(ctx),

aws/service.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ import (
138138
"github.com/aws/aws-sdk-go-v2/service/servicediscovery"
139139
"github.com/aws/aws-sdk-go-v2/service/servicequotas"
140140
"github.com/aws/aws-sdk-go-v2/service/ses"
141+
"github.com/aws/aws-sdk-go-v2/service/sesv2"
141142
"github.com/aws/aws-sdk-go-v2/service/sfn"
142143
"github.com/aws/aws-sdk-go-v2/service/shield"
143144
"github.com/aws/aws-sdk-go-v2/service/simspaceweaver"
@@ -1562,6 +1563,17 @@ func SESClient(ctx context.Context, d *plugin.QueryData) (*ses.Client, error) {
15621563
return ses.NewFromConfig(*cfg), nil
15631564
}
15641565

1566+
func SESV2Client(ctx context.Context, d *plugin.QueryData) (*sesv2.Client, error) {
1567+
cfg, err := getClientForQuerySupportedRegion(ctx, d, AWS_EMAIL_SERVICE_ID)
1568+
if err != nil {
1569+
return nil, err
1570+
}
1571+
if cfg == nil {
1572+
return nil, nil
1573+
}
1574+
return sesv2.NewFromConfig(*cfg), nil
1575+
}
1576+
15651577
func ServerlessApplicationRepositoryClient(ctx context.Context, d *plugin.QueryData) (*serverlessapplicationrepository.Client, error) {
15661578
cfg, err := getClientForQuerySupportedRegion(ctx, d, AWS_SERVERLESSREPO_SERVICE_ID)
15671579
if err != nil {
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package aws
2+
3+
import (
4+
"context"
5+
"errors"
6+
"time"
7+
8+
"github.com/aws/aws-sdk-go-v2/service/sesv2"
9+
"github.com/aws/aws-sdk-go-v2/service/sesv2/types"
10+
11+
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
12+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
13+
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
14+
)
15+
16+
// QueryParams struct to hold the filter parameters used in the query
17+
type QueryParams struct {
18+
StartDate *time.Time
19+
EndDate *time.Time
20+
}
21+
22+
func tableAwsSesV2SuppressedDestination(_ context.Context) *plugin.Table {
23+
return &plugin.Table{
24+
Name: "aws_sesv2_suppressed_destination",
25+
Description: "Lists the email addresses that are on the suppression list for your account.",
26+
Get: &plugin.GetConfig{
27+
KeyColumns: plugin.SingleColumn("email_address"),
28+
Hydrate: getSuppressedDestination,
29+
Tags: map[string]string{"service": "sesv2", "action": "GetSuppressedDestination"},
30+
},
31+
List: &plugin.ListConfig{
32+
Hydrate: listSesSuppressedDestinations,
33+
Tags: map[string]string{"service": "sesv2", "action": "ListSuppressedDestinations"},
34+
KeyColumns: []*plugin.KeyColumn{
35+
{
36+
Name: "reason",
37+
Require: plugin.Optional,
38+
},
39+
{
40+
Name: "start_date",
41+
Require: plugin.Optional,
42+
Operators: []string{"="},
43+
},
44+
{
45+
Name: "end_date",
46+
Require: plugin.Optional,
47+
Operators: []string{"="},
48+
},
49+
},
50+
},
51+
HydrateConfig: []plugin.HydrateConfig{
52+
{
53+
Func: getSuppressedDestination,
54+
Tags: map[string]string{"service": "sesv2", "action": "GetSuppressedDestination"},
55+
},
56+
},
57+
GetMatrixItemFunc: SupportedRegionMatrix(AWS_EMAIL_SERVICE_ID),
58+
Columns: awsRegionalColumns([]*plugin.Column{
59+
{
60+
Name: "email_address",
61+
Description: "The email address that is on the suppression list for your account.",
62+
Type: proto.ColumnType_STRING,
63+
},
64+
{
65+
Name: "reason",
66+
Description: "The reason that the address was added to the suppression list for your account.",
67+
Type: proto.ColumnType_STRING,
68+
},
69+
{
70+
Name: "last_update_time",
71+
Description: "The date and time when the suppressed destination was last updated.",
72+
Type: proto.ColumnType_TIMESTAMP,
73+
},
74+
{
75+
Name: "start_date",
76+
Description: "Used to filter the list of suppressed email addresses so that it only includes addresses that were added to the list after the specified date.",
77+
Type: proto.ColumnType_TIMESTAMP,
78+
Transform: transform.FromQual("start_date"),
79+
},
80+
{
81+
Name: "end_date",
82+
Description: "Used to filter the list of suppressed email addresses so that it only includes addresses that were added to the list before the specified date.",
83+
Type: proto.ColumnType_TIMESTAMP,
84+
Transform: transform.FromQual("end_date"),
85+
},
86+
87+
// Hydrated columns from GetSuppressedDestination
88+
{
89+
Name: "suppressed_destination_attributes",
90+
Description: "An object that contains additional attributes that are related to a suppressed destination.",
91+
Type: proto.ColumnType_JSON,
92+
Hydrate: getSuppressedDestination,
93+
},
94+
{
95+
Name: "message_tag",
96+
Description: "A unique identifier that's generated when an email address is added to the suppression list for your account.",
97+
Type: proto.ColumnType_STRING,
98+
Hydrate: getSuppressedDestination,
99+
Transform: transform.FromField("Attributes.MessageTag"),
100+
},
101+
{
102+
Name: "feedback_id",
103+
Description: "The unique identifier of the email message that caused the email address to be added to the suppression list for your account.",
104+
Type: proto.ColumnType_STRING,
105+
Hydrate: getSuppressedDestination,
106+
Transform: transform.FromField("Attributes.FeedbackId"),
107+
},
108+
109+
// Steampipe standard columns
110+
{
111+
Name: "title",
112+
Description: resourceInterfaceDescription("title"),
113+
Type: proto.ColumnType_STRING,
114+
Transform: transform.FromField("EmailAddress"),
115+
},
116+
}),
117+
}
118+
}
119+
120+
//// LIST FUNCTION
121+
func listSesSuppressedDestinations(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
122+
svc, err := SESV2Client(ctx, d)
123+
if err != nil {
124+
plugin.Logger(ctx).Error("aws_sesv2_suppressed_destination.listSesSuppressedDestinations", "client_error", err)
125+
return nil, err
126+
}
127+
128+
maxItems := int32(1000)
129+
params := &sesv2.ListSuppressedDestinationsInput{}
130+
131+
// Handle optional qualifiers
132+
if d.EqualsQuals["reason"] != nil {
133+
reasonStr := d.EqualsQualString("reason")
134+
if reasonStr != "" {
135+
// Convert string to SuppressionListReason enum
136+
switch reasonStr {
137+
case "BOUNCE":
138+
params.Reasons = []types.SuppressionListReason{types.SuppressionListReasonBounce}
139+
case "COMPLAINT":
140+
params.Reasons = []types.SuppressionListReason{types.SuppressionListReasonComplaint}
141+
default:
142+
plugin.Logger(ctx).Warn("aws_sesv2_suppressed_destination.listSesSuppressedDestinations", "invalid_reason", reasonStr)
143+
}
144+
}
145+
}
146+
147+
if d.EqualsQuals["start_date"] != nil {
148+
if d.EqualsQuals["start_date"].GetTimestampValue() != nil {
149+
startDate := d.EqualsQuals["start_date"].GetTimestampValue().AsTime()
150+
params.StartDate = &startDate
151+
}
152+
}
153+
154+
if d.EqualsQuals["end_date"] != nil {
155+
if d.EqualsQuals["end_date"].GetTimestampValue() != nil {
156+
endDate := d.EqualsQuals["end_date"].GetTimestampValue().AsTime()
157+
params.EndDate = &endDate
158+
}
159+
}
160+
161+
if d.QueryContext.Limit != nil {
162+
limit := int32(*d.QueryContext.Limit)
163+
if limit < maxItems {
164+
params.PageSize = &limit
165+
}
166+
}
167+
168+
paginator := sesv2.NewListSuppressedDestinationsPaginator(svc, params, func(o *sesv2.ListSuppressedDestinationsPaginatorOptions) {
169+
o.Limit = maxItems
170+
o.StopOnDuplicateToken = true
171+
})
172+
173+
for paginator.HasMorePages() {
174+
d.WaitForListRateLimit(ctx)
175+
output, err := paginator.NextPage(ctx)
176+
if err != nil {
177+
plugin.Logger(ctx).Error("aws_sesv2_suppressed_destination.listSesSuppressedDestinations", "api_error", err)
178+
return nil, err
179+
}
180+
181+
for _, item := range output.SuppressedDestinationSummaries {
182+
d.StreamListItem(ctx, item)
183+
184+
// Context may get cancelled due to manual cancellation or if the limit has been reached
185+
if d.RowsRemaining(ctx) == 0 {
186+
return nil, nil
187+
}
188+
}
189+
}
190+
191+
return nil, nil
192+
}
193+
194+
//// HYDRATE FUNCTION
195+
func getSuppressedDestination(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
196+
svc, err := SESV2Client(ctx, d)
197+
if err != nil {
198+
plugin.Logger(ctx).Error("aws_sesv2_suppressed_destination.getSuppressedDestination", "client_error", err)
199+
return nil, err
200+
}
201+
202+
var emailAddress string
203+
if h.Item != nil {
204+
// Called from list - extract email address from the list item
205+
if item, ok := h.Item.(types.SuppressedDestinationSummary); ok {
206+
if item.EmailAddress != nil {
207+
emailAddress = *item.EmailAddress
208+
}
209+
}
210+
} else {
211+
// Called from get - extract email address from key columns
212+
emailAddress = d.EqualsQualString("email_address")
213+
}
214+
215+
if emailAddress == "" {
216+
plugin.Logger(ctx).Error("aws_sesv2_suppressed_destination.getSuppressedDestination", "missing_email_address")
217+
return nil, nil
218+
}
219+
220+
params := &sesv2.GetSuppressedDestinationInput{
221+
EmailAddress: &emailAddress,
222+
}
223+
224+
d.WaitForListRateLimit(ctx)
225+
output, err := svc.GetSuppressedDestination(ctx, params)
226+
if err != nil {
227+
var notFoundErr *types.NotFoundException
228+
if errors.As(err, &notFoundErr) {
229+
plugin.Logger(ctx).Debug("aws_sesv2_suppressed_destination.getSuppressedDestination", "email_not_found", emailAddress)
230+
}
231+
232+
plugin.Logger(ctx).Error("aws_sesv2_suppressed_destination.getSuppressedDestination", "api_error", err)
233+
return nil, err
234+
}
235+
236+
return output.SuppressedDestination, nil
237+
}

0 commit comments

Comments
 (0)