Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/googleps04 94 map datatype #9

Open
wants to merge 11 commits into
base: map_datatype_support
Choose a base branch
from
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@

# Project Title

A brief description of what this project does and who it's for

# DynamoDB Adapter

[![Join the chat at
Expand Down Expand Up @@ -56,6 +61,14 @@ DynamoDB Adapter currently supports the following DynamoDB data types
| `BOOL` (boolean) | `BOOL`
| `B` (binary type) | `BYTES(MAX)`
| `S` (string and data values) | `STRING(MAX)`
| `M` (map and data values) | `JSON`

- Note: The Map (M) supports the following types for values, while the key type is always a string.
- `N` - number.
- `BOOL` - boolean
- `B` - binary
- `S` - string
- `M` - map

## Configuration

Expand Down Expand Up @@ -264,6 +277,9 @@ so that configuration files can be loaded directly from go file.
rice embed-go
```

## Limitations
1. The `FilterExpression` for Query and Scan API has the support only for `=`operator.

## API Documentation

This is can be imported in Postman or can be used for Swagger UI.
Expand Down
13 changes: 7 additions & 6 deletions api/v1/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func parseUpdateExpresstion(actionValue string) *models.UpdateExpressionConditio
return expr
}

func performOperation(ctx context.Context, action string, actionValue string, updateAtrr models.UpdateAttr, oldRes map[string]interface{}) (map[string]interface{}, map[string]interface{}, error) {
func performOperation(ctx context.Context, action string, actionValue string, updateAtrr models.UpdateAttr, oldRes map[string]interface{}, spannerRow map[string]interface{}) (map[string]interface{}, map[string]interface{}, error) {
switch {
case action == "DELETE":
// perform delete
Expand All @@ -234,7 +234,7 @@ func performOperation(ctx context.Context, action string, actionValue string, up
case action == "SET":
// Update data in table
m, expr := parseActionValue(actionValue, updateAtrr, false)
res, err := services.Put(ctx, updateAtrr.TableName, m, expr, updateAtrr.ConditionExpression, updateAtrr.ExpressionAttributeMap, oldRes)
res, err := services.Put(ctx, updateAtrr.TableName, m, expr, updateAtrr.ConditionExpression, updateAtrr.ExpressionAttributeMap, oldRes, spannerRow)
return res, m, err
case action == "ADD":
// Add data in table
Expand All @@ -254,8 +254,9 @@ func performOperation(ctx context.Context, action string, actionValue string, up
func UpdateExpression(ctx context.Context, updateAtrr models.UpdateAttr) (interface{}, error) {
updateAtrr.ExpressionAttributeNames = ChangeColumnToSpannerExpressionName(updateAtrr.TableName, updateAtrr.ExpressionAttributeNames)
var oldRes map[string]interface{}
var spannerRow map[string]interface{}
if updateAtrr.ReturnValues != "NONE" {
oldRes, _ = services.GetWithProjection(ctx, updateAtrr.TableName, updateAtrr.PrimaryKeyMap, "", nil)
oldRes, spannerRow, _ = services.GetWithProjection(ctx, updateAtrr.TableName, updateAtrr.PrimaryKeyMap, "", nil)
}
var resp map[string]interface{}
var actVal = make(map[string]interface{})
Expand All @@ -266,7 +267,7 @@ func UpdateExpression(ctx context.Context, updateAtrr models.UpdateAttr) (interf
}
m := extractOperations(updateAtrr.UpdateExpression)
for k, v := range m {
res, acVal, err := performOperation(ctx, k, v, updateAtrr, oldRes)
res, acVal, err := performOperation(ctx, k, v, updateAtrr, oldRes, spannerRow)
resp = res
er = err
for k, v := range acVal {
Expand Down Expand Up @@ -454,7 +455,7 @@ func ChangeResponseColumn(obj map[string]interface{}) map[string]interface{} {
// ChangeColumnToSpanner converts original column name to spanner supported column names
func ChangeColumnToSpanner(obj map[string]interface{}) map[string]interface{} {
rs := make(map[string]interface{})

for k, v := range obj {

if k1, ok := models.ColumnToOriginalCol[k]; ok {
Expand Down Expand Up @@ -631,7 +632,7 @@ func ChangeQueryResponseColumn(tableName string, obj map[string]interface{}) map
return obj
}

//ChangeMaptoDynamoMap converts simple map into dynamo map
// ChangeMaptoDynamoMap converts simple map into dynamo map
func ChangeMaptoDynamoMap(in interface{}) (map[string]interface{}, error) {
if in == nil {
return nil, nil
Expand Down
12 changes: 6 additions & 6 deletions api/v1/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func RouteRequest(c *gin.Context) {
case "UpdateItem":
Update(c)
default:
c.JSON(errors.New("ValidationException", "Invalid X-Amz-Target header value of" + amzTarget).
c.JSON(errors.New("ValidationException", "Invalid X-Amz-Target header value of"+amzTarget).
shoaibcldcvr marked this conversation as resolved.
Show resolved Hide resolved
HTTPResponse("X-Amz-Target Header not supported"))
}
}
Expand Down Expand Up @@ -146,11 +146,11 @@ func put(ctx context.Context, tableName string, putObj map[string]interface{}, e
pKey := tableConf.PartitionKey
var oldResp map[string]interface{}

oldResp, err = storage.GetStorageInstance().SpannerGet(ctx, tableName, putObj[pKey], putObj[sKey], nil)
oldResp, spannerRow, err := storage.GetStorageInstance().SpannerGet(ctx, tableName, putObj[pKey], putObj[sKey], nil)
if err != nil {
return nil, err
}
res, err := services.Put(ctx, tableName, putObj, nil, conditionExp, expressionAttr, oldResp)
res, err := services.Put(ctx, tableName, putObj, nil, conditionExp, expressionAttr, oldResp, spannerRow)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -290,7 +290,7 @@ func GetItemMeta(c *gin.Context) {
return
}
getItemMeta.ExpressionAttributeNames = ChangeColumnToSpannerExpressionName(getItemMeta.TableName, getItemMeta.ExpressionAttributeNames)
res, rowErr := services.GetWithProjection(c.Request.Context(), getItemMeta.TableName, getItemMeta.PrimaryKeyMap, getItemMeta.ProjectionExpression, getItemMeta.ExpressionAttributeNames)
res, _, rowErr := services.GetWithProjection(c.Request.Context(), getItemMeta.TableName, getItemMeta.PrimaryKeyMap, getItemMeta.ProjectionExpression, getItemMeta.ExpressionAttributeNames)
if rowErr == nil {
changedColumns := ChangeResponseToOriginalColumns(getItemMeta.TableName, res)
output, err := ChangeMaptoDynamoMap(changedColumns)
Expand Down Expand Up @@ -431,7 +431,7 @@ func DeleteItem(c *gin.Context) {
deleteItem.ConditionExpression = strings.ReplaceAll(deleteItem.ConditionExpression, k, v)
}

oldRes, _ := services.GetWithProjection(c.Request.Context(), deleteItem.TableName, deleteItem.PrimaryKeyMap, "", nil)
oldRes, _, _ := services.GetWithProjection(c.Request.Context(), deleteItem.TableName, deleteItem.PrimaryKeyMap, "", nil)
err := services.Delete(c.Request.Context(), deleteItem.TableName, deleteItem.PrimaryKeyMap, deleteItem.ConditionExpression, deleteItem.ExpressionAttributeMap, nil)
if err == nil {
output, _ := ChangeMaptoDynamoMap(ChangeResponseToOriginalColumns(deleteItem.TableName, oldRes))
Expand Down Expand Up @@ -657,7 +657,7 @@ func batchUpdateItems(con context.Context, batchMetaUpdate models.BatchMetaUpdat
if err != nil {
return err
}
err = services.BatchPut(con, batchMetaUpdate.TableName, batchMetaUpdate.ArrAttrMap)
err = services.BatchPut(con, batchMetaUpdate.TableName, batchMetaUpdate.ArrAttrMap, nil)
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions config-files/staging/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"GoogleProjectID": "Your Google Project ID",
"SpannerDb": "Your Spanner Database Name",
"QueryLimit": 5000
}
"GoogleProjectID": "Your Google Project ID",
"SpannerDb": "Your Spanner Database Name",
"QueryLimit": 5000
}
8 changes: 4 additions & 4 deletions config-files/staging/spanner.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dynamodb_adapter_table_ddl": "instance-ID of dynamodb_adapter_table_ddl table",
"dynamodb_adapter_config_manager": "instance-ID of dynamodb_adapter_config_manager table",
"tableName": "instance-ID of Table"
}
"dynamodb_adapter_table_ddl": "instance-ID of dynamodb_adapter_table_ddl table",
"dynamodb_adapter_config_manager": "instance-ID of dynamodb_adapter_config_manager table",
"tableName": "instance-ID of Table"
}
4 changes: 2 additions & 2 deletions config-files/staging/tables.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
"ColDate": "S",
"ColTimestamp": "S"
},
"indices": {
"indices": {
"indexName1": {
"sortKey": "sort key for indexName1",
"partitionKey": "partition key for indexName1"
}
}
}
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/cloudspannerecosystem/dynamodb-adapter

go 1.17
go 1.22

require (
cloud.google.com/go/pubsub v1.17.0
Expand Down
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ github.com/swaggo/swag v1.7.1 h1:gY9ZakXlNWg/i/v5bQBic7VMZ4teq4m89lpiao74p/s=
github.com/swaggo/swag v1.7.1/go.mod h1:gAiHxNTb9cIpNmA/VEGUP+CyZMCP/EW7mdtc8Bny+p8=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E=
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
Expand Down
19 changes: 17 additions & 2 deletions integrationtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ Running the integration tests will require the files present in the
"dynamodb_adapter_table_ddl": "<spanner-instance-name>",
"dynamodb_adapter_config_manager": "<spanner-instance-name>",
"department": "<spanner-instance-name>",
"employee": "<spanner-instance-name>"
"employee": "<spanner-instance-name>",
"mapdynamo": "<spanner-instance-name>"
}
```

Expand Down Expand Up @@ -51,8 +52,22 @@ Running the integration tests will require the files present in the
"d_specialization": "S"
},
"indices": {}
},
"mapdynamo": {
"partitionKey": "guid",
"sortKey": "context",
"spannerIndexName": "guid",
"actualTable": "mapdynamo",
"attributeTypes": {
"guid": "S",
"context": "S",
"contact_ranking_list": "S",
"name": "S",
"address": "M"
},
"indices": {}
}
}
}
```

## Execute tests
Expand Down
Loading