Created
June 29, 2022 18:31
-
-
Save mutovkin/4912e83b2f16dd4ac8cf80ce2c51673e to your computer and use it in GitHub Desktop.
DynamoDB Time Formatting Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"fmt" | |
"os" | |
"time" | |
"github.com/aws/aws-sdk-go-v2/aws" | |
"github.com/aws/aws-sdk-go-v2/config" | |
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" | |
"github.com/aws/aws-sdk-go-v2/service/dynamodb" | |
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types" | |
"github.com/google/uuid" | |
"github.com/rs/zerolog" | |
"github.com/rs/zerolog/log" | |
) | |
var awsConfig aws.Config | |
var dynamoDbClient *dynamodb.Client | |
func initAwsConfig(profile, region string) error { | |
var err error | |
var optFns [](func(*config.LoadOptions) error) | |
optFns = append(optFns, config.WithRegion(region)) | |
optFns = append(optFns, config.WithSharedConfigProfile(profile)) | |
// Using the SDK's default configuration, loading additional config | |
// and credentials values from the environment variables, shared | |
// credentials, and shared configuration files | |
customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) { | |
if service == dynamodb.ServiceID && region == "localhost" { | |
return aws.Endpoint{ | |
URL: "http://localhost:8000", | |
}, nil | |
} | |
// returning EndpointNotFoundError will allow the service to fallback to it's default resolution | |
return aws.Endpoint{}, &aws.EndpointNotFoundError{} | |
}) | |
optFns = append(optFns, config.WithEndpointResolver(customResolver)) | |
awsConfig, err = config.LoadDefaultConfig(context.TODO(), optFns...) | |
if err != nil { | |
log.Error().Err(err).Msg("failed to load SDK config") | |
return err | |
} | |
return nil | |
} | |
func configureDynamoDbClient(profile, region string) error { | |
if err := initAwsConfig(profile, region); err != nil { | |
return err | |
} | |
log.Debug(). | |
Interface("AWSConfig", awsConfig.Region). | |
Str("Region", region). | |
Msg("ConfigureDynamoDbClient") | |
dynamoDbClient = dynamodb.NewFromConfig(awsConfig) | |
return nil | |
} | |
func CreateTable(client *dynamodb.Client, tableName string) error { | |
tableInput := dynamodb.CreateTableInput{ | |
TableName: aws.String(tableName), | |
KeySchema: []types.KeySchemaElement{ | |
{ | |
AttributeName: aws.String("PK"), | |
KeyType: "HASH", | |
}, | |
{ | |
AttributeName: aws.String("SK"), | |
KeyType: "RANGE", | |
}, | |
}, | |
GlobalSecondaryIndexes: []types.GlobalSecondaryIndex{ | |
{ | |
IndexName: aws.String("GlobalIndex"), | |
KeySchema: []types.KeySchemaElement{ | |
{ | |
AttributeName: aws.String("GSI1PK"), | |
KeyType: "HASH", | |
}, | |
{ | |
AttributeName: aws.String("GSI1SK"), | |
KeyType: "RANGE", | |
}, | |
}, | |
Projection: &types.Projection{ | |
ProjectionType: "ALL", | |
}, | |
ProvisionedThroughput: &types.ProvisionedThroughput{ | |
ReadCapacityUnits: aws.Int64(10), | |
WriteCapacityUnits: aws.Int64(10), | |
}, | |
}, | |
}, | |
AttributeDefinitions: []types.AttributeDefinition{ | |
{ | |
AttributeName: aws.String("PK"), | |
AttributeType: "S", | |
}, | |
{ | |
AttributeName: aws.String("SK"), | |
AttributeType: "S", | |
}, | |
{ | |
AttributeName: aws.String("GSI1PK"), | |
AttributeType: "S", | |
}, | |
{ | |
AttributeName: aws.String("GSI1SK"), | |
AttributeType: "S", | |
}, | |
}, | |
ProvisionedThroughput: &types.ProvisionedThroughput{ | |
ReadCapacityUnits: aws.Int64(10), | |
WriteCapacityUnits: aws.Int64(10), | |
}, | |
} | |
_, err := client.CreateTable(context.TODO(), &tableInput) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
func DeleteTable(client *dynamodb.Client, tableName string) error { | |
deleteTableInput := dynamodb.DeleteTableInput{ | |
TableName: aws.String(tableName), | |
} | |
_, err := client.DeleteTable(context.TODO(), &deleteTableInput) | |
if err != nil { | |
return err | |
} | |
return nil | |
} | |
func createTableIfNotFound(client *dynamodb.Client, tableName string) error { | |
isFound := false | |
resp, err := client.ListTables(context.TODO(), &dynamodb.ListTablesInput{ | |
Limit: aws.Int32(5), | |
}) | |
if err != nil { | |
log.Error().Err(err).Msg("failed to list tables") | |
return err | |
} | |
for _, name := range resp.TableNames { | |
log.Debug().Str("table", name).Msg("dynamodb table found") | |
if tableName == name { | |
isFound = true | |
break | |
} | |
} | |
if !isFound { | |
err := CreateTable(client, tableName) | |
if err != nil { | |
return err | |
} | |
} | |
return nil | |
} | |
func configureLogger() { | |
zerolog.TimeFieldFormat = "2006-01-02T15:04:05.000Z" | |
// set time format UTC | |
zerolog.TimestampFunc = func() time.Time { | |
return time.Now() //.UTC() | |
} | |
log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger() | |
consoleLogger := log.Output(zerolog.ConsoleWriter{ | |
Out: os.Stderr, | |
TimeFormat: "15:04:05.000", | |
}) | |
log.Logger = consoleLogger | |
} | |
type DemoRecord struct { | |
PK string | |
SK string | |
Time time.Time | |
Text string | |
} | |
func main() { | |
tableName := "time-demo-table" | |
configureLogger() | |
if err := configureDynamoDbClient("dev", "us-west-2"); err != nil { | |
log.Panic().Err(err).Msg("failed configureDynamoDbClient") | |
} | |
if err := createTableIfNotFound(dynamoDbClient, tableName); err != nil { | |
log.Panic().Err(err).Msg("failed createTableIfNotFound") | |
} | |
timeFormat := time.RFC3339Nano | |
timeFormat = "2006-01-02T15:04:05.000Z" | |
modDefaultOptionsFunc := func(options *attributevalue.EncoderOptions) { | |
options.EncodeTime = func(t time.Time) (types.AttributeValue, error) { | |
return &types.AttributeValueMemberS{ | |
Value: t.Format(timeFormat), | |
}, nil | |
} | |
} | |
demoRecord := DemoRecord{ | |
PK: uuid.NewString(), | |
SK: "time-demo", | |
Time: time.Now().UTC(), | |
Text: fmt.Sprintf("Time Format Used: %s", timeFormat), | |
} | |
av, err := attributevalue.MarshalMapWithOptions(demoRecord, modDefaultOptionsFunc) | |
if err != nil { | |
log.Panic().Err(err).Msg("failed MarshalMap") | |
} | |
input := &dynamodb.PutItemInput{ | |
Item: av, | |
TableName: aws.String(tableName), | |
ReturnValues: types.ReturnValueAllOld, | |
} | |
output, err := dynamoDbClient.PutItem(context.TODO(), input) | |
if err != nil { | |
log.Panic().Err(err).Msg("failed PutItem operation") | |
} | |
log.Debug().Interface("attributes", output.Attributes).Msg("result") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment