Skip to content

Commit c17ae3b

Browse files
jramosfJavier Ramos
authored andcommitted
Add table_aws_ses_suppressed_destination
1 parent 62e0802 commit c17ae3b

File tree

6 files changed

+441
-6
lines changed

6 files changed

+441
-6
lines changed

aws/plugin.go

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

0 commit comments

Comments
 (0)