diff --git a/bindings/azure/cosmosdb/cosmosdb.go b/bindings/azure/cosmosdb/cosmosdb.go index 4386c44f8a..7577d6a223 100644 --- a/bindings/azure/cosmosdb/cosmosdb.go +++ b/bindings/azure/cosmosdb/cosmosdb.go @@ -68,6 +68,10 @@ func (c *CosmosDB) Init(metadata bindings.Metadata) error { } config = documentdb.NewConfigWithServicePrincipal(spt) } + // disable the identification hydrator (which autogenerates IDs if missing from the request) + // so we aren't forced to use a struct by the upstream SDK + // this allows us to provide the most flexibility in the request document sent to this binding + config.IdentificationHydrator = nil client := documentdb.New(m.URL, config) dbs, err := client.QueryDatabases(&documentdb.Query{ @@ -122,23 +126,28 @@ func (c *CosmosDB) Operations() []bindings.OperationKind { } func (c *CosmosDB) Invoke(req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) { - var obj interface{} - err := json.Unmarshal(req.Data, &obj) - if err != nil { - return nil, err - } + switch req.Operation { + case bindings.CreateOperation: + var obj interface{} + err := json.Unmarshal(req.Data, &obj) + if err != nil { + return nil, err + } - val, err := c.getPartitionKeyValue(c.partitionKey, obj) - if err != nil { - return nil, err - } + val, err := c.getPartitionKeyValue(c.partitionKey, obj) + if err != nil { + return nil, err + } - _, err = c.client.CreateDocument(c.collection.Self, obj, documentdb.PartitionKey(val)) - if err != nil { - return nil, err - } + _, err = c.client.CreateDocument(c.collection.Self, obj, documentdb.PartitionKey(val)) + if err != nil { + return nil, err + } - return nil, nil + return nil, nil + default: + return nil, fmt.Errorf("operation kind %s not supported", req.Operation) + } } func (c *CosmosDB) getPartitionKeyValue(key string, obj interface{}) (interface{}, error) { diff --git a/tests/config/bindings/azure/cosmosdb/bindings.yml b/tests/config/bindings/azure/cosmosdb/bindings.yml new file mode 100644 index 0000000000..620017f3bb --- /dev/null +++ b/tests/config/bindings/azure/cosmosdb/bindings.yml @@ -0,0 +1,19 @@ +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: azure-cosmosdb-binding + namespace: default +spec: + type: bindings.azure.cosmosdb + version: v1 + metadata: + - name: url + value: ${{AzureCosmosDBUrl}} + - name: masterKey + value: ${{AzureCosmosDBMasterKey}} + - name: database + value: ${{AzureCosmosDB}} + - name: collection + value: ${{AzureCosmosDBCollection}} + - name: partitionKey + value: partitionKey diff --git a/tests/config/bindings/tests.yml b/tests/config/bindings/tests.yml index 08e6bc6ff1..f60a5dc90a 100644 --- a/tests/config/bindings/tests.yml +++ b/tests/config/bindings/tests.yml @@ -17,6 +17,10 @@ components: config: output: blobName: $((uuid)) + - component: azure.cosmosdb + operations: ["create", "operations"] + config: + outputData: '{"id": "$((uuid))", "orderid": "abcdef-test", "partitionKey": "partitionValue", "nestedproperty": {"subproperty": "something of value for testing"}, "description": "conformance test item"}' - component: azure.eventhubs operations: ["create", "operations", "read"] - component: azure.eventgrid diff --git a/tests/conformance/common.go b/tests/conformance/common.go index 36f45bbba8..e8aa5e7c4e 100644 --- a/tests/conformance/common.go +++ b/tests/conformance/common.go @@ -6,6 +6,7 @@ package conformance import ( + "encoding/json" "errors" "fmt" "io/ioutil" @@ -27,6 +28,7 @@ import ( "github.com/dapr/kit/logger" b_azure_blobstorage "github.com/dapr/components-contrib/bindings/azure/blobstorage" + b_azure_cosmosdb "github.com/dapr/components-contrib/bindings/azure/cosmosdb" b_azure_eventgrid "github.com/dapr/components-contrib/bindings/azure/eventgrid" b_azure_eventhubs "github.com/dapr/components-contrib/bindings/azure/eventhubs" b_azure_servicebusqueues "github.com/dapr/components-contrib/bindings/azure/servicebusqueues" @@ -131,6 +133,16 @@ func ParseConfigurationMap(t *testing.T, configMap map[string]interface{}) { val = uuid.New().String() t.Logf("Generated UUID %s", val) configMap[k] = val + } else { + jsonMap := make(map[string]interface{}) + err := json.Unmarshal([]byte(val), &jsonMap) + if err == nil { + ParseConfigurationMap(t, jsonMap) + mapBytes, err := json.Marshal(jsonMap) + if err == nil { + configMap[k] = string(mapBytes) + } + } } case map[string]interface{}: ParseConfigurationMap(t, val) @@ -149,6 +161,16 @@ func parseConfigurationInterfaceMap(t *testing.T, configMap map[interface{}]inte val = uuid.New().String() t.Logf("Generated UUID %s", val) configMap[k] = val + } else { + jsonMap := make(map[string]interface{}) + err := json.Unmarshal([]byte(val), &jsonMap) + if err == nil { + ParseConfigurationMap(t, jsonMap) + mapBytes, err := json.Marshal(jsonMap) + if err == nil { + configMap[k] = string(mapBytes) + } + } } case map[string]interface{}: ParseConfigurationMap(t, val) @@ -405,6 +427,8 @@ func loadOutputBindings(tc TestComponent) bindings.OutputBinding { binding = b_azure_eventgrid.NewAzureEventGrid(testLogger) case eventhubs: binding = b_azure_eventhubs.NewAzureEventHubs(testLogger) + case "azure.cosmosdb": + binding = b_azure_cosmosdb.NewCosmosDB(testLogger) case kafka: binding = b_kafka.NewKafka(testLogger) case "http": diff --git a/tests/conformance/common_test.go b/tests/conformance/common_test.go index 585f4e2c17..189e633b75 100644 --- a/tests/conformance/common_test.go +++ b/tests/conformance/common_test.go @@ -6,6 +6,7 @@ package conformance import ( + "encoding/json" "os" "testing" @@ -104,15 +105,26 @@ func TestConvertMetadataToProperties(t *testing.T) { func TestParseConfigurationMap(t *testing.T) { testMap := map[string]interface{}{ - "key": "$((uuid))", - "blob": "testblob", + "key": "$((uuid))", + "blob": "testblob", + "mapString": `{"nestedkey": "$((uuid))", "somethingtested": "somevalue"}`, + "map": map[string]interface{}{ + "nestedkey": "$((uuid))", + }, } ParseConfigurationMap(t, testMap) - assert.Equal(t, 2, len(testMap)) + assert.Equal(t, 4, len(testMap)) assert.Equal(t, "testblob", testMap["blob"]) _, err := uuid.ParseBytes([]byte(testMap["key"].(string))) assert.NoError(t, err) + + var nestedMap map[string]interface{} + json.Unmarshal([]byte(testMap["mapString"].(string)), &nestedMap) + _, err = uuid.ParseBytes([]byte(nestedMap["nestedkey"].(string))) + assert.NoError(t, err) + _, err = uuid.ParseBytes([]byte(testMap["map"].(map[string]interface{})["nestedkey"].(string))) + assert.NoError(t, err) } func TestConvertComponentNameToPath(t *testing.T) {