这是indexloc提供的服务,不要输入任何密码
Skip to content

feat: add more retry options #169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ jobs:
- name: Vet
run: go vet ./...
- name: Lint
uses: golangci/golangci-lint-action@v6
uses: golangci/golangci-lint-action@v7
with:
version: latest
only-new-issues: true
skip-cache: true
args: --timeout=120s
6 changes: 0 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ jobs:
test-go:
name: Run unit and integration tests
runs-on: ubuntu-latest
permissions:
pull-requests: write
# Required: allow read access to the content for analysis.
contents: read
# Optional: Allow write access to checks to allow the action to annotate code in the PR.
checks: write
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
109 changes: 109 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
version: "2"

linters:
default: all
disable:
- err113
- lll
- depguard
- wrapcheck
- varnamelen
- exhaustruct
- ireturn
- gochecknoglobals
- mnd
- recvcheck
- wsl
- makezero
- godox
- gocognit
- gocyclo
- funcorder
- forcetypeassert
- containedctx
- maintidx
settings:
exhaustive:
# Program elements to check for exhaustiveness.
check:
- switch
- map
# Presence of "default" case in switch statements satisfies exhaustiveness,
# even if all enum members are not listed.
default-signifies-exhaustive: true
cyclop:
max-complexity: 70
revive:
# Maximum number of open files at the same time.
max-open-files: 2048
severity: error
rules:
- name: var-naming
disabled: true
- name: exported
disabled: true
gosec:
excludes:
- G115
- G301
- G304
- G306
- G404
funlen:
lines: 210
statements: 120
nestif:
min-complexity: 10

# Defines a set of rules to ignore issues.
# It does not skip the analysis, and so does not ignore "typecheck" errors.
exclusions:
# Mode of the generated files analysis.
#
# - `strict`: sources are excluded by strictly following the Go generated file convention.
# Source files that have lines matching only the following regular expression will be excluded: `^// Code generated .* DO NOT EDIT\.$`
# This line must appear before the first non-comment, non-blank text in the file.
# https://go.dev/s/generatedcode
# - `lax`: sources are excluded if they contain lines like `autogenerated file`, `code generated`, `do not edit`, etc.
# - `disable`: disable the generated files exclusion.
#
# Default: lax
generated: strict
# Log a warning if an exclusion rule is unused.
warn-unused: false
rules: []
# - path: connector/api/
# linters:
# - godot
# Which file paths to exclude: they will be analyzed, but issues from them won't be reported.
# "/" will be replaced by the current OS file path separator to properly work on Windows.
paths:
- ".*_test\\.go$"
- example/

formatters:
# Enable specific formatter.
# Default: [] (uses standard Go formatting)
enable:
- gci
- gofmt
- gofumpt
- goimports
- golines
# Formatters settings.
settings: {}
exclusions:
# Mode of the generated files analysis.
#
# - `strict`: sources are excluded by strictly following the Go generated file convention.
# Source files that have lines matching only the following regular expression will be excluded: `^// Code generated .* DO NOT EDIT\.$`
# This line must appear before the first non-comment, non-blank text in the file.
# https://go.dev/s/generatedcode
# - `lax`: sources are excluded if they contain lines like `autogenerated file`, `code generated`, `do not edit`, etc.
# - `disable`: disable the generated files exclusion.
#
# Default: lax
generated: strict
# Which file paths to exclude.
# Default: []
paths: []
100 changes: 61 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,47 @@ The subscription client follows Apollo client specification https://github.com/a

Package `graphql` provides a GraphQL client implementation.

For more information, see package [`github.com/shurcooL/githubv4`](https://github.com/shurcooL/githubv4), which is a specialized version targeting GitHub GraphQL API v4. That package is driving the feature development.

**Note**: Before v0.8.0, `QueryRaw`, `MutateRaw`, and `Subscribe` methods return `*json.RawMessage`. This output type is redundant to be decoded. From v0.8.0, the output type is changed to `[]byte`.

- [go-graphql-client](#go-graphql-client)
- [Installation](#installation)
- [Usage](#usage)
- [Authentication](#authentication)
- [WithRequestModifier](#withrequestmodifier)
- [OAuth2](#oauth2)
- [Simple Query](#simple-query)
- [Arguments and Variables](#arguments-and-variables)
- [Custom scalar tag](#custom-scalar-tag)
- [Skip GraphQL field](#skip-graphql-field)
- [Inline Fragments](#inline-fragments)
- [Specify GraphQL type name](#specify-graphql-type-name)
- [Mutations](#mutations)
- [Mutations Without Fields](#mutations-without-fields)
- [Subscription](#subscription)
- [Usage](#usage-1)
- [Subscribe](#subscribe)
- [Stop the subscription](#stop-the-subscription)
- [Authentication](#authentication-1)
- [Options](#options)
- [Subscription Protocols](#subscription-protocols)
- [Handle connection error](#handle-connection-error)
- [Connection Initialisation Timeout](#connection-initialisation-timeout)
- [WebSocket Connection Idle Timeout](#websocket-connection-idle-timeout)
- [Events](#events)
- [Custom HTTP Client](#custom-http-client)
- [Custom WebSocket client](#custom-websocket-client)
- [Options](#options-1)
- [Execute pre-built query](#execute-pre-built-query)
- [Get extensions from response](#get-extensions-from-response)
- [Get headers from response](#get-headers-from-response)
- [With operation name (deprecated)](#with-operation-name-deprecated)
- [Raw bytes response](#raw-bytes-response)
- [Multiple mutations with ordered map](#multiple-mutations-with-ordered-map)
- [Debugging and Unit test](#debugging-and-unit-test)
- [Directories](#directories)
- [References](#references)
- [License](#license)
- [Installation](#installation)
- [Usage](#usage)
- [Authentication](#authentication)
- [WithRequestModifier](#withrequestmodifier)
- [OAuth2](#oauth2)
- [Simple Query](#simple-query)
- [Arguments and Variables](#arguments-and-variables)
- [Custom scalar tag](#custom-scalar-tag)
- [Skip GraphQL field](#skip-graphql-field)
- [Inline Fragments](#inline-fragments)
- [Specify GraphQL type name](#specify-graphql-type-name)
- [Mutations](#mutations)
- [Mutations Without Fields](#mutations-without-fields)
- [Retry Options](#retry-options)
- [Subscription](#subscription)
- [Usage](#usage-1)
- [Subscribe](#subscribe)
- [Stop the subscription](#stop-the-subscription)
- [Authentication](#authentication-1)
- [Options](#options)
- [Subscription Protocols](#subscription-protocols)
- [Handle connection error](#handle-connection-error)
- [Connection Initialisation Timeout](#connection-initialisation-timeout)
- [WebSocket Connection Idle Timeout](#websocket-connection-idle-timeout)
- [Events](#events)
- [Custom HTTP Client](#custom-http-client)
- [Custom WebSocket client](#custom-websocket-client)
- [Options](#options-1)
- [Execute pre-built query](#execute-pre-built-query)
- [Get extensions from response](#get-extensions-from-response)
- [Get headers from response](#get-headers-from-response)
- [With operation name (deprecated)](#with-operation-name-deprecated)
- [Raw bytes response](#raw-bytes-response)
- [Multiple mutations with ordered map](#multiple-mutations-with-ordered-map)
- [Debugging and Unit test](#debugging-and-unit-test)
- [Directories](#directories)
- [References](#references)
- [License](#license)

## Installation

Expand Down Expand Up @@ -490,6 +489,29 @@ fmt.Printf("Created a review: %s.\n", m.CreateReview)
// Created a review: .
```

### Retry Options

Construct the client with the following options:

```go
client := graphql.NewClient("/graphql", http.DefaultClient,
// number of retries
graphql.WithRetry(3),
// base backoff interval. Optional, default 1 second.
// Prioritize the Retry-After header if it exists in the response.
graphql.WithRetryBaseDelay(time.Second),
// exponential rate. Optional, default 2.0
graphql.WithRetryExponentialRate(2),
// retry on http statuses. Optional, default: 429, 502, 503, 504
graphql.WithRetryHTTPStatus([]int{http.StatusServiceUnavailable}),
// if the http status is 200 but the graphql response is error,
// use this option to check if the error is retryable.
graphql.WithRetryOnGraphQLError(func(errs graphql.Errors) bool {
return len(errs) == 1 && errs[0].Message == "Field 'user' is missing required arguments: login"
}),
)
```

### Subscription

#### Usage
Expand Down
8 changes: 5 additions & 3 deletions example/graphql-ws-bc/client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import (

func main() {
protocol := graphql.GraphQLWS
protocolArg := flag.String("protocol", "graphql-ws", "The protocol is used for the subscription")
protocolArg := flag.String(
"protocol",
"graphql-ws",
"The protocol is used for the subscription",
)
flag.Parse()

if protocolArg != nil {
Expand Down Expand Up @@ -64,7 +68,6 @@ func startSubscription(protocol graphql.SubscriptionProtocolType) error {
}

_, err := client.Subscribe(sub, nil, func(data []byte, err error) error {

if err != nil {
log.Println(err)
return nil
Expand All @@ -76,7 +79,6 @@ func startSubscription(protocol graphql.SubscriptionProtocolType) error {
log.Printf("hello: %+v", string(data))
return nil
})

if err != nil {
panic(err)
}
Expand Down
13 changes: 7 additions & 6 deletions example/hasura/client/graphql-ws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ func main() {
}

func startSubscription() error {

client := graphql.NewSubscriptionClient(serverEndpoint).
WithProtocol(graphql.GraphQLWS).
WithConnectionParams(map[string]interface{}{
Expand Down Expand Up @@ -59,7 +58,6 @@ func startSubscription() error {
}

_, err := client.Subscribe(sub, nil, func(data []byte, err error) error {

if err != nil {
log.Println(err)
return nil
Expand All @@ -71,7 +69,6 @@ func startSubscription() error {
log.Println(string(data))
return nil
})

if err != nil {
panic(err)
}
Expand All @@ -89,7 +86,6 @@ type user_insert_input map[string]interface{}

// insertUsers insert users to the graphql server, so the subscription client can receive messages
func insertUsers() {

client := graphql.NewClient(serverEndpoint, &http.Client{
Transport: headerRoundTripper{
setHeaders: func(req *http.Request) {
Expand Down Expand Up @@ -124,7 +120,12 @@ func insertUsers() {
},
},
}
err := client.Mutate(context.Background(), &q, variables, graphql.OperationName("InsertUser"))
err := client.Mutate(
context.Background(),
&q,
variables,
graphql.OperationName("InsertUser"),
)
if err != nil {
fmt.Println(err)
}
Expand All @@ -133,7 +134,7 @@ func insertUsers() {
}

func randomString() string {
var letter = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
letter := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

b := make([]rune, 16)
for i := range b {
Expand Down
13 changes: 7 additions & 6 deletions example/hasura/client/subscriptions-transport-ws/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ func main() {
}

func startSubscription() error {

client := graphql.NewSubscriptionClient(serverEndpoint).
WithConnectionParams(map[string]interface{}{
"headers": map[string]string{
Expand Down Expand Up @@ -55,7 +54,6 @@ func startSubscription() error {
}

subId, err := client.Subscribe(sub, nil, func(data []byte, err error) error {

if err != nil {
log.Println(err)
return nil
Expand All @@ -67,7 +65,6 @@ func startSubscription() error {
log.Println(string(data))
return nil
})

if err != nil {
panic(err)
}
Expand All @@ -85,7 +82,6 @@ type user_insert_input map[string]interface{}

// insertUsers insert users to the graphql server, so the subscription client can receive messages
func insertUsers() {

client := graphql.NewClient(serverEndpoint, &http.Client{
Transport: headerRoundTripper{
setHeaders: func(req *http.Request) {
Expand Down Expand Up @@ -120,7 +116,12 @@ func insertUsers() {
},
},
}
err := client.Mutate(context.Background(), &q, variables, graphql.OperationName("InsertUser"))
err := client.Mutate(
context.Background(),
&q,
variables,
graphql.OperationName("InsertUser"),
)
if err != nil {
fmt.Println(err)
}
Expand All @@ -129,7 +130,7 @@ func insertUsers() {
}

func randomString() string {
var letter = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
letter := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

b := make([]rune, 16)
for i := range b {
Expand Down
1 change: 0 additions & 1 deletion example/realworld/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ func main() {
}

func run() error {

url := "https://rickandmortyapi.com/graphql"

client := graphql.NewClient(url, nil)
Expand Down
Loading