Delete DynamoDB string attribute with golang expression package - go

I'm having trouble deleting an attribute of a dynamodb item using the UpdateItem command: https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/expression/#Builder.WithUpdate
expr := expression.Set(
expression.Name("my_attribute_a"),
expression.Value(""),
).Set(
expression.Name("my_attribute_b"),
expression.Value(""),
)
update, err := expression.NewBuilder().
WithUpdate(expr).
Build()
if err != nil {
return fmt.Errorf("failed to build update expression: %v", err)
}
input := &dynamodb.UpdateItemInput{
TableName: &ddb.emailAccountTableName,
ExpressionAttributeNames: update.Names(),
ExpressionAttributeValues: update.Values(),
Key: map[string]*dynamodb.AttributeValue{
"id": {
S: &id,
},
},
UpdateExpression: update.Update(),
ReturnConsumedCapacity: aws.String(dynamodb.ReturnConsumedCapacityTotal),
}
fmt.Println(input.String())
result, err := ddb.svc.UpdateItem(input)
The result of running the above is:
{
ExpressionAttributeNames: {
#1: "my_attribute_a",
#0: "my_attribute_b"
},
ExpressionAttributeValues: {
:0: {
NULL: true
},
:1: {
NULL: true
}
},
Key: {
id: {
S: "38zqtaNezbB8eZw4pbJKm7"
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: "my-table",
UpdateExpression: "SET #0 = :0, #1 = :1\n"
}
And the result is the attributes for the item get set to true. I want the attributes to be removed. I've tried using nil but that has the same result, and expression.Null just sets the string to the value NULL

Turns out I needed to use expression.Remove instead of expression.Set here's the relevant update:
expr := expression.Remove(
expression.Name("my_attribute_a"),
).Remove(
expression.Name("my_attribute_b"),
)
update, err := expression.NewBuilder().
WithUpdate(expr).
Build()
Which results in the following:
{
ExpressionAttributeNames: {
#0: "my_attribute_a",
#1: "my_attribute_b
},
Key: {
id: {
S: "qfsphqt37ELyn2BanwE2fd"
}
},
ReturnConsumedCapacity: "TOTAL",
TableName: "my-table",
UpdateExpression: "REMOVE #0, #1\n"
}

Related

Go AWS SES Unable to find credentials

I'm trying to send mail using aws sdk v2 for Go.
I'm getting below error. When using s3 client everything is working fine. I checked the permissions associated with the credentials and it has administrator access. Unable to understand what could be the problem.
operation error SES: SendEmail, failed to sign request: failed to retrieve credentials: request canceled, context canceled
config.go
AWSConfig, err = awsConfig.LoadDefaultConfig(context.TODO())
if err != nil {
log.Println("Error configuring aws: ", err)
}
mailer.go
type Mail struct {
To []string
From string
Subject string
Body string
}
func (m *Mail) Send(ctx context.Context) error {
sesClient := ses.NewFromConfig(config.AWSConfig)
result, err := sesClient.SendEmail(ctx, &ses.SendEmailInput{
Destination: &types.Destination{
ToAddresses: m.To,
},
Message: &types.Message{
Body: &types.Body{
Html: &types.Content{
Data: &m.Body,
Charset: &CharSet,
},
},
Subject: &types.Content{
Data: &m.Subject,
Charset: &CharSet,
},
},
Source: &m.From,
ReplyToAddresses: ReplyTo,
ReturnPath: &BounceEmail,
})
if err != nil {
log.Println(fmt.Errorf("[MailSenderUtil]: error sending mail: %w", err))
return err
}
log.Println("[MailSenderUtilResult]: ", InJson(result))
return nil
}
I think you need to add your aws credentials to config.AWSConfig. This is my config
sess, err := session.NewSession(&aws.Config{
Credentials: credentials.NewStaticCredentials(constants.AWSAccessID, constants.AWSAccessKey , ""),
Region:aws.String("eu-west-2")},
)
svc := ses.New(sess)
input := &ses.SendEmailInput{
Destination: &ses.Destination{
ToAddresses: []*string{
aws.String(Recipient),
},
},
Message: &ses.Message{
Body: &ses.Body{
Html: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(HtmlBody),
},
Text: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(TextBody),
},
},
Subject: &ses.Content{
Charset: aws.String(CharSet),
Data: aws.String(Subject),
},
},
Source: aws.String(Sender),
// Uncomment to use a configuration set
//ConfigurationSetName: aws.String(ConfigurationSet),
}
// Attempt to send the email.
_, err = svc.SendEmail(input)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case ses.ErrCodeMessageRejected:
logger.Logger.Error(ses.ErrCodeMessageRejected, aerr.OrigErr())
case ses.ErrCodeMailFromDomainNotVerifiedException:
logger.Logger.Error(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.OrigErr())
case ses.ErrCodeConfigurationSetDoesNotExistException:
logger.Logger.Error(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.OrigErr())
default:
logger.Logger.Error("amazon default error", aerr.OrigErr())
}
} else {
logger.Logger.Error("amazon default error (else)", aerr.OrigErr())
}
return
}

Exporting two dbus objects not possible

Using godbus library and according to their server example, I'm trying to export (serve) two different objects with different interfaces. One object path is /a/b/c and another is /a/b/c/d. If I export one of them everything works fine. Even if have no overlap everything works fine(/a/b/c & /w/x/y/z). But exporting /a/b/c & /a/b/c/d results in just having one of them on the dbus. Here is my code:
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
)
const introP = `
<node>
<interface name="a.b.c.Ping">
<method name="Ping">
<arg direction="out" type="s"/>
</method>
</interface>` + introspect.IntrospectDataString + `</node> `
type ping string
func (p ping) Ping() (string, *dbus.Error) {
fmt.Println(p)
return string(p), nil
}
const introZ = `
<node>
<interface name="a.b.c.d.Zing">
<method name="Zing">
<arg direction="out" type="s"/>
</method>
</interface>` + introspect.IntrospectDataString + `</node> `
type zing string
func (z zing) Zing() (string, *dbus.Error) {
fmt.Println(z)
return string(z), nil
}
func main() {
conn, err := dbus.ConnectSessionBus()
if err != nil {
panic(err)
}
defer conn.Close()
reply, err := conn.RequestName("a.b.c",
dbus.NameFlagDoNotQueue)
if err != nil {
panic(err)
}
if reply != dbus.RequestNameReplyPrimaryOwner {
fmt.Fprintln(os.Stderr, "name already taken")
os.Exit(1)
}
p := ping("Pong")
conn.Export(p, "/a/b/c", "a.b.c.Ping")
conn.Export(introspect.Introspectable(introP), "/a/b/c",
"org.freedesktop.DBus.Introspectable")
z := zing("Zong")
conn.Export(z, "/a/b/c/d", "a.b.c.d.Zing")
conn.Export(introspect.Introspectable(introZ), "/a/b/c/d",
"org.freedesktop.DBus.Introspectable")
fmt.Println("Listening on dbus...")
select {}
}
package main
import (
"fmt"
"os"
"github.com/godbus/dbus/v5"
"github.com/godbus/dbus/v5/introspect"
)
type ping string
func (p ping) Ping() (string, *dbus.Error) {
fmt.Println(p)
return string(p), nil
}
type zing string
func (z zing) Zing() (string, *dbus.Error) {
fmt.Println(z)
return string(z), nil
}
func main() {
conn, err := dbus.SessionBus()
if err != nil {
panic(err)
}
replyP, errP := conn.RequestName("a.b.c.d.Ping",
dbus.NameFlagDoNotQueue)
if errP != nil {
panic(errP)
}
if replyP != dbus.RequestNameReplyPrimaryOwner {
fmt.Fprintln(os.Stderr, "name already taken")
os.Exit(1)
}
p := ping("Pong")
var introP = &introspect.Node{
Name: "/a/b/c/d/Ping",
Interfaces: []introspect.Interface{
introspect.IntrospectData,
{
Name: "a.b.c.d.Ping",
Methods: introspect.Methods(p),
},
},
}
conn.Export(p, "/a/b/c/d/Ping", "a.b.c.d.Ping")
z := zing("Zong")
var introZ = &introspect.Node{
Name: "/a/b/c/Zing",
Interfaces: []introspect.Interface{
introspect.IntrospectData,
{
Name: "a.b.c.Zing",
Methods: introspect.Methods(z),
},
},
}
conn.Export(z, "/a/b/c/Zing", "a.b.c.Zing")
conn.Export(introspect.NewIntrospectable(&introspect.Node{
Name: "/",
Children: []introspect.Node{
{
Name: "a",
},
},
}), "/", "org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(&introspect.Node{
Name: "/a",
Children: []introspect.Node{
{
Name: "b",
},
},
}), "/com", "org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(&introspect.Node{
Name: "/a/b",
Children: []introspect.Node{
{
Name: "c",
},
},
}), "/a/b", "org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(&introspect.Node{
Name: "/a/b/c",
Children: []introspect.Node{
{
Name: "d",
},
{
Name: "Zing",
},
},
}), "/a/b/c", "org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(&introspect.Node{
Name: "/a/b/c/d",
Children: []introspect.Node{
{
Name: "Ping",
},
},
}), "/a/b/c/d", "org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(introP), "/a/b/c/d/Ping",
"org.freedesktop.DBus.Introspectable")
conn.Export(introspect.NewIntrospectable(introZ), "/a/b/c/Zing",
"org.freedesktop.DBus.Introspectable")
fmt.Printf("Listening on %s / %s ...\n", "a.b.c...", "/a/b/c...")
select {}
}

Dynamodb unmarshal map into struct

I am trying to retrieve a column from dynamodb which is saved as map[string][]string (map with key as string and values as a list of string)
example:
{
{ "string1": ["1", "2"]},
{ "string2": ["3", "4"]},
}
I am getting the data but I am not able to unmarshal it and store it in the struct
type Record struct {
listOfMap map[string][]string
}
scanInput := &dynamodb.ScanInput{
TableName: aws.String("someTable"),
ProjectionExpression: aws.String("desiredColumnToRetrieve"),
}
dynamodbOutput, err := svc.dynamodb.Scan(scanInput)
if err != nil {
log.Errorf("dynamodb.Scan() error - %s", err)
}
fmt.Println(dynamodbOutput)
records := []Record{}
err = dynamodbattribute.UnmarshalListOfMaps(dynamodbOutput.Items, &records)
if err != nil {
log.Errorf("dynamodbattribute.UnmarshalListOfMaps() error - %s", err)
}
fmt.Println(records)
dynamodb output (this output is from the above print statements):
{
Count: 2,
Items: [{
desiredColumnToRetrieve: {
M: {
string1: {
L: [{
S: "1"
},{
S: "2"
}]
}
}
}
},{
desiredColumnToRetrieve: {
M: {
string2: {
L: [{
S: "3"
},{
S: "4"
}]
}
}
}
}],
ScannedCount: 2
}
{[map[] map[]}
The data is not getting saved to the struct/map
Probably it would not work, you need to iterate over items first, like:
for _, i := range dynamodbOutput.Items {
record := Record{}
err = dynamodbattribute.UnmarshalMap(i, &record)
And in fact, your listOfMaps is regular map. Why do you call so? It's rather mapOfLists
By the way doing full scans on NoSQL tables isn't the best practice, if you are going to do always, you'd better to review partitioning sorting keys and whole data schema. Cheers.

Add startup-script while creating instance in gcp using golang

I am trying to create an instance with a startup-script in gcp using google.golang.org/api/compute/v1. However I am having some problems setting up the metadata to pass the startup-script.
Link to a similar example.
Link to do library documentation.
The function I created is the following one:
func CreateInstance(service *compute.Service, projectId string, instanceName string, zone string) {
imageURL := "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140606"
prefix := "https://www.googleapis.com/compute/v1/projects/" + projectId
file, err := os.Open("startup-script.sh")
if err != nil {
log.Fatal(err)
}
instance := &compute.Instance{
Name: instanceName,
Description: "compute sample instance",
MachineType: prefix + "/zones/" + zone + "/machineTypes/n1-standard-1",
Disks: []*compute.AttachedDisk{
{
AutoDelete: true,
Boot: true,
Type: "PERSISTENT",
InitializeParams: &compute.AttachedDiskInitializeParams{
DiskName: "my-root-pd",
SourceImage: imageURL,
},
},
},
ServiceAccounts: []*compute.ServiceAccount{
{
Email: "default",
Scopes: []string{
compute.DevstorageFullControlScope,
compute.ComputeScope,
},
},
},
Metadata: &compute.Metadata{
{
Items: &compute.MetadataItems{
{
Key: "startup-script",
Value : file,
},
},
},
},
}
op, err := service.Instances.Insert(projectId, zone, instance).Do()
log.Printf("Got compute.Operation, err: %#v, %v", op, err)
etag := op.Header.Get("Etag")
log.Printf("Etag=%v", etag)
}
However I am getting the following error:
./createInstance.go:54:4: missing type in composite literal
./createInstance.go:54:4: too few values in &compute.Metadata literal
Can someone point what I am doing wrong?
The problem are the brackets around Metadata. It should be:
Metadata: &compute.Metadata{
Items: &compute.MetadataItems{
{
Key: "startup-script",
Value : file,
},
},
},

Using the Go SDK, how do I set a DynamoDB table item field to a map?

I am trying to use the UpdateItem method to set the value of a table item field to a map. Here's what I have tried:
type myStruct {
name string
}
myStructInstance := myStruct{name: "foo"}
_, err := svc.UpdateItem(&dynamodb.UpdateItemInput{
TableName: aws.String("MyTable"),
Key: myKey,
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":m": {
M: dynamodbattribute.MarshalMap(*myStructInstance),
},
},
UpdateExpression: aws.String("SET myField = :m"),
})
With this, I keep getting the error:
multiple-value dynamodbattribute.MarshalMap() in single-value context
What am I doing wrong?
Ah, I forgot that the MarshalMap function has two return values, so I have to do this instead:
av, err := dynamodbattribute.MarshalMap(*myStructInstance)
_, err = svc.UpdateItem(&dynamodb.UpdateItemInput{
TableName: aws.String("MyTable"),
Key: myKey,
ExpressionAttributeValues: map[string]*dynamodb.AttributeValue{
":m": {
M: av,
},
},
UpdateExpression: aws.String("SET myField = :m"),
})

Resources