mirror of
https://github.com/lukaszraczylo/go-simple-graphql.git
synced 2026-10-12 00:45:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5655ae2fa
|
||
|
|
9b65ba069f
|
||
|
|
feed2f36c2
|
@@ -9,6 +9,7 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lukaszraczylo/pandati v0.0.15
|
github.com/lukaszraczylo/pandati v0.0.15
|
||||||
github.com/rs/zerolog v1.27.0
|
github.com/rs/zerolog v1.27.0
|
||||||
|
github.com/sethvargo/go-retry v0.2.3
|
||||||
github.com/stretchr/testify v1.7.1
|
github.com/stretchr/testify v1.7.1
|
||||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
|
||||||
)
|
)
|
||||||
@@ -25,7 +26,7 @@ require (
|
|||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/wI2L/jsondiff v0.2.0 // indirect
|
github.com/wI2L/jsondiff v0.2.0 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
|
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
|||||||
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
||||||
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
|
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
|
||||||
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
|
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
|
||||||
|
github.com/sethvargo/go-retry v0.2.3 h1:oYlgvIvsju3jNbottWABtbnoLC+GDtLdBHxKWxQm/iU=
|
||||||
|
github.com/sethvargo/go-retry v0.2.3/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||||
@@ -73,6 +75,8 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
|
||||||
|
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/allegro/bigcache/v3"
|
"github.com/allegro/bigcache/v3"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/lukaszraczylo/go-simple-graphql/pkg/logging"
|
"github.com/lukaszraczylo/go-simple-graphql/pkg/logging"
|
||||||
|
retry "github.com/sethvargo/go-retry"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,7 +39,8 @@ type GraphQL struct {
|
|||||||
CacheStore *bigcache.BigCache
|
CacheStore *bigcache.BigCache
|
||||||
RetriesEnable bool
|
RetriesEnable bool
|
||||||
RetriesNumber int
|
RetriesNumber int
|
||||||
RetriesDelay int
|
RetriesDelay time.Duration
|
||||||
|
BackoffSetup retry.Backoff
|
||||||
}
|
}
|
||||||
|
|
||||||
func pickGraphqlEndpoint() (graphqlEndpoint string) {
|
func pickGraphqlEndpoint() (graphqlEndpoint string) {
|
||||||
@@ -86,7 +88,7 @@ func NewConnection() *GraphQL {
|
|||||||
ReadIdleTimeout: 30 * time.Second,
|
ReadIdleTimeout: 30 * time.Second,
|
||||||
DisableCompression: false,
|
DisableCompression: false,
|
||||||
AllowHTTP: true,
|
AllowHTTP: true,
|
||||||
PingTimeout: 10 * time.Second,
|
PingTimeout: 5 * time.Second,
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
@@ -98,6 +100,7 @@ func NewConnection() *GraphQL {
|
|||||||
RetriesEnable: false,
|
RetriesEnable: false,
|
||||||
RetriesNumber: 1,
|
RetriesNumber: 1,
|
||||||
RetriesDelay: 250,
|
RetriesDelay: 250,
|
||||||
|
BackoffSetup: nil,
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
retriesEnable, retriesEnableExists := os.LookupEnv("RETRIES_ENABLE")
|
retriesEnable, retriesEnableExists := os.LookupEnv("RETRIES_ENABLE")
|
||||||
@@ -115,9 +118,16 @@ func NewConnection() *GraphQL {
|
|||||||
}
|
}
|
||||||
retriesDelay, retriesDelayExists := os.LookupEnv("RETRIES_DELAY")
|
retriesDelay, retriesDelayExists := os.LookupEnv("RETRIES_DELAY")
|
||||||
if retriesDelayExists {
|
if retriesDelayExists {
|
||||||
g.RetriesDelay, err = strconv.Atoi(retriesDelay)
|
retDel, err := strconv.Atoi(retriesDelay)
|
||||||
|
if err != nil {
|
||||||
|
panic("Invalid value for RETRIES_DELAY")
|
||||||
|
}
|
||||||
|
g.RetriesDelay = time.Duration(retDel) * time.Millisecond
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
g.BackoffSetup = retry.NewExponential(g.RetriesDelay)
|
||||||
|
g.BackoffSetup = retry.WithMaxRetries(uint64(g.RetriesNumber), g.BackoffSetup)
|
||||||
|
|
||||||
return &g
|
return &g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ package gql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
retry "github.com/avast/retry-go"
|
|
||||||
"github.com/lukaszraczylo/pandati"
|
"github.com/lukaszraczylo/pandati"
|
||||||
|
retry "github.com/sethvargo/go-retry"
|
||||||
)
|
)
|
||||||
|
|
||||||
type requestBase struct {
|
type requestBase struct {
|
||||||
@@ -41,6 +41,7 @@ func (g *GraphQL) queryBuilder(queryContent string, queryVariables interface{})
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHeaders map[string]interface{}) (responseContent string, err error) {
|
func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHeaders map[string]interface{}) (responseContent string, err error) {
|
||||||
|
ctx := context.Background()
|
||||||
g.Log.Debug("Query details", map[string]interface{}{"_query": queryContent, "_variables": queryVariables})
|
g.Log.Debug("Query details", map[string]interface{}{"_query": queryContent, "_variables": queryVariables})
|
||||||
query, err := g.queryBuilder(queryContent, queryVariables)
|
query, err := g.queryBuilder(queryContent, queryVariables)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,52 +66,48 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
|
|||||||
}
|
}
|
||||||
|
|
||||||
httpRequest, err := http.NewRequest("POST", g.Endpoint, bytes.NewBuffer(query))
|
httpRequest, err := http.NewRequest("POST", g.Endpoint, bytes.NewBuffer(query))
|
||||||
//httpRequest.Header.Add("Content-Type", "application/json")
|
// httpRequest.Header.Set("Content-Type", "application/json")
|
||||||
|
if err != nil {
|
||||||
|
g.Log.Error("Unable to create the request", map[string]interface{}{"_error": err.Error()})
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
for header, value := range queryHeaders {
|
for header, value := range queryHeaders {
|
||||||
httpRequest.Header.Add(fmt.Sprintf("%v", header), fmt.Sprintf("%v", value))
|
httpRequest.Header.Add(fmt.Sprintf("%v", header), fmt.Sprintf("%v", value))
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpResponse *http.Response
|
var httpResponse *http.Response
|
||||||
|
|
||||||
err = retry.Do(
|
retry.Do(ctx, g.BackoffSetup, func(ctx context.Context) error {
|
||||||
func() error {
|
httpResponse, err = g.HttpClient.Do(httpRequest)
|
||||||
httpResponse, err = g.HttpClient.Do(httpRequest)
|
if err != nil {
|
||||||
if err != nil {
|
g.Log.Debug("Unable to send the query RETRY", map[string]interface{}{"_error": err.Error()})
|
||||||
g.Log.Error("Unable to send the query", map[string]interface{}{"_error": err.Error()})
|
return err
|
||||||
return err
|
}
|
||||||
}
|
if httpResponse.StatusCode <= 200 && httpResponse.StatusCode >= 204 {
|
||||||
if httpResponse.StatusCode <= 200 && httpResponse.StatusCode >= 204 {
|
return retry.RetryableError(errors.New(fmt.Sprintf("%v", httpResponse.StatusCode)))
|
||||||
return errors.New(fmt.Sprintf("%v", httpResponse.StatusCode))
|
}
|
||||||
}
|
|
||||||
|
|
||||||
defer io.Copy(ioutil.Discard, httpResponse.Body)
|
defer io.Copy(ioutil.Discard, httpResponse.Body)
|
||||||
defer httpResponse.Body.Close()
|
defer httpResponse.Body.Close()
|
||||||
|
|
||||||
body, err = ioutil.ReadAll(httpResponse.Body)
|
body, err = ioutil.ReadAll(httpResponse.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log.Critical("Unable to read the response", map[string]interface{}{"_error": err.Error()})
|
g.Log.Debug("Unable to read the response", map[string]interface{}{"_error": err.Error()})
|
||||||
return errors.New(fmt.Sprintf("body parsing %v", httpResponse.StatusCode))
|
return retry.RetryableError(errors.New(fmt.Sprintf("RETRY: body parsing %v", httpResponse.StatusCode)))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(body, &queryResult)
|
err = json.Unmarshal(body, &queryResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log.Error("Unable to unmarshal the query", map[string]interface{}{"_error": err.Error()})
|
g.Log.Debug("Unable to unmarshal the query", map[string]interface{}{"_error": err.Error()})
|
||||||
return errors.New(fmt.Sprintf("body unmarshal %v", httpResponse.StatusCode))
|
return retry.RetryableError(errors.New(fmt.Sprintf("RETRY: body unmarshal %v", httpResponse.StatusCode)))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
})
|
||||||
retry.Attempts(uint(g.RetriesNumber)),
|
|
||||||
retry.OnRetry(
|
|
||||||
func(n uint, err error) {
|
|
||||||
g.Log.Debug("Retrying the query", map[string]interface{}{"_attempt": n, "_error": err.Error()})
|
|
||||||
time.Sleep(time.Duration(g.RetriesDelay) * time.Millisecond)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
if !pandati.IsZero(queryResult.Errors) || err != nil {
|
if !pandati.IsZero(queryResult.Errors) {
|
||||||
g.Log.Error("Query returned error", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_error": fmt.Sprintf("%v", queryResult.Errors), "_response_code": httpResponse.StatusCode})
|
g.Log.Error("Query returned error", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_error": fmt.Sprintf("%v", queryResult.Errors), "_response_code": httpResponse.StatusCode})
|
||||||
return "", fmt.Errorf("%v", queryResult.Errors[0].Message)
|
return "", fmt.Errorf("%v", queryResult.Errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pandati.IsZero(queryResult.Data) {
|
if pandati.IsZero(queryResult.Data) {
|
||||||
|
|||||||
+1
-1
@@ -143,7 +143,7 @@ func (suite *TestSuite) Test_GraphQL_Query() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valid query to https endpoint",
|
name: "Valid query to https endpoint",
|
||||||
endpoint: "https://telegram-bot.app/v1/graphql",
|
endpoint: "https://hasura.local/v1/graphql",
|
||||||
isLocal: false,
|
isLocal: false,
|
||||||
args: args{
|
args: args{
|
||||||
queryContent: `query packages_prices {
|
queryContent: `query packages_prices {
|
||||||
|
|||||||
Reference in New Issue
Block a user