Compare commits

...
10 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
lukaszraczylo d1406eeac8 Improve http2 / SSL support 2021-12-10 22:33:47 +00:00
lukaszraczylo ea7ec683ef Check for possible cache eviction. 2021-12-03 15:12:02 +00:00
lukaszraczylo 5cd22318f1 Add environment variables to steer the query cache. 2021-12-03 09:12:59 +00:00
lukaszraczylo 13955da995 Add query ^query cache. 2021-12-03 02:11:37 +00:00
lukaszraczylo cd5d75feff Remove legacy timeout field. 2021-12-02 11:35:03 +00:00
9 changed files with 318 additions and 131 deletions
+2 -1
View File
@@ -1,3 +1,4 @@
*.out
*.test
coverage.txt
coverage.txt
*.DS_Store
+13 -2
View File
@@ -11,11 +11,22 @@ help: ## display this help
.PHONY: test
test: ## run tests on library
@go test $(ADDITIONAL_BUILD_FLAGS) -v -cover ./...
@LOG_LEVEL=debug go test $(ADDITIONAL_BUILD_FLAGS) -v -cover ./...
.PHONY: test-packages
test-packages: ## run tests on packages
@go test -v -cover ./pkg/...
.PHONY: all
all: test-packages test
all: test-packages test
.PHONY: update
update: ## update dependencies
@go get -u -v ./...
sonar:
sonar-scanner \
-Dsonar.projectKey=golang-simple-graphql \
-Dsonar.sources=. \
-Dsonar.host.url=http://localhost:9000 \
-Dsonar.login=555ba78b315faa07b3231a3be0f3cbb24ab33814
+15 -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
@@ -23,12 +23,15 @@ Therefore, I present you the simple client to which you can copy & paste your gr
* Executing GraphQL queries as they are, without types declaration
* HTTP2 support!
* Support for additional headers
* Query cache
## Usage example
### Environment variables
* `GRAPHQL_ENDPOINT` - Your GraphQL endpoint. Default: `http://127.0.0.1:9090/v1/graphql`
* `GRAPHQL_CACHE` - Should the query cache be enabled? Default: `false`
* `GRAPHQL_CACHE_TTL` - Cache TTL in seconds for SELECT type of queries. Default: `5`
* `LOG_LEVEL` - Logging level. Default: `info`
### Example reader code
@@ -37,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/simple-gql-client"
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
@@ -74,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")
}
+8 -5
View File
@@ -3,20 +3,23 @@ module github.com/lukaszraczylo/go-simple-graphql
go 1.17
require (
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.25.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-20210929193557-e81a3d93ecf6
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/kr/pretty v0.1.0 // 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
golang.org/x/text v0.3.6 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
+34 -12
View File
@@ -1,3 +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=
@@ -10,52 +14,70 @@ github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
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=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II=
github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
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=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 h1:Z04ewVs7JhXaYkmDhBERPi41gnltfQpMWDnTnQbaCqk=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-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-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
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=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+47 -6
View File
@@ -15,11 +15,12 @@ package gql
import (
"crypto/tls"
"fmt"
"net"
"net/http"
"os"
"strconv"
"time"
"github.com/allegro/bigcache/v3"
jsoniter "github.com/json-iterator/go"
"github.com/lukaszraczylo/go-simple-graphql/pkg/logging"
"golang.org/x/net/http2"
@@ -33,6 +34,8 @@ type GraphQL struct {
Endpoint string
HttpClient *http.Client
Log *logging.LogConfig
Cache bool // Enable caching for read queries
CacheStore *bigcache.BigCache
}
func pickGraphqlEndpoint() (graphqlEndpoint string) {
@@ -46,19 +49,57 @@ func pickGraphqlEndpoint() (graphqlEndpoint string) {
return graphqlEndpoint
}
func setCacheTTL() int {
value, present := os.LookupEnv("GRAPHQL_CACHE_TTL")
if present && value != "" {
i, err := strconv.Atoi(value)
if err != nil {
panic("Invalid value for query cache ttl")
}
return int(i)
} else {
return 5
}
}
func setCacheEnabled() bool {
value, present := os.LookupEnv("GRAPHQL_CACHE")
if present && value != "" {
b, err := strconv.ParseBool(value)
if err != nil {
panic("Invalid value for query cache")
}
return b
} else {
return false
}
}
func NewConnection() *GraphQL {
return &GraphQL{
g := GraphQL{
Endpoint: pickGraphqlEndpoint(),
HttpClient: &http.Client{
Timeout: time.Second * 30,
Transport: &http2.Transport{
ReadIdleTimeout: 30 * time.Second,
DisableCompression: false,
AllowHTTP: true,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
PingTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
},
Log: logging.NewLogger(),
Log: logging.NewLogger(),
Cache: setCacheEnabled(),
CacheStore: setupCache(),
}
return &g
}
func setupCache() *bigcache.BigCache {
cache, err := bigcache.NewBigCache(bigcache.DefaultConfig(time.Duration(setCacheTTL()) * time.Second))
if err != nil {
panic("Error creating cache: " + err.Error())
}
return cache
}
+34 -11
View File
@@ -2,6 +2,7 @@ package gql
import (
"bytes"
"crypto/md5"
"errors"
"fmt"
"io"
@@ -29,16 +30,11 @@ func (g *GraphQL) queryBuilder(queryContent string, queryVariables interface{})
Variables: queryVariables,
}
// j := new(bytes.Buffer)
j2, err := json.Marshal(qb)
if err != nil {
g.Log.Critical("Unable to marshal the query", map[string]interface{}{"_error": err.Error(), "_query": queryContent, "_variables": queryVariables})
return []byte{}, err
}
// if err = json.Compact(j, j2); err != nil {
// g.Log.Critical("Unable to compact the query", map[string]interface{}{"error": err.Error()})
// return []byte{}, err
// }
return j2, err
}
@@ -49,8 +45,25 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
g.Log.Error("Unable to build the query", map[string]interface{}{"_error": err.Error()})
return "", err
}
var body []byte
var queryResult *queryResults
queryHash := fmt.Sprintf("%x", md5.Sum(query))
if g.Cache {
g.Log.Debug("Checking the cache for the query", map[string]interface{}{"_query": queryHash})
if entry, entryInfo, err := g.CacheStore.GetWithInfo(queryHash); err == nil {
g.Log.Debug("Found the query in the cache", map[string]interface{}{"_query": queryHash})
if pandati.IsZero(entryInfo.EntryStatus) {
return string(entry), nil
}
} else {
g.Log.Debug("Unable to find the query in the cache", map[string]interface{}{"_query": queryHash, "_error": err.Error()})
}
}
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))
}
@@ -63,12 +76,12 @@ func (g *GraphQL) Query(queryContent string, queryVariables interface{}, queryHe
defer io.Copy(ioutil.Discard, httpResponse.Body)
defer httpResponse.Body.Close()
body, err := ioutil.ReadAll(httpResponse.Body)
body, err = ioutil.ReadAll(httpResponse.Body)
if err != nil {
g.Log.Critical("Unable to read the response", map[string]interface{}{"_error": err.Error()})
return "", err
}
var queryResult *queryResults
err = json.Unmarshal(body, &queryResult)
if err != nil {
g.Log.Error("Unable to unmarshal the query", map[string]interface{}{"_error": err.Error()})
@@ -76,20 +89,30 @@ 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
}
if g.Cache {
g.Log.Debug("Caching the query", map[string]interface{}{"_query": queryHash})
if queryContent[0:5] == "query" {
err = g.CacheStore.Set(queryHash, []byte(responseContent))
if err != nil {
g.Log.Error("Unable to cache the query", map[string]interface{}{"_query": queryHash, "_error": err.Error()})
}
}
}
return
}
+65 -65
View File
@@ -1,69 +1,69 @@
package gql
import (
"testing"
)
// import (
// "testing"
// )
func Benchmark_GraphQL_Query(t *testing.B) {
if testing.Short() {
t.Skip("Skipping test in short / CI mode")
}
type args struct {
queryContent string
queryVariables interface{}
}
tests := []struct {
name string
endpoint string
isLocal bool
args args
wantResult string
mockedReply string
wantErr bool
}{
{
name: "Valid query",
endpoint: "http://hasura.local/v1/graphql",
isLocal: false,
args: args{
queryContent: `query listUserBots {
tbl_bots(limit: 1) {
bot_name
}
}`,
queryVariables: nil,
},
wantResult: `{"tbl_bots":[{"bot_name":"littleMentionBot"}]}`,
mockedReply: `{"data":{"tbl_bots":[{"bot_name":"littleMentionBot"}]}}`,
wantErr: false,
},
{
name: "Invalid query",
endpoint: "http://hasura.local/v1/graphql",
isLocal: false,
args: args{
queryContent: `query listUserBots {
tbl_botz(limit: 1) {
bot_name
}
}`,
queryVariables: nil,
},
mockedReply: `{"errors":[{"message":"Unknown field \"tbl_botz\" on type \"Query\""}]}`,
wantResult: ``,
wantErr: true,
},
}
// func Benchmark_GraphQL_Query(t *testing.B) {
// if testing.Short() {
// t.Skip("Skipping test in short / CI mode")
// }
// type args struct {
// queryContent string
// queryVariables interface{}
// }
// tests := []struct {
// name string
// endpoint string
// isLocal bool
// args args
// wantResult string
// mockedReply string
// wantErr bool
// }{
// {
// name: "Valid query",
// endpoint: "http://hasura.local/v1/graphql",
// isLocal: false,
// args: args{
// queryContent: `query listUserBots {
// tbl_bots(limit: 1) {
// bot_name
// }
// }`,
// queryVariables: nil,
// },
// wantResult: `{"tbl_bots":[{"bot_name":"littleMentionBot"}]}`,
// mockedReply: `{"data":{"tbl_bots":[{"bot_name":"littleMentionBot"}]}}`,
// wantErr: false,
// },
// {
// name: "Invalid query",
// endpoint: "http://hasura.local/v1/graphql",
// isLocal: false,
// args: args{
// queryContent: `query listUserBots {
// tbl_botz(limit: 1) {
// bot_name
// }
// }`,
// queryVariables: nil,
// },
// mockedReply: `{"errors":[{"message":"Unknown field \"tbl_botz\" on type \"Query\""}]}`,
// wantResult: ``,
// wantErr: true,
// },
// }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.B) {
g := NewConnection()
server, serverURL := mockGraphQLServerResponses(tt.mockedReply)
g.Endpoint = serverURL
for n := 0; n < t.N; n++ {
g.Query(tt.args.queryContent, tt.args.queryVariables, nil)
}
server.Close()
})
}
}
// for _, tt := range tests {
// t.Run(tt.name, func(t *testing.B) {
// g := NewConnection()
// server, serverURL := mockGraphQLServerResponses(tt.mockedReply)
// g.Endpoint = serverURL
// for n := 0; n < t.N; n++ {
// g.Query(tt.args.queryContent, tt.args.queryVariables, nil)
// }
// server.Close()
// })
// }
// }
+100 -17
View File
@@ -1,6 +1,7 @@
package gql
import (
"fmt"
"os"
"testing"
@@ -54,22 +55,27 @@ func (suite *TestSuite) Test_GraphQL_Query() {
if testing.Short() {
suite.T().Skip("Skipping test in short / CI mode")
}
g := NewConnection()
type args struct {
queryContent string
queryVariables interface{}
queryHeaders map[string]interface{}
}
tests := []struct {
name string
endpoint string
isLocal bool
args args
wantResult string
wantErr bool
name string
endpoint string
isLocal bool
cache_enabled bool
args args
wantResult string
wantErr bool
}{
{
name: "Valid query",
endpoint: "http://hasura.local/v1/graphql",
isLocal: true,
name: "Valid query, no cache",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: false,
args: args{
queryContent: `query listUserBots {
tbl_bots(limit: 1) {
@@ -78,12 +84,44 @@ 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: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: true,
args: args{
queryContent: `query listUserBots {
tbl_bots(limit: 1) {
bot_name
}
}`,
queryVariables: nil,
},
wantResult: `{"tbl_bots":[{"bot_name":"ThetaGuardianBot"}]}`,
wantErr: false,
},
{
name: "Valid query, with cache filled",
endpoint: "https://hasura.local/v1/graphql",
isLocal: true,
cache_enabled: true,
args: args{
queryContent: `query listUserBots {
tbl_bots(limit: 1) {
bot_name
}
}`,
queryVariables: nil,
},
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 {
@@ -96,18 +134,63 @@ func (suite *TestSuite) Test_GraphQL_Query() {
wantResult: ``,
wantErr: true,
},
{
name: "Valid query to https endpoint",
endpoint: "https://web-dev.telegram-bot.app/v1/graphql",
isLocal: false,
args: args{
queryContent: `query packages_prices {
available_packages: tbl_available_packages(where: {enabled: {_eq: true}}) {
package_name
package_discount
package_price
package_size
package_type
}
}`,
queryVariables: nil,
},
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) {
if tt.isLocal {
os.Setenv("GRAPHQL_ENDPOINT", tt.endpoint)
}
g := NewConnection()
gotResult, gotErr := g.Query(tt.args.queryContent, tt.args.queryVariables, nil)
// if tt.isLocal {
// os.Setenv("GRAPHQL_ENDPOINT", tt.endpoint)
// }
g.Endpoint = tt.endpoint
g.Cache = tt.cache_enabled
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())
}