2020-04-23 07:42:16 +02:00
|
|
|
package psql_test
|
2019-03-24 14:57:29 +01:00
|
|
|
|
|
|
|
import (
|
2019-06-02 01:48:42 +02:00
|
|
|
"bytes"
|
2020-02-19 05:52:44 +01:00
|
|
|
"encoding/json"
|
2019-03-24 14:57:29 +01:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2019-04-01 07:18:14 +02:00
|
|
|
func withComplexArgs(t *testing.T) {
|
2019-03-24 23:16:03 +01:00
|
|
|
gql := `query {
|
2019-06-15 04:17:21 +02:00
|
|
|
proDUcts(
|
2019-03-24 23:16:03 +01:00
|
|
|
# returns only 30 items
|
|
|
|
limit: 30,
|
2020-02-10 07:45:37 +01:00
|
|
|
|
2019-03-24 23:16:03 +01:00
|
|
|
# starts from item 10, commented out for now
|
|
|
|
# offset: 10,
|
2020-02-10 07:45:37 +01:00
|
|
|
|
2019-03-24 23:16:03 +01:00
|
|
|
# orders the response items by highest price
|
|
|
|
order_by: { price: desc },
|
2020-02-10 07:45:37 +01:00
|
|
|
|
2019-03-24 23:16:03 +01:00
|
|
|
# no duplicate prices returned
|
|
|
|
distinct: [ price ]
|
2020-02-10 07:45:37 +01:00
|
|
|
|
2019-10-03 09:08:01 +02:00
|
|
|
# only items with an id >= 20 and < 28 are returned
|
2019-03-24 23:16:03 +01:00
|
|
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) {
|
|
|
|
id
|
2019-06-15 04:17:21 +02:00
|
|
|
NAME
|
2019-03-24 23:16:03 +01:00
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 07:58:19 +01:00
|
|
|
}
|
|
|
|
|
2020-05-27 01:41:28 +02:00
|
|
|
func withWhereIn(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products(where: { id: { in: $list } }) {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
vars := map[string]json.RawMessage{
|
|
|
|
"list": json.RawMessage(`[1,2,3]`),
|
|
|
|
}
|
|
|
|
|
|
|
|
compileGQLToPSQL(t, gql, vars, "user")
|
|
|
|
}
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
func withWhereAndList(t *testing.T) {
|
2019-03-27 07:58:19 +01:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 08:03:09 +02:00
|
|
|
where: {
|
2020-02-10 07:45:37 +01:00
|
|
|
and: [
|
|
|
|
{ not: { id: { is_null: true } } },
|
|
|
|
{ price: { gt: 10 } },
|
|
|
|
] } ) {
|
2019-03-27 07:58:19 +01:00
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 07:58:19 +01:00
|
|
|
}
|
|
|
|
|
2019-04-01 07:18:14 +02:00
|
|
|
func withWhereIsNull(t *testing.T) {
|
2019-03-27 07:58:19 +01:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 08:03:09 +02:00
|
|
|
where: {
|
|
|
|
and: {
|
|
|
|
not: { id: { is_null: true } },
|
|
|
|
price: { gt: 10 }
|
2019-03-27 07:58:19 +01:00
|
|
|
}}) {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 07:58:19 +01:00
|
|
|
}
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
func withWhereMultiOr(t *testing.T) {
|
2019-03-27 07:58:19 +01:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 08:03:09 +02:00
|
|
|
where: {
|
2020-02-10 07:45:37 +01:00
|
|
|
or: {
|
|
|
|
not: { id: { is_null: true } },
|
|
|
|
price: { gt: 10 },
|
|
|
|
price: { lt: 20 }
|
|
|
|
} }
|
|
|
|
) {
|
2019-03-27 07:58:19 +01:00
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
|
2019-06-01 08:03:09 +02:00
|
|
|
func fetchByID(t *testing.T) {
|
2019-03-24 23:16:03 +01:00
|
|
|
gql := `query {
|
2020-02-19 05:52:44 +01:00
|
|
|
product(id: $id) {
|
2019-06-01 08:03:09 +02:00
|
|
|
id
|
|
|
|
name
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
|
2019-06-01 08:03:09 +02:00
|
|
|
func searchQuery(t *testing.T) {
|
2019-03-24 23:16:03 +01:00
|
|
|
gql := `query {
|
2020-02-19 05:52:44 +01:00
|
|
|
products(search: $query) {
|
2019-06-01 08:03:09 +02:00
|
|
|
id
|
2019-03-24 23:16:03 +01:00
|
|
|
name
|
2019-12-02 16:52:22 +01:00
|
|
|
search_rank
|
|
|
|
search_headline_description
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
|
2019-06-01 08:03:09 +02:00
|
|
|
func oneToMany(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
users {
|
|
|
|
email
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
price
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
|
2019-12-09 07:48:18 +01:00
|
|
|
func oneToManyReverse(t *testing.T) {
|
2019-03-24 23:16:03 +01:00
|
|
|
gql := `query {
|
2019-06-01 08:03:09 +02:00
|
|
|
products {
|
|
|
|
name
|
|
|
|
price
|
|
|
|
users {
|
|
|
|
email
|
2019-03-24 23:16:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
2019-03-25 04:42:54 +01:00
|
|
|
|
2019-12-09 07:48:18 +01:00
|
|
|
func oneToManyArray(t *testing.T) {
|
2020-01-16 07:44:19 +01:00
|
|
|
gql := `
|
|
|
|
query {
|
|
|
|
product {
|
|
|
|
name
|
|
|
|
price
|
|
|
|
tags {
|
|
|
|
id
|
2019-12-09 07:48:18 +01:00
|
|
|
name
|
|
|
|
}
|
2020-01-16 07:44:19 +01:00
|
|
|
}
|
|
|
|
tags {
|
|
|
|
name
|
|
|
|
product {
|
2019-12-09 07:48:18 +01:00
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2019-12-09 07:48:18 +01:00
|
|
|
}
|
|
|
|
|
2019-06-01 08:03:09 +02:00
|
|
|
func manyToMany(t *testing.T) {
|
2019-04-04 06:53:24 +02:00
|
|
|
gql := `query {
|
2019-06-01 08:03:09 +02:00
|
|
|
products {
|
2019-04-04 06:53:24 +02:00
|
|
|
name
|
2019-06-01 08:03:09 +02:00
|
|
|
customers {
|
|
|
|
email
|
|
|
|
full_name
|
|
|
|
}
|
2019-04-04 06:53:24 +02:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-04 06:53:24 +02:00
|
|
|
}
|
|
|
|
|
2019-06-01 08:03:09 +02:00
|
|
|
func manyToManyReverse(t *testing.T) {
|
2019-04-05 07:44:30 +02:00
|
|
|
gql := `query {
|
2019-06-01 08:03:09 +02:00
|
|
|
customers {
|
|
|
|
email
|
|
|
|
full_name
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
}
|
2019-04-05 07:44:30 +02:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-05 07:44:30 +02:00
|
|
|
}
|
|
|
|
|
2019-04-01 07:18:14 +02:00
|
|
|
func aggFunction(t *testing.T) {
|
2019-03-31 17:18:33 +02:00
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-10-14 08:51:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func aggFunctionBlockedByCol(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "anon")
|
2019-10-14 08:51:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func aggFunctionDisabled(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "anon1")
|
2019-03-31 17:18:33 +02:00
|
|
|
}
|
|
|
|
|
2019-04-01 07:18:14 +02:00
|
|
|
func aggFunctionWithFilter(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products(where: { id: { gt: 10 } }) {
|
|
|
|
id
|
|
|
|
max_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-09 03:24:29 +02:00
|
|
|
}
|
|
|
|
|
2019-11-05 05:44:42 +01:00
|
|
|
func syntheticTables(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
me {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-11-05 05:44:42 +01:00
|
|
|
}
|
|
|
|
|
2019-04-20 06:35:57 +02:00
|
|
|
func queryWithVariables(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
product(id: $PRODUCT_ID, where: { price: { eq: $PRODUCT_PRICE } }) {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-20 06:35:57 +02:00
|
|
|
}
|
|
|
|
|
2019-11-05 05:44:42 +01:00
|
|
|
func withWhereOnRelations(t *testing.T) {
|
2019-04-09 03:24:29 +02:00
|
|
|
gql := `query {
|
2019-11-05 05:44:42 +01:00
|
|
|
users(where: {
|
|
|
|
not: {
|
|
|
|
products: {
|
2020-01-13 15:34:15 +01:00
|
|
|
price: { gt: 3 }
|
2019-11-05 05:44:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
id
|
2019-04-09 03:24:29 +02:00
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-11-19 06:47:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func multiRoot(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
product {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
customer {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
customers {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}
|
|
|
|
user {
|
|
|
|
id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
customer {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-01 07:18:14 +02:00
|
|
|
}
|
|
|
|
|
2020-02-19 05:52:44 +01:00
|
|
|
func withCursor(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
Products(
|
|
|
|
first: 20
|
|
|
|
after: $cursor
|
|
|
|
order_by: { price: desc }) {
|
|
|
|
Name
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
|
|
|
vars := map[string]json.RawMessage{
|
|
|
|
"cursor": json.RawMessage(`"0,1"`),
|
|
|
|
}
|
|
|
|
|
|
|
|
compileGQLToPSQL(t, gql, vars, "admin")
|
|
|
|
}
|
|
|
|
|
2020-01-28 06:26:53 +01:00
|
|
|
func jsonColumnAsTable(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
tag_count {
|
|
|
|
count
|
|
|
|
tags {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2020-01-28 06:26:53 +01:00
|
|
|
}
|
|
|
|
|
2020-03-14 06:35:42 +01:00
|
|
|
func nullForAuthRequiredInAnon(t *testing.T) {
|
2020-01-21 05:38:17 +01:00
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
user(where: { id: { eq: $user_id } }) {
|
|
|
|
id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "anon")
|
2020-01-21 05:38:17 +01:00
|
|
|
}
|
|
|
|
|
2019-11-05 05:44:42 +01:00
|
|
|
func blockedQuery(t *testing.T) {
|
|
|
|
gql := `query {
|
2020-02-19 05:52:44 +01:00
|
|
|
user(id: $id, where: { id: { gt: 3 } }) {
|
2019-11-05 05:44:42 +01:00
|
|
|
id
|
|
|
|
full_name
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "bad_dude")
|
2019-11-05 05:44:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func blockedFunctions(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
users {
|
|
|
|
count_id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 07:45:37 +01:00
|
|
|
compileGQLToPSQL(t, gql, nil, "bad_dude")
|
2019-11-05 05:44:42 +01:00
|
|
|
}
|
|
|
|
|
2019-10-14 08:51:36 +02:00
|
|
|
func TestCompileQuery(t *testing.T) {
|
2019-04-01 07:18:14 +02:00
|
|
|
t.Run("withComplexArgs", withComplexArgs)
|
2020-05-27 01:41:28 +02:00
|
|
|
t.Run("withWhereIn", withWhereIn)
|
2019-04-01 07:18:14 +02:00
|
|
|
t.Run("withWhereAndList", withWhereAndList)
|
|
|
|
t.Run("withWhereIsNull", withWhereIsNull)
|
|
|
|
t.Run("withWhereMultiOr", withWhereMultiOr)
|
2019-04-04 06:53:24 +02:00
|
|
|
t.Run("fetchByID", fetchByID)
|
2019-04-05 07:44:30 +02:00
|
|
|
t.Run("searchQuery", searchQuery)
|
2019-04-01 07:18:14 +02:00
|
|
|
t.Run("oneToMany", oneToMany)
|
2019-12-09 07:48:18 +01:00
|
|
|
t.Run("oneToManyReverse", oneToManyReverse)
|
|
|
|
t.Run("oneToManyArray", oneToManyArray)
|
2019-04-01 07:18:14 +02:00
|
|
|
t.Run("manyToMany", manyToMany)
|
|
|
|
t.Run("manyToManyReverse", manyToManyReverse)
|
|
|
|
t.Run("aggFunction", aggFunction)
|
2019-10-14 08:51:36 +02:00
|
|
|
t.Run("aggFunctionBlockedByCol", aggFunctionBlockedByCol)
|
|
|
|
t.Run("aggFunctionDisabled", aggFunctionDisabled)
|
2019-04-01 07:18:14 +02:00
|
|
|
t.Run("aggFunctionWithFilter", aggFunctionWithFilter)
|
2019-04-09 03:24:29 +02:00
|
|
|
t.Run("syntheticTables", syntheticTables)
|
2019-10-03 09:08:01 +02:00
|
|
|
t.Run("queryWithVariables", queryWithVariables)
|
2019-11-05 05:44:42 +01:00
|
|
|
t.Run("withWhereOnRelations", withWhereOnRelations)
|
2019-11-19 06:47:55 +01:00
|
|
|
t.Run("multiRoot", multiRoot)
|
2020-01-28 06:26:53 +01:00
|
|
|
t.Run("jsonColumnAsTable", jsonColumnAsTable)
|
2020-02-19 05:52:44 +01:00
|
|
|
t.Run("withCursor", withCursor)
|
2020-03-14 06:35:42 +01:00
|
|
|
t.Run("nullForAuthRequiredInAnon", nullForAuthRequiredInAnon)
|
2019-10-26 07:34:29 +02:00
|
|
|
t.Run("blockedQuery", blockedQuery)
|
2019-10-27 06:52:48 +01:00
|
|
|
t.Run("blockedFunctions", blockedFunctions)
|
2019-04-01 07:18:14 +02:00
|
|
|
}
|
|
|
|
|
2019-06-15 04:17:21 +02:00
|
|
|
var benchGQL = []byte(`query {
|
|
|
|
proDUcts(
|
2019-06-02 07:38:51 +02:00
|
|
|
# returns only 30 items
|
|
|
|
limit: 30,
|
|
|
|
|
|
|
|
# starts from item 10, commented out for now
|
|
|
|
# offset: 10,
|
|
|
|
|
|
|
|
# orders the response items by highest price
|
|
|
|
order_by: { price: desc },
|
|
|
|
|
|
|
|
# only items with an id >= 30 and < 30 are returned
|
|
|
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) {
|
|
|
|
id
|
2019-06-15 04:17:21 +02:00
|
|
|
NAME
|
2019-06-02 07:38:51 +02:00
|
|
|
price
|
|
|
|
user {
|
|
|
|
full_name
|
|
|
|
picture : avatar
|
2019-03-25 04:42:54 +01:00
|
|
|
}
|
2019-06-02 07:38:51 +02:00
|
|
|
}
|
2019-06-15 04:17:21 +02:00
|
|
|
}`)
|
2019-03-25 04:42:54 +01:00
|
|
|
|
2019-06-02 07:38:51 +02:00
|
|
|
func BenchmarkCompile(b *testing.B) {
|
|
|
|
w := &bytes.Buffer{}
|
2019-06-02 01:48:42 +02:00
|
|
|
|
2019-03-25 04:42:54 +01:00
|
|
|
b.ResetTimer()
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
for n := 0; n < b.N; n++ {
|
2019-06-02 07:38:51 +02:00
|
|
|
w.Reset()
|
|
|
|
|
2019-10-14 08:51:36 +02:00
|
|
|
qc, err := qcompile.Compile(benchGQL, "user")
|
2019-06-02 01:48:42 +02:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-05-27 01:41:28 +02:00
|
|
|
_, err = pcompile.Compile(w, qc, nil)
|
2019-03-25 04:42:54 +01:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-02 07:38:51 +02:00
|
|
|
|
|
|
|
func BenchmarkCompileParallel(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
w := &bytes.Buffer{}
|
|
|
|
|
|
|
|
for pb.Next() {
|
|
|
|
w.Reset()
|
|
|
|
|
2019-10-14 08:51:36 +02:00
|
|
|
qc, err := qcompile.Compile(benchGQL, "user")
|
2019-06-02 07:38:51 +02:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2020-05-27 01:41:28 +02:00
|
|
|
_, err = pcompile.Compile(w, qc, nil)
|
2019-06-02 07:38:51 +02:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|