Skip to content
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

Mm/refactoring/comments2 #4

Merged
merged 9 commits into from
Oct 9, 2023
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
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ linters-settings:
- golang.org/x/tools
- gopkg.in/yaml.v2
- github.com/alexflint/go-arg
- github.com/gorilla/websocket
- github.com/google/uuid

forbidigo:
forbid:
Expand Down
3 changes: 2 additions & 1 deletion docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ When releasing a new version:

- The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details.
- For schemas with enum values that differ only in casing, it's now possible to disable smart-casing in genqlient.yaml; see the [documentation](genqlient.yaml) for `casing` for details.
- genqlient now supports subscriptions
- genqlient now supports subscriptions; the websocket protocol is by default `graphql-transport-ws` but can be set to another value.
See the [documentation](FAQ.md) for how to `subscribe to an API 'subscription' endpoint`.

### Bug fixes:
- The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file.
Expand Down
18 changes: 15 additions & 3 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,21 +108,26 @@ Once your webSocket client matches the interfaces, you can get your `graphql.Web
a loop for incoming messages and errors:

```go
graphqlClient := graphql.NewClientUsingWebSocket(
graphqlClient := graphql.NewClientUsingWebSocket(
"ws://localhost:8080/query",
&MyDialer{Dialer: dialer},
headers,
)

respChan, errChan, err := count(context.Background(), graphqlClient)
errChan, err := graphqlClient.StartWebSocket(ctx)
if err != nil {
return
}

dataChan, subscriptionID, err := count(ctx, graphqlClient)
if err != nil {
return
}

defer graphqlClient.CloseWebSocket()
for loop := true; loop; {
select {
case msg, more := <-respChan:
case msg, more := <-dataChan:
if !more {
loop = false
break
Expand All @@ -136,9 +141,16 @@ graphqlClient := graphql.NewClientUsingWebSocket(
}
case err = <-errChan:
return
case <-time.After(time.Minute):
err = wsClient.Unsubscribe(subscriptionID)
loop = false
}
}
```
To change the websocket protocol from its default value `graphql-transport-ws`, add the following header before calling `graphql.NewClientUsingWebSocket()`:
```go
headers.Add("Sec-WebSocket-Protocol", "graphql-ws")
```

### … use an API that requires authentication?

Expand Down
2 changes: 1 addition & 1 deletion generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ func (g *generator) addOperation(op *ast.OperationDefinition) error {
docComment = "// " + strings.ReplaceAll(commentLines, "\n", "\n// ")
}
if op.Operation == ast.Subscription {
docComment += "\n// To close the connection, use [graphql.WebSocketClient.CloseWebSocket()]"
docComment += "\n// To unsubscribe, use [graphql.WebSocketClient.Unsubscribe]"
}

// If the filename is a pseudo-filename filename.go:startline, just
Expand Down
54 changes: 22 additions & 32 deletions generate/operation.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func {{.Name}}(
{{.GraphQLName}} {{.GoType.Reference}},
{{end -}}
{{end -}}
) ({{if eq .Type "subscription"}}dataChan_ chan {{.Name}}WsResponse, errChan_ chan error,{{else}}data_ *{{.ResponseName}}, {{if .Config.Extensions -}}ext_ map[string]interface{},{{end}}{{end}} err_ error) {
) ({{if eq .Type "subscription"}}dataChan_ chan {{.Name}}WsResponse, subscriptionID_ string,{{else}}data_ *{{.ResponseName}}, {{if .Config.Extensions -}}ext_ map[string]interface{},{{end}}{{end}} err_ error) {
req_ := &graphql.Request{
OpName: "{{.Name}}",
Query: {{.Name}}_Operation,
Expand All @@ -36,14 +36,8 @@ func {{.Name}}(
}
{{end}}
{{if eq .Type "subscription"}}
dataChan_ = make(chan {{.Name}}WsResponse, 1)
respChan_ := make(chan json.RawMessage, 1)

errChan_, err_ = client_.DialWebSocket({{if ne .Config.ContextType "-" -}}ctx_{{else}}context.Background(){{end}}, req_, respChan_)
if err_ != nil {
return nil, nil, err_
}
go {{.Name}}ForwardData(dataChan_, respChan_, errChan_)
dataChan_ = make(chan {{.Name}}WsResponse)
subscriptionID_, err_ = client_.Subscribe(req_, dataChan_, {{.Name}}ForwardData)
{{else}}
data_ = &{{.ResponseName}}{}
resp_ := &graphql.Response{Data: data_}
Expand All @@ -55,7 +49,7 @@ func {{.Name}}(
)
{{end}}

return {{if eq .Type "subscription"}}dataChan_, errChan_,{{else}}data_, {{if .Config.Extensions -}}resp_.Extensions,{{end -}}{{end}} err_
return {{if eq .Type "subscription"}}dataChan_, subscriptionID_,{{else}}data_, {{if .Config.Extensions -}}resp_.Extensions,{{end -}}{{end}} err_
}

{{if eq .Type "subscription"}}
Expand All @@ -65,30 +59,26 @@ type {{.Name}}WsResponse struct {
Errors error `json:"errors"`
}

func {{.Name}}ForwardData(dataChan_ chan {{.Name}}WsResponse, respChan_ chan json.RawMessage, errChan_ chan error) {
defer close(dataChan_)
func {{.Name}}ForwardData(interfaceChan interface{}, jsonRawMsg json.RawMessage) error {
var gqlResp graphql.Response
var wsResp {{.Name}}WsResponse
for {
jsonRaw, more_ := <-respChan_
if !more_ {
return
}
err := json.Unmarshal(jsonRaw, &gqlResp)
if err != nil {
errChan_ <- err
return
}
if len(gqlResp.Errors) == 0 {
err = json.Unmarshal(jsonRaw, &wsResp)
if err != nil {
errChan_ <- err
return
}
} else {
wsResp.Errors = gqlResp.Errors
}
dataChan_ <- wsResp
err := json.Unmarshal(jsonRawMsg, &gqlResp)
if err != nil {
return err
}
if len(gqlResp.Errors) == 0 {
err = json.Unmarshal(jsonRawMsg, &wsResp)
if err != nil {
return err
}
} else {
wsResp.Errors = gqlResp.Errors
}
dataChan_, ok := interfaceChan.(chan {{.Name}}WsResponse)
if !ok {
return errors.New("failed to cast interface into 'chan {{.Name}}WsResponse'")
}
dataChan_ <- wsResp
return nil
}
{{end}}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/99designs/gqlgen v0.17.35
github.com/alexflint/go-arg v1.4.2
github.com/bradleyjkemp/cupaloy/v2 v2.6.0
github.com/google/uuid v1.3.1
github.com/gorilla/websocket v1.5.0
github.com/stretchr/testify v1.8.2
github.com/vektah/gqlparser/v2 v2.5.8
Expand Down
2 changes: 2 additions & 0 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading