mirror of
https://github.com/lukaszraczylo/go-simple-graphql.git
synced 2026-10-13 00:48:37 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5c4643a5e
|
||
|
|
42e1afbf77
|
||
|
|
9e8df7e297
|
||
|
|
405d159eb9
|
||
|
|
b3e594fc4f
|
||
|
|
32d9e0bd80
|
@@ -11,6 +11,7 @@ Ps. It's Hasura friendly.
|
||||
- [Environment variables](#environment-variables)
|
||||
- [Cache](#cache)
|
||||
- [Example reader code](#example-reader-code)
|
||||
- [Tips](#tips)
|
||||
- [Working with results](#working-with-results)
|
||||
|
||||
## Reasoning
|
||||
@@ -63,32 +64,39 @@ import (
|
||||
graphql "github.com/lukaszraczylo/go-simple-graphql"
|
||||
)
|
||||
|
||||
headers := map[string]interface{}{
|
||||
"x-hasura-user-id": 37,
|
||||
"x-hasura-user-uuid": "bde1962e-b42e-1212-ac10-d43fa27f44a5",
|
||||
}
|
||||
|
||||
variables := map[string]interface{}{
|
||||
"fileHash": "123deadc0w321",
|
||||
}
|
||||
|
||||
query := `query searchFileKnown($fileHash: String) {
|
||||
tbl_file_scans(where: {file_hash: {_eq: $fileHash}}) {
|
||||
racy
|
||||
violence
|
||||
virus
|
||||
func main() {
|
||||
headers := map[string]interface{}{
|
||||
"x-hasura-user-id": 37,
|
||||
"x-hasura-user-uuid": "bde1962e-b42e-1212-ac10-d43fa27f44a5",
|
||||
}
|
||||
}`
|
||||
|
||||
gql := graphql.NewConnection()
|
||||
result, err := gql.Query(query, variables, headers)
|
||||
if err != nil {
|
||||
fmt.Println("Query error", err)
|
||||
return
|
||||
variables := map[string]interface{}{
|
||||
"fileHash": "123deadc0w321",
|
||||
}
|
||||
|
||||
query := `query searchFileKnown($fileHash: String) {
|
||||
tbl_file_scans(where: {file_hash: {_eq: $fileHash}}) {
|
||||
racy
|
||||
violence
|
||||
virus
|
||||
}
|
||||
}`
|
||||
|
||||
gql := graphql.NewConnection()
|
||||
result, err := gql.Query(query, variables, headers)
|
||||
if err != nil {
|
||||
fmt.Println("Query error", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(result)
|
||||
}
|
||||
fmt.Println(result)
|
||||
```
|
||||
|
||||
### Tips
|
||||
|
||||
* Connection handler ( `gql := graphql.NewConnection()` ) should be created once and reused in the application especially if you run dozens of queries per second. It will allow you also to use cache and http2 to its full potential.
|
||||
|
||||
|
||||
**Result**
|
||||
|
||||
```json
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
module github.com/lukaszraczylo/go-simple-graphql
|
||||
|
||||
go 1.18
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/allegro/bigcache/v3 v3.1.0
|
||||
github.com/avast/retry-go v3.0.0+incompatible
|
||||
github.com/buger/jsonparser v1.1.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lukaszraczylo/pandati v0.0.17
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/lukaszraczylo/pandati v0.0.20
|
||||
github.com/rs/zerolog v1.29.0
|
||||
github.com/sethvargo/go-retry v0.2.4
|
||||
github.com/stretchr/testify v1.7.1
|
||||
golang.org/x/net v0.4.0
|
||||
golang.org/x/net v0.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
@@ -26,7 +26,7 @@ require (
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/wI2L/jsondiff v0.3.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -10,18 +10,21 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/lukaszraczylo/pandati v0.0.17 h1:p5317W4mXmyLPHvpebRvIGzwoQpt5jBWpkktcJIXE00=
|
||||
github.com/lukaszraczylo/pandati v0.0.17/go.mod h1:2/+hBv1EighvY4J1J1lo4IrC+OZ8GmQ5eDwJygLT0Do=
|
||||
github.com/lukaszraczylo/pandati v0.0.18 h1:6khtDY6QXkiyh4a3dzHtIzx/9UqUGfHjzKBCA0SIurI=
|
||||
github.com/lukaszraczylo/pandati v0.0.18/go.mod h1:Aqlc0KH+ssvrscYYbEKMv7n5cFwe+0MjsJbikNz1h60=
|
||||
github.com/lukaszraczylo/pandati v0.0.20 h1:LqF7CS3z9Zi04KwEY8wd/YUpfWKSwi0nc7pagKQLXHc=
|
||||
github.com/lukaszraczylo/pandati v0.0.20/go.mod h1:ZegK9ll9UebKV1uNMF73qdLb43G/O4CSOdPglu6NniQ=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -31,8 +34,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
|
||||
github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
|
||||
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/sethvargo/go-retry v0.2.4 h1:T+jHEQy/zKJf5s95UkguisicE0zuF9y7+/vgz08Ocec=
|
||||
github.com/sethvargo/go-retry v0.2.4/go.mod h1:1afjQuvh7s4gflMObvjLPaWgluLLyhA1wmVZ6KLpICw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -48,17 +51,17 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/wI2L/jsondiff v0.3.0 h1:iTzQ9u/d86GE9RsBzVHX88f2EA1vQUboHwLhSQFc1s4=
|
||||
github.com/wI2L/jsondiff v0.3.0/go.mod h1:y1IMzNNjlSsk3IUoJdRJO7VRBtzMvRgyo4Vu0LdHpTc=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bigcache "github.com/allegro/bigcache/v3"
|
||||
@@ -32,15 +33,15 @@ var (
|
||||
)
|
||||
|
||||
type GraphQL struct {
|
||||
Endpoint string
|
||||
BackoffSetup retry.Backoff
|
||||
HttpClient *http.Client
|
||||
Log *logging.LogConfig
|
||||
Cache bool // Enable caching for read queries
|
||||
CacheStore *bigcache.BigCache
|
||||
RetriesEnable bool
|
||||
Endpoint string
|
||||
RetriesNumber int
|
||||
RetriesDelay time.Duration
|
||||
BackoffSetup retry.Backoff
|
||||
Cache bool
|
||||
RetriesEnable bool
|
||||
}
|
||||
|
||||
func pickGraphqlEndpoint() (graphqlEndpoint string) {
|
||||
@@ -81,19 +82,30 @@ func setCacheEnabled() bool {
|
||||
}
|
||||
|
||||
func NewConnection() *GraphQL {
|
||||
|
||||
endpoint := pickGraphqlEndpoint()
|
||||
var httpClient *http.Client
|
||||
if strings.HasPrefix(endpoint, "http://") {
|
||||
// HTTP/1.1 client
|
||||
httpClient = &http.Client{}
|
||||
} else {
|
||||
// HTTP/2 or HTTPS client
|
||||
http2Transport := &http2.Transport{
|
||||
AllowHTTP: true,
|
||||
}
|
||||
if strings.HasPrefix(endpoint, "https://") {
|
||||
http2Transport.TLSClientConfig = &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
}
|
||||
httpClient = &http.Client{
|
||||
Transport: http2Transport,
|
||||
}
|
||||
}
|
||||
|
||||
g := GraphQL{
|
||||
Endpoint: pickGraphqlEndpoint(),
|
||||
HttpClient: &http.Client{
|
||||
Transport: &http2.Transport{
|
||||
ReadIdleTimeout: 30 * time.Second,
|
||||
DisableCompression: false,
|
||||
AllowHTTP: true,
|
||||
PingTimeout: 5 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Endpoint: endpoint,
|
||||
HttpClient: httpClient,
|
||||
Log: logging.NewLogger(),
|
||||
Cache: setCacheEnabled(),
|
||||
CacheStore: setupCache(),
|
||||
|
||||
+1
-1
@@ -35,9 +35,9 @@ func (suite *TestSuite) TestNewConnection() {
|
||||
endpoint string
|
||||
}
|
||||
tests := []struct {
|
||||
want *GraphQL
|
||||
name string
|
||||
args args
|
||||
want *GraphQL
|
||||
}{
|
||||
{
|
||||
name: "New connection: Env variable endpoint",
|
||||
|
||||
@@ -82,9 +82,9 @@ func (suite *LoggingTestSuite) TestLogConfig_AllHandlers() {
|
||||
wantLevel string
|
||||
wantMessage string
|
||||
envMinLogLevel string
|
||||
loggerType string
|
||||
stdOutExpect bool
|
||||
stdErrExpect bool
|
||||
loggerType string
|
||||
}{
|
||||
{
|
||||
name: "Test log: Error",
|
||||
@@ -232,21 +232,21 @@ func (suite *LoggingTestSuite) TestLogConfig_AllHandlers() {
|
||||
|
||||
func (suite *LoggingTestSuite) TestFullMessage() {
|
||||
type args struct {
|
||||
message string
|
||||
extraFields map[string]interface{}
|
||||
message string
|
||||
}
|
||||
extraFields := make(map[string]interface{})
|
||||
extraFields["_full_message"] = "full message"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
name string
|
||||
wantLevel string
|
||||
wantMessage string
|
||||
envMinLogLevel string
|
||||
loggerType string
|
||||
stdOutExpect bool
|
||||
stdErrExpect bool
|
||||
loggerType string
|
||||
}{
|
||||
{
|
||||
name: "Test log: Error",
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
type requestBase struct {
|
||||
Query string `json:"query"`
|
||||
Variables interface{} `json:"variables"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type queryResults struct {
|
||||
@@ -83,6 +83,7 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
|
||||
|
||||
err = retry.Do(
|
||||
func() error {
|
||||
g.Log.Debug("Sending the query")
|
||||
httpResponse, err = g.HttpClient.Do(httpRequest)
|
||||
|
||||
if err != nil {
|
||||
|
||||
+6
-6
@@ -11,8 +11,8 @@ import (
|
||||
func (suite *TestSuite) Test_GraphQL_queryBuilder() {
|
||||
|
||||
type args struct {
|
||||
queryContent string
|
||||
queryVariables interface{}
|
||||
queryContent string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -26,7 +26,7 @@ func (suite *TestSuite) Test_GraphQL_queryBuilder() {
|
||||
queryContent: `query listUserBots { tbl_bots { bot_name } }`,
|
||||
queryVariables: nil,
|
||||
},
|
||||
want: []byte(`{"query":"query listUserBots { tbl_bots { bot_name } }","variables":null}`),
|
||||
want: []byte(`{"variables":null,"query":"query listUserBots { tbl_bots { bot_name } }"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
@@ -35,7 +35,7 @@ func (suite *TestSuite) Test_GraphQL_queryBuilder() {
|
||||
queryContent: `query listUserBots { tbl_bots { bot_name } }`,
|
||||
queryVariables: map[string]interface{}{"user_id": 1},
|
||||
},
|
||||
want: []byte(`{"query":"query listUserBots { tbl_bots { bot_name } }","variables":{"user_id":1}}`),
|
||||
want: []byte(`{"variables":{"user_id":1},"query":"query listUserBots { tbl_bots { bot_name } }"}`),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
@@ -58,17 +58,17 @@ func (suite *TestSuite) Test_GraphQL_Query() {
|
||||
g := NewConnection()
|
||||
|
||||
type args struct {
|
||||
queryContent string
|
||||
queryVariables interface{}
|
||||
queryHeaders map[string]interface{}
|
||||
queryContent string
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
name string
|
||||
endpoint string
|
||||
wantResult string
|
||||
isLocal bool
|
||||
cache_enabled bool
|
||||
args args
|
||||
wantResult string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user