diff --git a/bindings/azure/cosmosgraphdb/cosmosgraphdb.go b/bindings/azure/cosmosgraphdb/cosmosgraphdb.go new file mode 100644 index 0000000000..7962d81b17 --- /dev/null +++ b/bindings/azure/cosmosgraphdb/cosmosgraphdb.go @@ -0,0 +1,123 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation and Dapr Contributors. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +package cosmosgraphdb + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/dapr/components-contrib/bindings" + "github.com/dapr/kit/logger" + gremcos "github.com/supplyon/gremcos" +) + +const ( + queryOperation bindings.OperationKind = "query" + + // keys from request's Data + commandGremlinKey = "gremlin" + + // keys from response's Data + respGremlinKey = "gremlin" + respOpKey = "operation" + respStartTimeKey = "start-time" + respEndTimeKey = "end-time" + respDurationKey = "duration" +) + +// CosmosGraphDB allows performing state operations on collections +type CosmosGraphDB struct { + metadata *cosmosGraphDBCredentials + client *gremcos.Cosmos + logger logger.Logger +} + +type cosmosGraphDBCredentials struct { + URL string `json:"url"` + MasterKey string `json:"masterKey"` + Username string `json:"username"` +} + +// NewCosmosGraphDB returns a new CosmosGraphDB instance +func NewCosmosGraphDB(logger logger.Logger) *CosmosGraphDB { + return &CosmosGraphDB{logger: logger} +} + +// Init performs CosmosDB connection parsing and connecting +func (c *CosmosGraphDB) Init(metadata bindings.Metadata) error { + c.logger.Debug("Initializing Cosmos Graph DB binding") + + m, err := c.parseMetadata(metadata) + if err != nil { + return err + } + c.metadata = m + client, err := gremcos.New(c.metadata.URL, + gremcos.WithAuth(c.metadata.Username, c.metadata.MasterKey), + ) + if err != nil { + return errors.New("CosmosGraphDB Error: failed to create the Cosmos Graph DB connector") + } + + c.client = client + + return nil +} + +func (c *CosmosGraphDB) parseMetadata(metadata bindings.Metadata) (*cosmosGraphDBCredentials, error) { + b, err := json.Marshal(metadata.Properties) + if err != nil { + return nil, err + } + + var creds cosmosGraphDBCredentials + err = json.Unmarshal(b, &creds) + if err != nil { + return nil, err + } + + return &creds, nil +} + +func (c *CosmosGraphDB) Operations() []bindings.OperationKind { + return []bindings.OperationKind{queryOperation} +} + +func (c *CosmosGraphDB) Invoke(req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) { + var jsonPoint map[string]interface{} + err := json.Unmarshal(req.Data, &jsonPoint) + if err != nil { + return nil, errors.New("CosmosGraphDB Error: Cannot convert request data") + } + + gq := fmt.Sprintf("%s", jsonPoint[commandGremlinKey]) + + if gq == "" { + return nil, errors.New("CosmosGraphDB Error: missing data - gremlin query not set") + } + startTime := time.Now().UTC() + resp := &bindings.InvokeResponse{ + Metadata: map[string]string{ + respOpKey: string(req.Operation), + respGremlinKey: gq, + respStartTimeKey: startTime.Format(time.RFC3339Nano), + }, + } + d, err := c.client.Execute(gq) + if err != nil { + return nil, errors.New("CosmosGraphDB Error:error excuting gremlin") + } + if len(d) > 0 { + resp.Data = d[0].Result.Data + } + endTime := time.Now().UTC() + resp.Metadata[respEndTimeKey] = endTime.Format(time.RFC3339Nano) + resp.Metadata[respDurationKey] = endTime.Sub(startTime).String() + + return resp, nil +} diff --git a/bindings/azure/cosmosgraphdb/cosmosgraphdb_test.go b/bindings/azure/cosmosgraphdb/cosmosgraphdb_test.go new file mode 100644 index 0000000000..57f11092aa --- /dev/null +++ b/bindings/azure/cosmosgraphdb/cosmosgraphdb_test.go @@ -0,0 +1,25 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation and Dapr Contributors. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +package cosmosgraphdb + +import ( + "testing" + + "github.com/dapr/components-contrib/bindings" + "github.com/dapr/kit/logger" + "github.com/stretchr/testify/assert" +) + +func TestParseMetadata(t *testing.T) { + m := bindings.Metadata{} + m.Properties = map[string]string{"Url": "a", "masterKey": "a", "username": "a"} + cosmosgraphdb := CosmosGraphDB{logger: logger.NewLogger("test")} + im, err := cosmosgraphdb.parseMetadata(m) + assert.Nil(t, err) + assert.Equal(t, "a", im.URL) + assert.Equal(t, "a", im.MasterKey) + assert.Equal(t, "a", im.Username) +} diff --git a/go.mod b/go.mod index d9c394dced..3a40e8ada0 100644 --- a/go.mod +++ b/go.mod @@ -113,6 +113,7 @@ require ( github.com/sergi/go-diff v1.2.0 // indirect github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 github.com/stretchr/testify v1.7.0 + github.com/supplyon/gremcos v0.1.0 github.com/tidwall/gjson v1.8.0 // indirect github.com/tidwall/pretty v1.2.0 // indirect github.com/valyala/fasthttp v1.21.0 diff --git a/go.sum b/go.sum index eceb9bf223..e55495e430 100644 --- a/go.sum +++ b/go.sum @@ -914,6 +914,7 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= @@ -931,6 +932,7 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= @@ -955,6 +957,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.18.0 h1:CbAm3kP2Tptby1i9sYy2MGRg0uxIN9cyDb59Ys7W8z8= +github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -999,6 +1003,8 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.0-20181021141114-fe5e611709b0/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= @@ -1028,6 +1034,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/supplyon/gremcos v0.1.0 h1:kZdC3P6m8dkfBO4ZLB2XmzHrPu/Z5enwgz6/x8MTIhc= +github.com/supplyon/gremcos v0.1.0/go.mod h1:ZnXsXGVbGCYDFU5GLPX9HZLWfD+ZWkiPo30KUjNoOtw= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/testcontainers/testcontainers-go v0.9.0/go.mod h1:b22BFXhRbg4PJmeMVWh6ftqjyZHgiIl3w274e9r3C2E= @@ -1388,6 +1396,7 @@ golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=