Skip to content

Commit

Permalink
Merge pull request #98 from illacloud/develop
Browse files Browse the repository at this point in the history
Release: v1.6.1
  • Loading branch information
naj1n authored Dec 22, 2022
2 parents 42442ba + 0ec813c commit 2133d6e
Show file tree
Hide file tree
Showing 5 changed files with 307 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pkg/action/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/illa-family/builder-backend/pkg/plugins/common"
"github.com/illa-family/builder-backend/pkg/plugins/elasticsearch"
"github.com/illa-family/builder-backend/pkg/plugins/firebase"
"github.com/illa-family/builder-backend/pkg/plugins/graphql"
"github.com/illa-family/builder-backend/pkg/plugins/mongodb"
"github.com/illa-family/builder-backend/pkg/plugins/mysql"
"github.com/illa-family/builder-backend/pkg/plugins/postgresql"
Expand All @@ -43,6 +44,7 @@ var (
SUPABASEDB_ACTION = "supabasedb"
FIREBASE_ACTION = "firebase"
CLICKHOUSE_ACTION = "clickhouse"
GRAPHQL_ACTION = "graphql"
)

type AbstractActionFactory interface {
Expand Down Expand Up @@ -85,6 +87,9 @@ func (f *Factory) Build() common.DataConnector {
case CLICKHOUSE_ACTION:
clickhouseAction := &clickhouse.Connector{}
return clickhouseAction
case GRAPHQL_ACTION:
graphqlAction := &graphql.Connector{}
return graphqlAction
default:
return nil
}
Expand Down
98 changes: 98 additions & 0 deletions pkg/plugins/graphql/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright 2022 The ILLA 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 graphql

import (
"net/http"
"net/url"

"github.com/go-resty/resty/v2"
)

const (
AUTH_NONE = "none"
AUTH_BASIC = "basic"
AUTH_BEARER = "bearer"
AUTH_APIKEY = "apiKey"
)

func (g *Connector) doQuery(baseURL string, queryParams, headers, cookies map[string]string, authentication string,
authContent map[string]string, query string, vars map[string]interface{}) (*resty.Response, error) {

client := resty.New()

// corner case
if authentication == AUTH_APIKEY && authContent["addTo"] == "urlParams" {
queryParams[authContent["key"]] = authContent["value"]
authentication = AUTH_NONE
}

// build request url
uri, err := url.Parse(baseURL)
if err != nil {
return nil, err
}
params := url.Values{}
for k, v := range queryParams {
if k != "" {
params.Set(k, v)
}
}
uri.RawQuery = params.Encode()
reqURL := uri.String()

// set authentication
switch authentication {
case AUTH_BASIC:
client.SetBasicAuth(authContent["username"], authContent["password"])
break
case AUTH_BEARER:
client.SetAuthToken(authContent["bearerToken"])
break
case AUTH_APIKEY:
client.SetAuthScheme(authContent["headerPrefix"])
client.SetAuthToken(authContent["value"])
break
case AUTH_NONE:
break
}

queryClient := client.R()

// set headers
queryClient.SetHeaders(headers)
queryClient.SetHeader("Content-Type", "application/json")

// set cookies
reqCookies := make([]*http.Cookie, 0, len(cookies))
for k, v := range cookies {
reqCookies = append(reqCookies, &http.Cookie{Name: k, Value: v})
}
queryClient.SetCookies(reqCookies)

// set body
queryClient.SetBody(map[string]interface{}{
"query": query,
"variables": vars,
})

// do the query
resp, err := queryClient.Post(reqURL)
if err != nil {
return nil, err
}

return resp, nil
}
168 changes: 168 additions & 0 deletions pkg/plugins/graphql/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright 2022 The ILLA 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 graphql

import (
"encoding/json"
"errors"

"github.com/illa-family/builder-backend/pkg/plugins/common"

"github.com/go-playground/validator/v10"
"github.com/mitchellh/mapstructure"
)

type Connector struct {
ResourceOpts Resource
ActionOpts Action
}

func (g *Connector) ValidateResourceOptions(resourceOptions map[string]interface{}) (common.ValidateResult, error) {
// format resource options
if err := mapstructure.Decode(resourceOptions, &g.ResourceOpts); err != nil {
return common.ValidateResult{Valid: false}, err
}

// validate graphql options
validate := validator.New()
if err := validate.Struct(g.ResourceOpts); err != nil {
return common.ValidateResult{Valid: false}, err
}

return common.ValidateResult{Valid: true}, nil
}

func (g *Connector) ValidateActionOptions(actionOptions map[string]interface{}) (common.ValidateResult, error) {
// format action options
if err := mapstructure.Decode(actionOptions, &g.ActionOpts); err != nil {
return common.ValidateResult{Valid: false}, err
}

// validate graphql options
validate := validator.New()
if err := validate.Struct(g.ActionOpts); err != nil {
return common.ValidateResult{Valid: false}, err
}

return common.ValidateResult{Valid: true}, nil
}

func (g *Connector) TestConnection(resourceOptions map[string]interface{}) (common.ConnectionResult, error) {
// format resource options
if err := mapstructure.Decode(resourceOptions, &g.ResourceOpts); err != nil {
return common.ConnectionResult{Success: false}, err
}

queryParams := make(map[string]string)
headers := make(map[string]string)
cookies := make(map[string]string)
for _, param := range g.ResourceOpts.URLParams {
if param["key"] != "" {
queryParams[param["key"]] = param["value"]
}
}

for _, header := range g.ResourceOpts.Headers {
if header["key"] != "" {
headers[header["key"]] = header["value"]
}
}

for _, cookie := range g.ResourceOpts.Cookies {
if cookie["key"] != "" {
cookies[cookie["key"]] = cookie["value"]
}
}

resp, err := g.doQuery(g.ResourceOpts.BaseURL, queryParams, headers, cookies, g.ResourceOpts.Authentication,
g.ResourceOpts.AuthContent, "{__typename}", nil)
if err != nil {
return common.ConnectionResult{Success: false}, err
}

if resp.IsError() {
return common.ConnectionResult{Success: false}, errors.New("unknown error")
}

return common.ConnectionResult{Success: true}, nil
}

func (g *Connector) GetMetaInfo(resourceOptions map[string]interface{}) (common.MetaInfoResult, error) {
return common.MetaInfoResult{
Success: true,
Schema: nil,
}, nil
}

func (g *Connector) Run(resourceOptions map[string]interface{}, actionOptions map[string]interface{}) (common.RuntimeResult, error) {
// format resource options
if err := mapstructure.Decode(resourceOptions, &g.ResourceOpts); err != nil {
return common.RuntimeResult{Success: false}, err
}

// format action options
if err := mapstructure.Decode(actionOptions, &g.ActionOpts); err != nil {
return common.RuntimeResult{Success: false}, err
}

queryParams := make(map[string]string)
headers := make(map[string]string)
cookies := make(map[string]string)
for _, param := range g.ResourceOpts.URLParams {
if param["key"] != "" {
queryParams[param["key"]] = param["value"]
}
}

for _, header := range g.ResourceOpts.Headers {
if header["key"] != "" {
headers[header["key"]] = header["value"]
}
}
for _, header := range g.ActionOpts.Headers {
if header["key"] != "" {
headers[header["key"]] = header["value"]
}
}

for _, cookie := range g.ResourceOpts.Cookies {
if cookie["key"] != "" {
cookies[cookie["key"]] = cookie["value"]
}
}

vars := make(map[string]interface{})
for _, variable := range g.ActionOpts.Variables {
if variable["key"] != "" {
vars[variable["key"].(string)] = variable["value"]
}
}

resp, err := g.doQuery(g.ResourceOpts.BaseURL, queryParams, headers, cookies, g.ResourceOpts.Authentication,
g.ResourceOpts.AuthContent, g.ActionOpts.Query, vars)
if err != nil {
return common.RuntimeResult{Success: false}, err
}

if resp.IsError() {
return common.RuntimeResult{Success: false}, errors.New("unknown error")
}
body := make(map[string]interface{})
if err := json.Unmarshal(resp.Body(), &body); err != nil {
return common.RuntimeResult{Success: false}, err
}

return common.RuntimeResult{Success: true, Rows: []map[string]interface{}{body}}, nil
}
31 changes: 31 additions & 0 deletions pkg/plugins/graphql/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2022 The ILLA 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 graphql

type Resource struct {
BaseURL string `validate:"required"`
URLParams []map[string]string
Headers []map[string]string
Cookies []map[string]string
Authentication string `validate:"required,oneof=none basic bearer apiKey"`
AuthContent map[string]string
DisableIntrospection bool
}

type Action struct {
Query string
Variables []map[string]interface{}
Headers []map[string]string
}
5 changes: 5 additions & 0 deletions pkg/resource/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/illa-family/builder-backend/pkg/plugins/common"
"github.com/illa-family/builder-backend/pkg/plugins/elasticsearch"
"github.com/illa-family/builder-backend/pkg/plugins/firebase"
"github.com/illa-family/builder-backend/pkg/plugins/graphql"
"github.com/illa-family/builder-backend/pkg/plugins/mongodb"
"github.com/illa-family/builder-backend/pkg/plugins/mysql"
"github.com/illa-family/builder-backend/pkg/plugins/postgresql"
Expand All @@ -42,6 +43,7 @@ var (
SUPABASEDB_RESOURCE = "supabasedb"
FIREBASE_RESOURCE = "firebase"
CLICKHOUSE_RESOURCE = "clickhouse"
GRAPHQL_RESOURCE = "graphql"
)

type AbstractResourceFactory interface {
Expand Down Expand Up @@ -84,6 +86,9 @@ func (f *Factory) Generate() common.DataConnector {
case CLICKHOUSE_RESOURCE:
clickhouseRsc := &clickhouse.Connector{}
return clickhouseRsc
case GRAPHQL_RESOURCE:
graphqlRsc := &graphql.Connector{}
return graphqlRsc
default:
return nil
}
Expand Down

0 comments on commit 2133d6e

Please sign in to comment.