Escape special characters while sending email through AWS ses - amazon-web-services

Hello I am using AWS SES SDK V2 to send emails. I have a case where sender name is like Michael čisto. If I pass this name directly to AWS email input struct then it gives me following error:
operation error SESv2: SendEmail, https response error StatusCode: 400, BadRequestException: Missing '"'
Here is my code:
package main
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
awsconfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/sesv2"
"github.com/aws/aws-sdk-go-v2/service/sesv2/types"
)
func main() {
name := "Michael čisto"
body := "test email body"
subject := "test email"
CharSet := "UTF-8"
/* Assemble the email */
input := &sesv2.SendEmailInput{
Destination: &types.Destination{
CcAddresses: []string{},
ToAddresses: []string{receiverEmail},
},
Content: &types.EmailContent{
Simple: &types.Message{
Body: &types.Body{
Html: &types.Content{
Charset: aws.String(CharSet),
Data: aws.String(body),
},
},
Subject: &types.Content{
Charset: aws.String(CharSet),
Data: aws.String(subject),
},
},
},
ReplyToAddresses: []string{"\"" + name + "\" <" + senderEmail + ">"},
FromEmailAddress: aws.String("\"" + name + "\" <" + senderEmail + ">"),
}
cfg, err := awsconfig.LoadDefaultConfig(context.TODO(),
awsconfig.WithRegion("us-east-1"),
)
client := sesv2.NewFromConfig(cfg)
emailResp, err = client.SendEmail(context.TODO(), input)
}
Can anyone help me to figure out how to escape these kind of characters in GO ?

Try to format the address using mail.Address.String:
package main
import (
"fmt"
"net/mail"
)
func main() {
a := mail.Address{Name: "Michael čisto", Address: "sender#example.com"}
fmt.Println(a.String())
// Output:
// =?utf-8?q?Michael_=C4=8Disto?= <sender#example.com>
}
In case your domain contains non-ASCII characters:
Please note that Amazon SES does not support the SMTPUTF8 extension. If the domain part of an address (the part after the # sign) contains non-ASCII characters, they must be encoded using Punycode (see types.BulkEmailEntry). mail.Address.String does not do this for you. But you can use idna.ToASCII to convert the domain yourself.

Related

GO AWS SDKv2: Parsing json file and adding values as tags for AWS Secretsmanager

I have the requirement to write a cross-platform cli tool in go by leveraging the AWS SDKv2 GO.
I need to parse a json file like this:
{
"Tags": [
{
"Key": "global1",
"Value": "val1"
},
{
"Key": "global2",
"Value": "val2"
}
]
}
I have this function:
package lib
import (
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"encoding/json"
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
"io/ioutil"
"os"
)
//func CreateSecret(client *secretsmanager.Client, secretName string, description string, kmsKeyId string, secretString string) {
func CreateSecret(client *secretsmanager.Client) {
// Parse tags.json
jsonFile, err := os.Open("tags.json")
if err != nil {
fmt.Println(err)
}
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
tags := []*secretsmanager.Tag{}
json.Unmarshal(byteValue, &tags)
// rest of the code is truncated...
}
However, I receive this error:
lib/commands.go:58:28: undefined: secretsmanager.Tag
What am I doing wrong?
secretsmanager.Tag does not exist. But what I think you are looking for is this package: github.com/aws/aws-sdk-go-v2/service/secretsmanager/types. Import it and then you will have types.Tag, which is what you probably need.

AWS EventBridge PutEvent Detail Malformed

I have tried both AWS Go SDKs both versions and every time I get an error stating that the Details field is malformed. The Details field accepts a JSON string.
In SDK V2 you basically have a struct for the event
type Event struct {
Details []struct {
Key string `json:"Key"`
Value string `json:"Value"`
} `json:"Details"`
DetailType string `json:"DetailType"`
Source string `json:"Source"`
}
The example then builds up the JSON string with this code
myDetails := "{ "
for _, d := range event.Details {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\","
}
myDetails = myDetails + " }"
And then make the api call
input := &cloudwatchevents.PutEventsInput{
Entries: []types.PutEventsRequestEntry{
{
Detail: &myDetails,
DetailType: &event.DetailType,
Resources: []string{
*lambdaARN,
},
Source: &event.Source,
},
},
}
Basically I get an error saying that the string assigned to the Detail field is malformed. I believe this is because the example code generates a string with a trailing which is not valid . However when you omit the , you get a nil memory reference.
AWS SDK Example
The example for SDK version 1 also generates an error.
Any help would be great
The code on the page you linked is not 100% correct. They actually link the "complete example" at the bottom of the page, which has slightly different code:
https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/gov2/cloudwatch/PutEvent/PutEventv2.go#L108-L117
myDetails := "{ "
for i, d := range event.Details {
if i == (len(event.Details) - 1) {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\""
} else {
myDetails = myDetails + "\"" + d.Key + "\": \"" + d.Value + "\","
}
}
myDetails = myDetails + " }"
But building the JSON like this is not ideal and error-prone, as you already found out.
I'd propose the following:
details := make(map[string]string, len(event.Details))
for _, d := range event.Details {
details[d.Key] = d.Value
}
b, err := json.Marshal(details)
if err != nil {
return
}
fmt.Println(string(b))
Checkout the playground to see it in action:
https://play.golang.com/p/E4ueZLGIKp4

Python script for creating Space on Cloud Confluence

I have over 700 Projects for which I have to create Spaces on Confluence Cloud. Doing this manually is not feasible. Have to come up with some script or tool that reads the information from Text File and creates the spaces. I have programming background - Python and Java. Can someone suggest a solution for achieving this please?
Here is the code snippet for reading the space and page names from excel and creating them in Confluence cloud -
import requests
import json
from requests.auth import HTTPBasicAuth
from openpyxl import load_workbook
from collections import defaultdict
filename = "path/PjtExport.xlsx"
wb = load_workbook(filename)
sheet = wb["Sheet1"]
confdict = defaultdict(list)
for i1 in range(2,10) :
if(str(sheet.cell(row=i1, column=1).value) != "None") :
spaceName = str(sheet.cell(row=i1, column=1).value)
keyName = spaceName.replace(" ","")
#print(keyName)
if not(confdict.keys().__contains__(spaceName)):
confdict.setdefault(keyName,spaceName)
#print(spaceName)
url = 'https://domain.atlassian.net/wiki/rest/api/space'
auth = HTTPBasicAuth('xxx#yyy.com', 'abcabcabc')
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
payload = json.dumps({
"key": keyName,
"name": spaceName,
"description": {
"plain": {
"value": "Test space created in Python - XL Int",
"representation": "storage"
}
},
})
response = requests.request(
"POST",
url,
data=payload,
headers=headers,
auth=auth
)
res_data = response.json()
spaceID = str((res_data["homepage"]["id"]))
pageName = str(sheet.cell(row=i1, column=2).value)
url = "https://domain.atlassian.net/wiki/rest/api/content/aaaa/pagehierarchy/copy"
auth = HTTPBasicAuth('xxx#yyy.com', 'abcabcabc')
headers = {
"Content-Type": "application/json"
}
payload = json.dumps({
"copyAttachments": True,
"copyPermissions": True,
"copyProperties": True,
"copyLabels": True,
"copyCustomContents": True,
"destinationPageId": spaceID,
"titleOptions": {
"prefix": pageName + " ",
"replace": "",
"search": ""
}
})
response = requests.request(
"POST",
url,
data=payload,
headers=headers,
auth=auth
)
page_data = response.json()
#spaceID = str((res_data["homepage"]["id"]))
print(page_data)

route53 list hosted zones output throws "does not support indexing" error

I can't parse the output of the response below.
When I include the line:
"fmt.Println(*r["HostedZones"][0])"
it throws:
"type *route53.ListHostedZonesOutput does not support indexing".
I'd like to retrieve the "Id" and "Name" of each zone in the output. If the type doesn't support indexing, how can I retrieve the parts of the output I need?
Thank you.
package main
import (
"log"
"fmt"
"reflect"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)
func main() {
r53 := route53.New(session.New())
r, err := r53.ListHostedZones(&route53.ListHostedZonesInput{})
if err != nil {
log.Fatal(err)
}
log.Println(r)
fmt.Println(reflect.TypeOf(r))
fmt.Println(*r["HostedZones"][0])
}
sample output:
{
HostedZones: [{
CallerReference: "5E95CADD-59E-A6",
Config: {
PrivateZone: false
},
Id: "/hostedzone/Z1Q1TZTO",
Name: "testzone.local.",
ResourceRecordSetCount: 4
},{
CallerReference: "39895A3C-9B8B-95C2A3",
Config: {
PrivateZone: false
},
Id: "/hostedzone/Z2MXJQ7",
Name: "2.168.192.in-addr.arpa.",
ResourceRecordSetCount: 3
}],
IsTruncated: false,
MaxItems: "100"
}
This is an example of how to get the Id:
fmt.Println(*r.HostedZones[0].Id)
Name:
fmt.Println(*r.HostedZones[0].Name)
you are probably trying to index with the pointer to your struct, your r is a pointer, you can get the deferenced value by doing (*r)["yourkeyhere"]["index"].

How to concatenate Service metadata for consul-template with commas

Does anyone know how to concatenate strings from consul for consul-template?
If I have a service 'foo' registered in Consul
{
"Node": "node1",
"Address": "192.168.0.1",
"Port": 3333
},
{
"Node": "node2",
"Address": "192.168.0.2",
"Port": 4444
}
I would like consul-template to generate the following line:
servers=192.168.0.1:3333,192.168.0.2:4444/bogus
The following attempt does not work since it leaves a trailing comma ,
servers={{range service "foo"}}{{.Address}}{{.Port}},{{end}}/bogus
# renders
servers=192.168.0.1:3333,192.168.0.2:4444,/bogus
# What I actually want
servers=192.168.0.1:3333,192.168.0.2:4444/bogus
I know consul-template uses golang template syntax, but I simply cannot figure out the syntax to get this working. Its likely that I should use consul-template's join but how do I pass both .Address and .Port to join? This is just a trivial example, and I'm not using indexes intentionally since the number of services could be more than two. Any ideas?
This should work.
{{$foo_srv := service "foo"}}
{{if $foo_srv}}
{{$last := len $foo_srv | subtract 1}}
servers=
{{- range $i := loop $last}}
{{- with index $foo_srv $i}}{{.Address}}{{.Port}},{{end}}
{{- end}}
{{- with index $foo_srv last}}{{.Address}}{{.Port}}{{end}}/bogus
{{end}}
I was thinking if "join" can be used.
Note "{{-" means removing leading white spaces (such ' ', \t, \n).
You can use a custom plugin.
servers={{service "foo" | toJSON | plugin "path/to/plugin"}}
The plugin code:
package main
import (
"encoding/json"
"fmt"
"os"
)
type InputEntry struct {
Node string
Address string
Port int
}
func main() {
arg := []byte(os.Args[1])
var input []InputEntry
if err := json.Unmarshal(arg, &input); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("err: %s", err))
os.Exit(1)
}
var output string
for i, entry := range input {
output += fmt.Sprintf("%v:%v", entry.Address, entry.Port)
if i != len(input)-1 {
output += ","
}
}
fmt.Fprintln(os.Stdout, string(output))
os.Exit(0)
}