components-contrib/bindings/commercetools/commercetools.go

209 lines
6.1 KiB
Go

/*
Copyright 2021 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package commercetools
import (
"context"
"encoding/json"
"errors"
"fmt"
"reflect"
"github.com/dapr/components-contrib/bindings"
contribMetadata "github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
"github.com/labd/commercetools-go-sdk/platform"
"golang.org/x/oauth2/clientcredentials"
)
type Binding struct {
client *platform.Client
logger logger.Logger
projectKey string
}
type Data struct {
CommercetoolsAPI string
Query string
}
type commercetoolsMetadata struct {
Region string
Provider string
ProjectKey string
ClientID string
ClientSecret string
Scopes string
}
func NewCommercetools(logger logger.Logger) bindings.OutputBinding {
return &Binding{logger: logger}
}
// Init does metadata parsing and connection establishment.
func (ct *Binding) Init(_ context.Context, metadata bindings.Metadata) error {
commercetoolsM, err := ct.getCommercetoolsMetadata(metadata)
if err != nil {
return err
}
ct.projectKey = commercetoolsM.ProjectKey
// The helper method NewClientEndpoint no longer exists, so URLs need to be manually constructed.
// Reference: https://github.com/labd/commercetools-go-sdk/blob/15f4e7e85260cf206301504dced00a8bbf4d8682/commercetools/client.go#L115
baseURLdomain := fmt.Sprintf("%s.%s.commercetools.com", commercetoolsM.Region, commercetoolsM.Provider)
authURL := fmt.Sprintf("https://auth.%s/oauth/token", baseURLdomain)
apiURL := fmt.Sprintf("https://api.%s", baseURLdomain)
// Create the new client. When an empty value is passed it will use the CTP_*
// environment variables to get the value. The HTTPClient arg is optional,
// and when empty will automatically be created using the env values.
client, err := platform.NewClient(&platform.ClientConfig{
URL: apiURL,
Credentials: &clientcredentials.Config{
TokenURL: authURL,
ClientID: commercetoolsM.ClientID,
ClientSecret: commercetoolsM.ClientSecret,
Scopes: []string{commercetoolsM.Scopes},
},
})
if err != nil {
ct.logger.Errorf("error creating commercetools client: %s", err)
return err
}
ct.client = client
return nil
}
func (ct *Binding) Operations() []bindings.OperationKind {
return []bindings.OperationKind{bindings.CreateOperation}
}
// Invoke is triggered from Dapr.
func (ct *Binding) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) {
var reqData Data
err := json.Unmarshal(req.Data, &reqData)
if err != nil {
return nil, err
}
query := reqData.Query
res := &bindings.InvokeResponse{Data: nil, Metadata: nil}
if len(reqData.CommercetoolsAPI) > 0 {
ct.logger.Infof("commercetoolsAPI: %s", reqData.CommercetoolsAPI)
if reqData.CommercetoolsAPI == "GraphQLQuery" {
res, err = handleGraphQLQuery(ctx, ct, query)
if err != nil {
ct.logger.Errorf("error GraphQLQuery")
return nil, err
}
}
} else {
return nil, errors.New("commercetools error: No commercetools API provided")
}
return res, nil
}
// handleGraphQLQuery executes the provided query against the commercetools backend.
func handleGraphQLQuery(ctx context.Context, ct *Binding, query string) (*bindings.InvokeResponse, error) {
ct.logger.Infof("handleGraphQLQuery")
res := &bindings.InvokeResponse{Data: nil, Metadata: nil}
if len(query) > 0 {
gql := ct.client.WithProjectKey(ct.projectKey).Graphql().Post(platform.GraphQLRequest{
Query: query,
})
gqlResp, errGQL := gql.Execute(ctx)
if errGQL != nil {
return nil, errors.New("commercetools error: Error executing the provided GraphQL query")
}
bQuery, errM := json.Marshal(gqlResp.Data)
if errM != nil {
return nil, errors.New("commercetools error: Error marshalling GraphQL query result")
}
res = &bindings.InvokeResponse{Data: bQuery, Metadata: nil}
} else {
return res, errors.New("commercetools error: No GraphQL query is provided")
}
return res, nil
}
// getCommercetoolsMetadata returns new commercetools metadata object.
func (ct *Binding) getCommercetoolsMetadata(metadata bindings.Metadata) (*commercetoolsMetadata, error) {
meta := commercetoolsMetadata{}
if val, ok := metadata.Properties["region"]; ok && val != "" {
meta.Region = val
} else {
return nil, errors.New("commercetools error: missing `region` configuration")
}
if val, ok := metadata.Properties["provider"]; ok && val != "" {
meta.Provider = val
} else {
return nil, errors.New("commercetools error: missing `provider` configuration")
}
if val, ok := metadata.Properties["projectKey"]; ok && val != "" {
meta.ProjectKey = val
} else {
return nil, errors.New("commercetools error: missing `projectKey` configuration")
}
if val, ok := metadata.Properties["clientID"]; ok && val != "" {
meta.ClientID = val
} else {
return nil, errors.New("commercetools error: missing `clientID` configuration")
}
if val, ok := metadata.Properties["clientSecret"]; ok && val != "" {
meta.ClientSecret = val
} else {
return nil, errors.New("commercetools error: missing `clientSecret` configuration")
}
if val, ok := metadata.Properties["scopes"]; ok && val != "" {
meta.Scopes = val
} else {
return nil, errors.New("commercetools error: missing `scopes` configuration")
}
return &meta, nil
}
// Close releases the client.
func (ct *Binding) Close() error {
ct.client = nil
return nil
}
// GetComponentMetadata returns the metadata of the component.
func (ct Binding) GetComponentMetadata() (metadataInfo contribMetadata.MetadataMap) {
metadataStruct := commercetoolsMetadata{}
contribMetadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, contribMetadata.BindingType)
return
}