Compare commits

...
5 Commits
Author SHA1 Message Date
lukaszraczylo 40652c3fdd Update dependencies. 2022-04-29 12:27:31 +01:00
lukaszraczylo f360a5cf7e Update dependencies. 2022-02-09 08:54:39 +00:00
lukaszraczylo 2b09da7685 Fix tests to run locally. 2021-12-24 11:11:54 +00:00
lukaszraczylo aac6ca113d Update README. 2021-12-11 01:09:10 +00:00
lukaszraczylo c8ed2290b4 Add endpoint status code to log output.
Ensure library works with publicly available endpoints.
2021-12-11 00:03:25 +00:00
8 changed files with 84 additions and 35 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
*.out
*.test
coverage.txt
coverage.txt
*.DS_Store
+8 -1
View File
@@ -22,4 +22,11 @@ all: test-packages test
.PHONY: update
update: ## update dependencies
@go get -u -v ./...
@go get -u -v ./...
sonar:
sonar-scanner \
-Dsonar.projectKey=golang-simple-graphql \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=555ba78b315faa07b3231a3be0f3cbb24ab33814
+12 -12
View File
@@ -14,8 +14,8 @@ Ps. It's Hasura friendly.
## Reasoning
I've tried to run few graphQL clients with hasura, all of them required conversion of the data into
the appropriate structures, causing issues with non-existing types ( thanks to Hasura ), for example `bigint` which was difficult to export.
I've tried to run a few GraphQL clients with Hasura, all of them required conversion of the data into
the appropriate structures, causing issues with non-existing types ( thanks to Hasura ), for example, `bigint` which was difficult to export.
Therefore, I present you the simple client to which you can copy & paste your graphQL query, variables and you are good to go.
## Features
@@ -40,28 +40,28 @@ Therefore, I present you the simple client to which you can copy & paste your gr
```go
import (
fmt
gql "github.com/lukaszraczylo/go-simple-graphql"
graphql "github.com/lukaszraczylo/go-simple-graphql"
)
headers := map[string]interface{}{
"x-hasura-user-id": 37,
"x-hasura-user-uuid": "bde3262e-b42e-4151-ac10-d43f0bef44a5",
"x-hasura-user-uuid": "bde1962e-b42e-1212-ac10-d43fa27f44a5",
}
variables := map[string]interface{}{
"fileHash": "123deadc0w321",
"fileHash": "123deadc0w321",
}
var query = `query searchFileKnown($fileHash: String) {
query := `query searchFileKnown($fileHash: String) {
tbl_file_scans(where: {file_hash: {_eq: $fileHash}}) {
porn
racy
violence
virus
}
}
}`
graphql := NewConnection()
result, err := graphql.Query(query, variables, nil)
gql := graphql.NewConnection()
result, err := gql.Query(query, variables, headers)
if err != nil {
fmt.Println("Query error", err)
return
@@ -77,10 +77,10 @@ fmt.Println(result)
## Working with results
I'm using an amazing library [tidwall/gjson](https://github.com/tidwall/gjson) to parse the results and extract information required in further steps and I strongly recommend this approach as the easiest and close to painless.
I'm using an amazing library [tidwall/gjson](https://github.com/tidwall/gjson) to parse the results and extract the information required in further steps and I strongly recommend this approach as the easiest and close to painless, for example:
```go
result = gjson.Get(result, "tbl_user_group_admins.0.is_admin").Bool()
result := gjson.Get(result, "tbl_user_group_admins.0.is_admin").Bool()
if result {
fmt.Println("User is an admin")
}
+4 -4
View File
@@ -3,13 +3,13 @@ module github.com/lukaszraczylo/go-simple-graphql
go 1.17
require (
github.com/allegro/bigcache/v3 v3.0.1
github.com/allegro/bigcache/v3 v3.0.2
github.com/buger/jsonparser v1.1.1
github.com/json-iterator/go v1.1.12
github.com/lukaszraczylo/pandati v0.0.10
github.com/rs/zerolog v1.26.0
github.com/lukaszraczylo/pandati v0.0.12
github.com/rs/zerolog v1.26.1
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
)
require (
+18 -4
View File
@@ -1,5 +1,7 @@
github.com/allegro/bigcache/v3 v3.0.1 h1:Q4Xl3chywXuJNOw7NV+MeySd3zGQDj4KCpkCg0te8mc=
github.com/allegro/bigcache/v3 v3.0.1/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI=
github.com/allegro/bigcache/v3 v3.0.2/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -19,6 +21,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lukaszraczylo/pandati v0.0.10 h1:ZXmLjxb/UYRBVyAKwXmUcJgZ2FYNwGY4YDJsQQMYAXI=
github.com/lukaszraczylo/pandati v0.0.10/go.mod h1:1lE/x4X4LfO1yerAQoiR7AVb/wIabH/zITDGnqTdyH8=
github.com/lukaszraczylo/pandati v0.0.12 h1:WPQM36t7tjaIe4eUtEpw5HZ8bm/KtHrVQA/T74Em1zU=
github.com/lukaszraczylo/pandati v0.0.12/go.mod h1:1lE/x4X4LfO1yerAQoiR7AVb/wIabH/zITDGnqTdyH8=
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=
@@ -28,8 +32,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.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
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/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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
@@ -37,21 +41,31 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c h1:WtYZ93XtWSO5KlOMgPZu7hXY9WhMZpprvlm5VwvAl8c=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+1
View File
@@ -80,6 +80,7 @@ func NewConnection() *GraphQL {
Endpoint: pickGraphqlEndpoint(),
HttpClient: &http.Client{
Transport: &http2.Transport{
ReadIdleTimeout: 30 * time.Second,
DisableCompression: false,
AllowHTTP: true,
PingTimeout: 10 * time.Second,
+4 -4
View File
@@ -63,7 +63,7 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
}
httpRequest, err := http.NewRequest("POST", g.Endpoint, bytes.NewBuffer(query))
httpRequest.Header.Add("Content-Type", "application/json")
//httpRequest.Header.Add("Content-Type", "application/json")
for header, value := range queryHeaders {
httpRequest.Header.Add(fmt.Sprintf("%v", header), fmt.Sprintf("%v", value))
}
@@ -89,18 +89,18 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
}
if !pandati.IsZero(queryResult.Errors) {
g.Log.Error("Query returned error", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_error": fmt.Sprintf("%v", 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})
return "", fmt.Errorf("%v", queryResult.Errors[0].Message)
}
if pandati.IsZero(queryResult.Data) {
g.Log.Error("Query returned no data", map[string]interface{}{"_query": queryContent, "_variables": queryVariables})
g.Log.Error("Query returned no data", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_response_code": httpResponse.StatusCode})
return "", errors.New("Query returned no data")
}
responseContent, err = json.MarshalToString(queryResult.Data)
if err != nil {
g.Log.Error("Invalid data result", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_result": responseContent})
g.Log.Error("Invalid data result", map[string]interface{}{"_query": queryContent, "_variables": queryVariables, "_result": responseContent, "_response_code": httpResponse.StatusCode})
return "", err
}
+35 -9
View File
@@ -2,6 +2,7 @@ package gql
import (
"fmt"
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -59,6 +60,7 @@ func (suite *TestSuite) Test_GraphQL_Query() {
type args struct {
queryContent string
queryVariables interface{}
queryHeaders map[string]interface{}
}
tests := []struct {
name string
@@ -71,7 +73,7 @@ func (suite *TestSuite) Test_GraphQL_Query() {
}{
{
name: "Valid query, no cache",
endpoint: "http://hasura.local/v1/graphql",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: false,
args: args{
@@ -82,12 +84,12 @@ func (suite *TestSuite) Test_GraphQL_Query() {
}`,
queryVariables: nil,
},
wantResult: `{"tbl_bots":[{"bot_name":"littleMentionBot"}]}`,
wantResult: `{"tbl_bots":[{"bot_name":"ThetaGuardianBot"}]}`,
wantErr: false,
},
{
name: "Valid query, with cache empty",
endpoint: "http://hasura.local/v1/graphql",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: true,
args: args{
@@ -98,12 +100,12 @@ func (suite *TestSuite) Test_GraphQL_Query() {
}`,
queryVariables: nil,
},
wantResult: `{"tbl_bots":[{"bot_name":"littleMentionBot"}]}`,
wantResult: `{"tbl_bots":[{"bot_name":"ThetaGuardianBot"}]}`,
wantErr: false,
},
{
name: "Valid query, with cache filled",
endpoint: "http://hasura.local/v1/graphql",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: true,
args: args{
@@ -114,12 +116,12 @@ func (suite *TestSuite) Test_GraphQL_Query() {
}`,
queryVariables: nil,
},
wantResult: `{"tbl_bots":[{"bot_name":"littleMentionBot"}]}`,
wantResult: `{"tbl_bots":[{"bot_name":"ThetaGuardianBot"}]}`,
wantErr: false,
},
{
name: "Invalid query",
endpoint: "http://hasura.local/v1/graphql",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
args: args{
queryContent: `query listUserBots {
@@ -151,6 +153,30 @@ func (suite *TestSuite) Test_GraphQL_Query() {
wantResult: `{"available_packages":[{"package_discount":0,"package_name":"media_1000","package_price":750,"package_size":1000,"package_type":"media"},{"package_discount":5,"package_name":"media_5000","package_price":3563,"package_size":5000,"package_type":"media"},{"package_discount":10,"package_name":"media_10000","package_price":6750,"package_size":10000,"package_type":"media"},{"package_discount":15,"package_name":"media_25000","package_price":15938,"package_size":25000,"package_type":"media"},{"package_discount":0,"package_name":"voice_500","package_price":1000,"package_size":500,"package_type":"voice"},{"package_discount":10,"package_name":"voice_1000","package_price":1800,"package_size":1000,"package_type":"voice"},{"package_discount":15,"package_name":"voice_2500","package_price":4250,"package_size":2500,"package_type":"voice"}]}`,
wantErr: false,
},
{
name: "Valid query to github endpoint",
endpoint: "https://api.github.com/graphql",
isLocal: false,
args: args{
queryContent: `query {
repository(name: "semver-generator", owner: "lukaszraczylo", followRenames: true) {
releases(last: 2) {
nodes {
tag {
name
}
}
}
}
}`,
queryVariables: nil,
queryHeaders: map[string]interface{}{
"Authorization": "Bearer " + os.Getenv("GITHUB_TOKEN"),
},
},
wantResult: `{"repository":{"releases":{"nodes":[`,
wantErr: false,
},
}
for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) {
@@ -159,11 +185,11 @@ func (suite *TestSuite) Test_GraphQL_Query() {
// }
g.Endpoint = tt.endpoint
g.Cache = tt.cache_enabled
gotResult, gotErr := g.Query(tt.args.queryContent, tt.args.queryVariables, nil)
gotResult, gotErr := g.Query(tt.args.queryContent, tt.args.queryVariables, tt.args.queryHeaders)
if tt.wantErr {
assert.Error(t, gotErr)
}
assert.Equal(t, tt.wantResult, gotResult)
assert.Contains(t, gotResult, tt.wantResult)
})
}
fmt.Println(g.CacheStore.Capacity())