AWS SES SendBulkTemplatedEmailResponse for tracking email statues - amazon-web-services

I am referring to .net SDK here but I believe class level concepts are all same.
This is for sending bulk emails using templates (https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-personalized-email-api.html)
https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SimpleEmail/TSendBulkTemplatedEmailResponse.html
SendBulkTemplatedEmailResponse response = client.SendBulkTemplatedEmailAsync(sendBulkTemplatedEmailRequest).Result
SendBulkTemplatedEmailRequest has more than one email addresses and SendBulkTemplatedEmailResponse is returned with individual status for each email as List<BulkEmailDestinationStatus> (https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/SimpleEmail/TBulkEmailDestinationStatus.html).
Each BulkEmailDestinationStatus has MessageId and Status (some predefined constants). But not having the email-address for which the status is returned (obviously there are more than one recipients so there are individual status for each recipient.)
With that said, how to figure out mapping from email-address to MessageId or vice-versa?
I am getting confused about what is the use of messageId in BulkEmailDestinationStatus where there is not any associated recipient email-address. Am I missing something very basic here?

While I didn't find any resources on this, I'll just leave what I gathered testing this feature, for anyone else that may find this question.
The order of the emails sent (ie. the order of the emails in the Destinations property) is the order of the returned messageIds.
So, using the docs json sample:
{
"Source":"Mary Major <mary.major#example.com>",
"Template":"MyTemplate",
"ConfigurationSetName": "ConfigSet",
"Destinations":[
{
"Destination":{
"ToAddresses":[
"anaya.iyengar#example.com"
]
},
"ReplacementTemplateData":"{ \"name\":\"Anaya\", \"favoriteanimal\":\"angelfish\" }"
},
{
"Destination":{
"ToAddresses":[
"liu.jie#example.com"
]
},
"ReplacementTemplateData":"{ \"name\":\"Liu\", \"favoriteanimal\":\"lion\" }"
},
{
"Destination":{
"ToAddresses":[
"shirley.rodriguez#example.com"
]
},
"ReplacementTemplateData":"{ \"name\":\"Shirley\", \"favoriteanimal\":\"shark\" }"
},
{
"Destination":{
"ToAddresses":[
"richard.roe#example.com"
]
},
"ReplacementTemplateData":"{}"
}
],
"DefaultTemplateData":"{ \"name\":\"friend\", \"favoriteanimal\":\"unknown\" }"
}
The object sent would be a
(SendBulkTemplatedEmailRequest) request with the following list:
request.Destinations[0].ToAddresses = {"anaya.iyengar#example.com"}
request.Destinations[1].ToAddresses = {"liu.jie#example.com"}
request.Destinations[2].ToAddresses = {"shirley.rodriguez#example.com"}
request.Destinations[3].ToAddresses = {"richard.roe#example.com"}
And the (SendBulkTemplatedEmailResponse) response would have this list:
response.Status[0].MessageId = "0000000000000000-11111111-2222-3333-4444-111111111111-000000"
response.Status[1].MessageId = "0000000000000000-11111111-2222-3333-4444-222222222222-000000"
response.Status[2].MessageId = "0000000000000000-11111111-2222-3333-4444-333333333333-000000"
response.Status[3].MessageId = "0000000000000000-11111111-2222-3333-4444-444444444444-000000"
Where:
the MessageId "0000000000000000-11111111-2222-3333-4444-111111111111-000000" refers to the email sent to anaya.iyengar#example.com;
the MessageId
"0000000000000000-11111111-2222-3333-4444-222222222222-000000"
refers to to the email sent to "liu.jie#example.com";
and so on.

Related

AWS Textract InvalidParameterException

I have a .Net core client application using amazon Textract with S3,SNS and SQS as per the AWS Document , Detecting and Analyzing Text in Multipage Documents(https://docs.aws.amazon.com/textract/latest/dg/async.html)
Created an AWS Role with AmazonTextractServiceRole Policy and added the Following Trust relation ship as per the documentation (https://docs.aws.amazon.com/textract/latest/dg/api-async-roles.html)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "textract.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
Subscribed SQS to the topic and Given Permission to the Amazon SNS Topic to Send Messages to the Amazon SQS Queue as per the aws documentation .
All Resources including S3 Bucket, SNS ,SQS are in the same us-west2 region
The following method shows a generic error "InvalidParameterException"
Request has invalid parameters
But If the NotificationChannel section is commented the code is working fine and returning the correct job id.
Error message is not giving a clear picture about the parameter. Highly appreciated any help .
public async Task<string> ScanDocument()
{
string roleArn = "aws:iam::xxxxxxxxxxxx:instance-profile/MyTextractRole";
string topicArn = "aws:sns:us-west-2:xxxxxxxxxxxx:AmazonTextract-My-Topic";
string bucketName = "mybucket";
string filename = "mytestdoc.pdf";
var request = new StartDocumentAnalysisRequest();
var notificationChannel = new NotificationChannel();
notificationChannel.RoleArn = roleArn;
notificationChannel.SNSTopicArn = topicArn;
var s3Object = new S3Object
{
Bucket = bucketName,
Name = filename
};
request.DocumentLocation = new DocumentLocation
{
S3Object = s3Object
};
request.FeatureTypes = new List<string>() { "TABLES", "FORMS" };
request.NotificationChannel = channel; /* Commenting this line work the code*/
var response = await this._textractService.StartDocumentAnalysisAsync(request);
return response.JobId;
}
Debugging Invalid AWS Requests
The AWS SDK validates your request object locally, before dispatching it to the AWS servers. This validation will fail with unhelpfully opaque errors, like the OP.
As the SDK is open source, you can inspect the source to help narrow down the invalid parameter.
Before we look at the code: The SDK (and documentation) are actually generated from special JSON files that describe the API, its requirements and how to validate them. The actual code is generated based on these JSON files.
I'm going to use the Node.js SDK as an example, but I'm sure similar approaches may work for the other SDKs, including .NET
In our case (AWS Textract), the latest Api version is 2018-06-27. Sure enough, the JSON source file is on GitHub, here.
In my case, experimentation narrowed the issue down to the ClientRequestToken. The error was an opaque InvalidParameterException. I searched for it in the SDK source JSON file, and sure enough, on line 392:
"ClientRequestToken": {
"type": "string",
"max": 64,
"min": 1,
"pattern": "^[a-zA-Z0-9-_]+$"
},
A whole bunch of undocumented requirements!
In my case the token I was using violated the regex (pattern in the above source code). Changing my token code to satisfy the regex solved the problem.
I recommend this approach for these sorts of opaque type errors.
After a long days analyzing the issue. I was able to resolve it .. as per the documentation topic only required SendMessage Action to the SQS . But after changing it to All SQS Action its Started Working . But Still AWS Error message is really misleading and confusing
you would need to change the permissions to All SQS Action and then use the code as below
def startJob(s3BucketName, objectName):
response = None
response = textract.start_document_text_detection(
DocumentLocation={
'S3Object': {
'Bucket': s3BucketName,
'Name': objectName
}
})
return response["JobId"]
def isJobComplete(jobId):
# For production use cases, use SNS based notification
# Details at: https://docs.aws.amazon.com/textract/latest/dg/api-async.html
time.sleep(5)
response = textract.get_document_text_detection(JobId=jobId)
status = response["JobStatus"]
print("Job status: {}".format(status))
while(status == "IN_PROGRESS"):
time.sleep(5)
response = textract.get_document_text_detection(JobId=jobId)
status = response["JobStatus"]
print("Job status: {}".format(status))
return status
def getJobResults(jobId):
pages = []
response = textract.get_document_text_detection(JobId=jobId)
pages.append(response)
print("Resultset page recieved: {}".format(len(pages)))
nextToken = None
if('NextToken' in response):
nextToken = response['NextToken']
while(nextToken):
response = textract.get_document_text_detection(JobId=jobId, NextToken=nextToken)
pages.append(response)
print("Resultset page recieved: {}".format(len(pages)))
nextToken = None
if('NextToken' in response):
nextToken = response['NextToken']
return pages
Invoking textract with Python, I received the same error until I truncated the ClientRequestToken down to 64 characters
response = client.start_document_text_detection(
DocumentLocation={
'S3Object':{
'Bucket': bucket,
'Name' : fileName
}
},
ClientRequestToken= fileName[:64],
NotificationChannel= {
"SNSTopicArn": "arn:aws:sns:us-east-1:AccountID:AmazonTextractXYZ",
"RoleArn": "arn:aws:iam::AccountId:role/TextractRole"
}
)
print('Processing started : %s' % json.dumps(response))

How to send email to multiple recipients through AWS SES

Hello guys i'm trying to send email to multiple user through AWS SES using Python but whenever i'm trying to send a mail i got a error : Illegal address
This is my code:
def emailServiceForCustomerInformation(self, emailSubject, customerLicenseMessage, installation_name):
# logger = ToolsLogger.getOrCreateLogger(current_user.keyspace)
logger = ToolsLogger.getOrCreateRootLogger()
logger.info("Email service For Customer is started")
record = int(recordCount)
# print("emailRcord-",record)
# This address must be verified with Amazon SES.
SENDER = "Snehil singh<snehil#codedata.io>"
# is still in the sandbox, this address must be verified.
recipients = ["cosmoandysysmo#gmail.com","snehil#codedata.io"]
RECIPIENT = ", ".join(recipients)
# If necessary, replace us-east-1 with the AWS Region currently using for Amazon SES.
AWS_REGION = "us-east-1"
# The subject line for the email.
SUBJECT = emailSubject
BODY_TEXT = (customerLicenseMessage + ' ''For InstallationName-'+ installation_name)
# The character encoding for the email.
CHARSET = "UTF-8"
client = boto3.client('ses', region_name=AWS_REGION,
aws_access_key_id=config[os.environ['CONFIG_TYPE']].S3_ACCESS_KEY,
aws_secret_access_key=config[os.environ['CONFIG_TYPE']].S3_ACCESS_SECRET_KEY,
config=Config(signature_version='s3v4'))
is_success = True
# Try to send the email.
try:
# Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
# If you are not using a configuration set, comment or delete the
# following line
# ConfigurationSetName=CONFIGURATION_SET,
)
# Display an error if something goes wrong.
except ClientError as e:
logger.exception(e)
print(e.response['Error']['Message'])
is_success = False
else:
# print("Email sent! Message ID:"),
# print(response['MessageId'])
logger.info("Email service is Completed and send to the mail")
return is_success
i have searched on internet but non of the answer helped This is another way i have tried https://www.jeffgeerling.com/blogs/jeff-geerling/sending-emails-multiple but this also not helpful please help me where i'm doing wrong where do i modify it please ping me if you have any questions related this...thanks in advance.
Looks to me like you should be passing in the 'recipient', not the RECIPENT string. Try something like this:
Destination={'ToAddresses':recipients}
It appears to be expecting an array, not a comma seperated list of strings.
In boto3 SES send_email documentation:
response = client.send_email(
Source='string',
Destination={
'ToAddresses': [
'string',
],
'CcAddresses': [
'string',
],
'BccAddresses': [
'string',
]
},
And if you read the SES SendEmail API call documentation, it tells you that the Destination object is:
BccAddresses.member.N
The BCC: field(s) of the message.
Type: Array of strings
Required: No
CcAddresses.member.N
The CC: field(s) of the message.
Type: Array of strings
Required: No
ToAddresses.member.N
The To: field(s) of the message.
Type: Array of strings
Required: No
In summary: don't join the address to construct RECIPIENT. RECIPIENT needs to be an array (a list, in Python) of strings, where each strings is one email address.
RECIPIENT must be array of strings > ['email1', 'email2']
and >>
Destination={
'ToAddresses': [
RECIPIENT,
],
},
to
Destination={
'ToAddresses': RECIPIENT
},
Credit to this answer
All you need to do is to put all the recipients as a list
email = ['xxx#gmail.com', 'yyy#yahoo.com', 'zzz#hotmail.com]
Then, you can modify the boto3 variables as below
Destination={'ToAddresses': email, .....}

Regex filtering of messages in SNS

Is there a way to filter messages based on Regex or substring in AWS SNS?
AWS Documentation for filtering messages mentions three types of filtering for strings:
Exact matching (whitelisting)
Anything-but matching (blacklisting)
Prefix matching
I want to filter out messages based on substrings in the messages, for example
I have a S3 event that sends a message to SNS when a new object is added to S3, the contents of the message are as below:
{
"Records": [
{
"s3": {
"bucket": {
"name": "images-bucket"
},
"object": {
"key": "some-key/more-key/filteringText/additionaldata.png"
}
}
}
]
}
I want to keep the messages if only filteringText is present in key field.
Note: The entire message is sent as text by S3 notification service, so Records is not a json object but string.
From what I've seen in the documentation, you can't do regex matches or substrings, but you can match prefixes and create your own attributes in the MessageAttributes field.
To do this, I send the S3 event to a simple Lambda that adds MessageAttributes and then sends to SNS.
In effect, S3 -> Lambda -> SNS -> other consumers (with filtering).
The Lambda can do something like this (where you'll have to programmatically decide when to add the attribute):
let messageAttributes = {
myfilterkey: {DataType: "String", StringValue:"filteringText"}
};
let params = {
Message: JSON.stringify(payload),
MessageAttributes: messageAttributes,
MessageStructure: 'json',
TargetArn: SNS_ARN
};
await sns.publish(params).promise();
Then in SNS you can filter:
{"myfilterkey": ["filtertext"]}
It seems a little convoluted to put the Lambda in there, but I like the idea of being able to plug and unplug consumers from SNS on the fly and use filtering to determine who gets what.

how to pass an object to amazon SNS

I see in the examples how to to pass a message string to amazon sns sdk's publish method. However, is there an exmaple of how to pass a custom object as the message? I tried setting "MessageStructure" to "json" but then I get InvalidParameter: Invalid parameter: Message Structure - No default entry in JSON message body error. Where should I be passing the object values into in the params?
Any examples?
var params = {
Message: JSON.stringify(item),
MessageStructure: 'json',
TopicArn: topic
//MessageAttributes: item
};
return sns.publishAsync(params);
There is no SDK-supported way to pass a custom object as a message-- messages are always strings. You can, of course, make the string a serialized version of your object.
MessageStructure: 'json' is for a different purpose-- when you want to pass different strings to different subscription types. In that case, you make the message a serialized json object with AWS-defined structure, where each element defines the message to send to a particular type of subscription (email, sqs, etc). Even in that case, the messages themselves are just strings.
MessageAttributes are parameters you add to the message to support specific subscription types. If you are using SNS to talk to Apple's IOS notification service, for example, you might have to supply additional message parameters or authentication keys-- MessageAttributes provide a mechanism to do this. This is described in this AWS documentation.
An example is shown here: https://docs.aws.amazon.com/sns/latest/api/API_Publish.html#API_Publish_Example_2
The JSON format for Message is as follows:
{
"default": "A message.",
"email": "A message for email.",
"email-json": "A message for email (JSON).",
"http": "A message for HTTP.",
"https": "A message for HTTPS.",
"sqs": "A message for Amazon SQS."
}
So, assuming what you wanted to pass is an object, the way it worked for me was:
const messageObjToSend = {
...
}
const params = {
Message: JSON.stringify({
default: JSON.stringify( messageObjToSend )
}),
MessageStructure: 'json',
TopicArn: 'arn:aws:sns...'
}
Jackson 2 has pretty good support to convert object to JSON String and vice versa.
To String
Cat c = new Cat();
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(c);
To Object
Cat obj = mapper.readValue(s,Cat.class);
The message needs to be a JSON object and the default property needs to be added and should contain the JSON you want included in the email.
var defaultMessage = { "default": item };
var params = {
Message: defaultMessage, /*JSON.stringify(item),*/
---------^
MessageStructure: 'json',
TopicArn: topic
//MessageAttributes: item
};
return sns.publishAsync(params);
Using python,
boto3.client("sns").publish(
TopicArn=sns_subscription_arn,
Subject="subject",
Message=json.dumps({"default": item}),
--------^
MessageStructure="json",
)
FYI, if you go to this SNS topic in the AWS Console you can "publish message" and choose "Custom payload for each delivery protocol.". Here you will see a template of the email and the "default" property is tagged for "Sample fallback message".

Getting "InvalidParameterValue - Missing final '#domain'" from Amazon SES while sending an email with unicode characters in destination address

Amazon SES returns the error mentioned above when i try to send an email that contains unicode characters in the To: field. Amazon SES Documentation says that such email addresses should be sent in MIME encoded-word syntax, which the mail gem (used by ActionMailer) is doing correctly, it is sent as: =?UTF-8?B?dmluYXl2aW5heeKAmXNAbWFpbGluYXRvci5jb20=?=
I was seeing this same error, and found it was due to an incorrect ReturnPath parameter. The error indicates your ReturnPath parameter does not have a domain name. The ReturnPath parameter should be an email address, and it's the address to which bounce notifications are forwarded.
The question is pretty old, but I'll add my case for it seems to be the first result searching for problems with "Missing finale #domain" on AWS SES.
(the only other SO question I found is AWS SES Missing final '#domain' PHP SDK
)
As in the other question's answer, InvalidParameterValue is returned every time a parameter don't pass validation.
In my case I was using boto3 on python, composing the Destination parameter with some keys that could be empty, like so:
to = []
bcc = []
# Some code to populate one or both lists..
response = client.send_email(
Destination={
'ToAddresses': to,
'BccAddresses': bcc
},
Message={
'Body': {
'Html': {
'Charset': MAIL_CHARSET,
'Data': message,
},
'Text': {
'Charset': MAIL_CHARSET,
'Data': message,
},
},
'Subject': {
'Charset': MAIL_CHARSET,
'Data': subject,
},
},
Source=MAIL_SENDER,
)
If one of the two keys in the dict assigned to the Destination parameter was an empty list the InvalidParameterValue was returned.
Solution is to simply remove empty, useless, key:
to = []
bcc = []
# Some code to populate one or both lists..
destinations = {
'ToAddresses': to,
'BccAddresses': bcc
}
response = client.send_email(
Destination={typ: addresses
for typ, addresses in destinations.iteritems()
if addresses},
Message={
'Body': {
'Html': {
'Charset': MAIL_CHARSET,
'Data': message,
},
'Text': {
'Charset': MAIL_CHARSET,
'Data': message,
},
},
'Subject': {
'Charset': MAIL_CHARSET,
'Data': subject,
},
},
Source=MAIL_SENDER,
)
I just got this error because I was storing email addresses in a .cfg file (which uses the same structure as a Windows .ini file) and I put quotes around the email address like programmers are in the habit of doing with strings but that you shouldn't do in a cfg file:
[email_settings]
email_to="some_user#domain.com"
When I removed the quotes, it worked. I'm sure there are multiple issues that can cause this error.
I was getting this error when using the SES SendRawEmail API because my entire "To" header was being encoded in MIME encoded-words syntax as a unit.
It's not completely clear from the documentation, but it hints that the sender name should be encoded separately.
The sender name (also known as the friendly name) may contain non-ASCII characters. These characters must be encoded using MIME encoded-word syntax, as described in RFC 2047. MIME encoded-word syntax uses the following form: =?charset?encoding?encoded-text?=.
source: https://docs.aws.amazon.com/ses/latest/APIReference/API_SendRawEmail.html#:~:text=The%20sender%20name,encoded%2Dtext%3F%3D.
Python's email.message.Message class automatically encodes headers containing non-ascii characters as a single chunk:
from email.message import Message
message1 = Message()
message1["To"] = "RecipiƩnt <foo#bar.com>"
print(message1.as_string())
# To: =?utf-8?b?UmVjaXBpw6ludCA8Zm9vQGJhci5jb20+?=
I worked around this by formatting the address in advance using the formataddr utility function, which does the encoding for the sender name separately:
from email.utils import formataddr
message2 = Message()
message2["To"] = formataddr(("RecipiƩnt", "foo#bar.com"))
print(message2.as_string())
# To: =?utf-8?q?Recipi=C3=A9nt?= <foo#bar.com>