Yamashou / gqlgenc

This is Go library for building GraphQL client with gqlgen
MIT License
370 stars 66 forks source link

Subscription Support #32

Open maaft opened 3 years ago

maaft commented 3 years ago

Hi!

What would be the required steps to add subscription support to this client?

maaft commented 3 years ago

@Yamashou I got it working by using a slightly modified websocket client from hasura: https://gist.github.com/maaft/ac197c72fe5d18eec5916f3f5a75943f

How to use with generated structs (generated.SubscribeProjectsQuery, generated.SubscribeProjects):

func main() {
    subscriptionClient := subscription.NewSubscriptionClient("ws://localhost:8080/graphql")

    defer subscriptionClient.Close()

    errChan := make(chan error)

    subscriptionID, err := subscriptionClient.Subscribe(generated.SubscribeProjectsQuery, nil, func(dataValue *json.RawMessage, errValue error) error {
        if errValue != nil {
            errChan <- errValue
        }

        data := generated.SubscribeProjects{}
        if err := graphqljson.UnmarshalData(*dataValue, &data); err != nil {
            errChan <- err
        }

        for _, p := range data.QueryProject {
            fmt.Println(p.Name)
        }

        return nil
    })

    if err != nil {
        panic(err)
    }

    go func() {
        err := <-errChan
        if err != nil {
            subscriptionClient.Unsubscribe(subscriptionID)
            panic(err)
        }
    }()

    err = subscriptionClient.Run()

    if err != nil {
        panic(err)
    }
}

Do you think you could integrated this properly into this project? I think it should be relatively easy.

Yamashou commented 3 years ago

Thanks for issue and a script. Show me your implementation and think about it

Yamashou commented 3 years ago

[Q] nhooyr.io/websocket is that you guess best ws client ?

If you compared other clients, tell me result

Yamashou commented 3 years ago

If you have time, I want you to create PR for this issues. my start point is reading your gist. So, It takes longer to implement this client than you do

maaft commented 3 years ago

Hi! I also just copy-pasted the client I found and it works fine so far. The only issue is 100% CPU after some time, but that should be fixable. I see if I can spend some time on this on the upcoming year.

maaft commented 3 years ago

Just to document it for later: The >100% CPU issue is related to this function here:

func (wh *websocketHandler) ReadJSON(v interface{}) error {
    ctx, cancel := context.WithTimeout(wh.ctx, wh.timeout)
    defer cancel()
    return wsjson.Read(ctx, wh.Conn, v)
}

The timeout is set to 1 minute, so when for 1 minute no messages arrive, the load starts. When I use it like this:

func (wh *websocketHandler) ReadJSON(v interface{}) error {
    return wsjson.Read(wh.ctx, wh.Conn, v)
}

the issue is gone. I'm not sure about the implications though. But for now it works for me.

Yamashou commented 3 years ago

Thanks for comment and response ! I try to create new client package for cache and interceptor now. If I finished it when you create PR for subscription support, I start this issue.

delaneyj commented 3 years ago

@Yamashou I got it working by using a slightly modified websocket client from hasura: https://gist.github.com/maaft/ac197c72fe5d18eec5916f3f5a75943f

How to use with generated structs (generated.SubscribeProjectsQuery, generated.SubscribeProjects):

func main() {
  subscriptionClient := subscription.NewSubscriptionClient("ws://localhost:8080/graphql")

  defer subscriptionClient.Close()

  errChan := make(chan error)

  subscriptionID, err := subscriptionClient.Subscribe(generated.SubscribeProjectsQuery, nil, func(dataValue *json.RawMessage, errValue error) error {
      if errValue != nil {
          errChan <- errValue
      }

      data := generated.SubscribeProjects{}
      if err := graphqljson.UnmarshalData(*dataValue, &data); err != nil {
          errChan <- err
      }

      for _, p := range data.QueryProject {
          fmt.Println(p.Name)
      }

      return nil
  })

  if err != nil {
      panic(err)
  }

  go func() {
      err := <-errChan
      if err != nil {
          subscriptionClient.Unsubscribe(subscriptionID)
          panic(err)
      }
  }()

  err = subscriptionClient.Run()

  if err != nil {
      panic(err)
  }
}

Do you think you could integrated this properly into this project? I think it should be relatively easy.

If I try to run gqlgenc on gql files that contains a subscription I get

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x40 pc=0x8a86a0]

goroutine 1 [running]:
github.com/Yamashou/gqlgenc/clientgen.(*SourceGenerator).NewResponseField(0xc000375d98, 0xa51d80, 0xc00067ca80, 0xc0002ca6b8)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/source_generator.go:147 +0x480
github.com/Yamashou/gqlgenc/clientgen.(*SourceGenerator).NewResponseFields(0xc000375d98, 0xc0007abe00, 0x1, 0x1, 0x30, 0x7fd726ebcd28, 0x30)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/source_generator.go:83 +0xc5
github.com/Yamashou/gqlgenc/clientgen.(*SourceGenerator).NewResponseField(0xc000375d98, 0xa51d80, 0xc00067ca00, 0xc0002ca6b0)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/source_generator.go:142 +0xa5
github.com/Yamashou/gqlgenc/clientgen.(*SourceGenerator).NewResponseFields(0xc000375d98, 0xc0007abe10, 0x1, 0x1, 0x0, 0xd2cec0, 0xc000375b48)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/source_generator.go:83 +0xc5
github.com/Yamashou/gqlgenc/clientgen.(*Source).OperationResponses(0xc000375d78, 0xd2cec0, 0x0, 0x0, 0x0, 0x0)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/source.go:144 +0x125
github.com/Yamashou/gqlgenc/clientgen.(*Plugin).MutateConfig(0xc0000aab80, 0xc00018cf20, 0xc0000aab80, 0x7fd6fc6bbed8)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/clientgen/client.go:69 +0x67c
github.com/Yamashou/gqlgenc/generator.Generate(0xa582d0, 0xc0000260d0, 0xc0000de6c0, 0xc00018bf60, 0x1, 0x1, 0xc000084710, 0x4426ca)
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/generator/generater.go:32 +0x2e2
main.main()
        /home/delaney/.gvm/pkgsets/go1.16.2/global/pkg/mod/github.com/!yamashou/gqlgenc@v0.0.0-20210330020310-5eab2091f840/main.go:30 +0x1cc

I'd like to try this way of supporting subscriptions but no luck on the initial type generation. What am I doing wrong?

raphaelfff commented 3 years ago

Facing the same issue, we ended up creating our own GQL client: https://github.com/infiotinc/gqlgenc

maaft commented 3 years ago

Thank you, looks good!

I'd advice you to honor the work of @Yamashou in your repository though as you obviously based your work on his. Currently you are violating the MIT License of this repo.

raphaelfff commented 3 years ago

Thank you :) The relevant directories that are direct copies (or at least started this way) from this repo contain the LICENSE file from Yamashou, and some files that have been modified contain a link to their origin. But i agree that adding an Acknowledgement section in the readme would only be fair!