Skip to content

Commit

Permalink
feat(web/sdk): feeds manager gql client (#1333)
Browse files Browse the repository at this point in the history
## Motivation

Integration tests with job orchestration require a Go client for
interacting with the GQL API.

## Solution

This PR generates a Go SDK using
[genqlient](https://github.com/Khan/genqlient) and exposes it via a
`Client` interface defined at `integration-tests/web/sdk`.
  • Loading branch information
MStreet3 authored Aug 20, 2024
1 parent 22f5d3c commit 5e33b2f
Show file tree
Hide file tree
Showing 12 changed files with 6,627 additions and 0 deletions.
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ zig 0.11.0
golangci-lint 1.59.1
protoc 25.1
python 3.10.5
task 3.35.1
5 changes: 5 additions & 0 deletions integration-tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ replace github.com/smartcontractkit/chainlink/v2 => ../
require (
dario.cat/mergo v1.0.0
github.com/AlekSi/pointer v1.1.0
github.com/Khan/genqlient v0.7.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/avast/retry-go/v4 v4.6.0
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
Expand Down Expand Up @@ -100,7 +101,10 @@ require (
github.com/NethermindEth/starknet.go v0.7.1-0.20240401080518-34a506f3cfdb // indirect
github.com/VictoriaMetrics/fastcache v1.12.1 // indirect
github.com/XSAM/otelsql v0.27.0 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
github.com/alexflint/go-arg v1.4.2 // indirect
github.com/alexflint/go-scalar v1.0.0 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/avast/retry-go v3.0.0+incompatible // indirect
Expand Down Expand Up @@ -427,6 +431,7 @@ require (
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect
github.com/valyala/fastjson v1.4.1 // indirect
github.com/vektah/gqlparser/v2 v2.5.11 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
Expand Down
18 changes: 18 additions & 0 deletions integration-tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ github.com/K-Phoen/grabana v0.22.1 h1:b/O+C3H2H6VNYSeMCYUO4X4wYuwFXgBcRkvYa+fjpQ
github.com/K-Phoen/grabana v0.22.1/go.mod h1:3LTXrTzQzTKTgvKSXdRjlsJbizSOW/V23Q3iX00R5bU=
github.com/K-Phoen/sdk v0.12.4 h1:j2EYuBJm3zDTD0fGKACVFWxAXtkR0q5QzfVqxmHSeGQ=
github.com/K-Phoen/sdk v0.12.4/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU=
github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w=
github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
Expand Down Expand Up @@ -158,6 +160,8 @@ github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd
github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A=
github.com/XSAM/otelsql v0.27.0 h1:i9xtxtdcqXV768a5C6SoT/RkG+ue3JTOgkYInzlTOqs=
github.com/XSAM/otelsql v0.27.0/go.mod h1:0mFB3TvLa7NCuhm/2nU7/b2wEtsczkj8Rey8ygO7V+A=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
Expand All @@ -166,6 +170,10 @@ github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrI
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0=
github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf2y2768kM=
github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70=
github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
Expand All @@ -175,7 +183,11 @@ github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
Expand Down Expand Up @@ -220,6 +232,8 @@ github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
Expand Down Expand Up @@ -411,6 +425,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WA
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E=
github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
Expand Down Expand Up @@ -1536,6 +1552,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE=
github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8=
github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc=
github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
Expand Down
13 changes: 13 additions & 0 deletions integration-tests/web/sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## GQL SDK

This package exports a `Client` for interacting with the `feeds-manager` service of core. The implementation is based on code generated via `[genqlient](https://github.com/Khan/genqlient)`.

### Extending the Client

Add additional queries or mutations to `genqlient.graphql` and then regenerate the implementation via the Taskfile:

```bash
$ task generate
```

Next, extend the `Client` interface and the `client` implementation.
156 changes: 156 additions & 0 deletions integration-tests/web/sdk/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package client

import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"

"github.com/Khan/genqlient/graphql"

"github.com/smartcontractkit/ccip/integration-tests/web/sdk/client/internal/doer"
"github.com/smartcontractkit/ccip/integration-tests/web/sdk/internal/generated"
)

type Client interface {
GetCSAKeys(ctx context.Context) (*generated.GetCSAKeysResponse, error)
GetJob(ctx context.Context, id string) (*generated.GetJobResponse, error)
ListJobs(ctx context.Context, offset, limit int) (*generated.ListJobsResponse, error)
GetBridge(ctx context.Context, id string) (*generated.GetBridgeResponse, error)
ListBridges(ctx context.Context, offset, limit int) (*generated.ListBridgesResponse, error)
GetFeedsManager(ctx context.Context, id string) (*generated.GetFeedsManagerResponse, error)
ListFeedsManagers(ctx context.Context) (*generated.ListFeedsManagersResponse, error)
CreateFeedsManager(ctx context.Context, cmd generated.CreateFeedsManagerInput) (*generated.CreateFeedsManagerResponse, error)
UpdateFeedsManager(ctx context.Context, id string, cmd generated.UpdateFeedsManagerInput) (*generated.UpdateFeedsManagerResponse, error)
GetJobProposal(ctx context.Context, id string) (*generated.GetJobProposalResponse, error)
ApproveJobProposalSpec(ctx context.Context, id string, force bool) (*generated.ApproveJobProposalSpecResponse, error)
CancelJobProposalSpec(ctx context.Context, id string) (*generated.CancelJobProposalSpecResponse, error)
RejectJobProposalSpec(ctx context.Context, id string) (*generated.RejectJobProposalSpecResponse, error)
UpdateJobProposalSpecDefinition(ctx context.Context, id string, cmd generated.UpdateJobProposalSpecDefinitionInput) (*generated.UpdateJobProposalSpecDefinitionResponse, error)
}

type client struct {
gqlClient graphql.Client
credentials Credentials
endpoints endpoints
cookie string
}

type endpoints struct {
Sessions string
Query string
}

type Credentials struct {
Email string `json:"email"`
Password string `json:"password"`
}

func New(baseURI string, creds Credentials) (Client, error) {
endpoints := endpoints{
Sessions: baseURI + "/sessions",
Query: baseURI + "/query",
}
c := &client{
endpoints: endpoints,
credentials: creds,
}

if err := c.login(); err != nil {
return nil, fmt.Errorf("failed to login to node: %w", err)
}

c.gqlClient = graphql.NewClient(
c.endpoints.Query,
doer.NewAuthed(c.cookie),
)

return c, nil
}

func (c *client) GetCSAKeys(ctx context.Context) (*generated.GetCSAKeysResponse, error) {
return generated.GetCSAKeys(ctx, c.gqlClient)
}

func (c *client) GetJob(ctx context.Context, id string) (*generated.GetJobResponse, error) {
return generated.GetJob(ctx, c.gqlClient, id)
}

func (c *client) ListJobs(ctx context.Context, offset, limit int) (*generated.ListJobsResponse, error) {
return generated.ListJobs(ctx, c.gqlClient, offset, limit)
}

func (c *client) GetBridge(ctx context.Context, id string) (*generated.GetBridgeResponse, error) {
return generated.GetBridge(ctx, c.gqlClient, id)
}

func (c *client) ListBridges(ctx context.Context, offset, limit int) (*generated.ListBridgesResponse, error) {
return generated.ListBridges(ctx, c.gqlClient, offset, limit)
}

func (c *client) GetFeedsManager(ctx context.Context, id string) (*generated.GetFeedsManagerResponse, error) {
return generated.GetFeedsManager(ctx, c.gqlClient, id)
}

func (c *client) ListFeedsManagers(ctx context.Context) (*generated.ListFeedsManagersResponse, error) {
return generated.ListFeedsManagers(ctx, c.gqlClient)
}

func (c *client) CreateFeedsManager(ctx context.Context, cmd generated.CreateFeedsManagerInput) (*generated.CreateFeedsManagerResponse, error) {
return generated.CreateFeedsManager(ctx, c.gqlClient, cmd)
}

func (c *client) UpdateFeedsManager(ctx context.Context, id string, cmd generated.UpdateFeedsManagerInput) (*generated.UpdateFeedsManagerResponse, error) {
return generated.UpdateFeedsManager(ctx, c.gqlClient, id, cmd)
}

func (c *client) GetJobProposal(ctx context.Context, id string) (*generated.GetJobProposalResponse, error) {
return generated.GetJobProposal(ctx, c.gqlClient, id)
}

func (c *client) ApproveJobProposalSpec(ctx context.Context, id string, force bool) (*generated.ApproveJobProposalSpecResponse, error) {
return generated.ApproveJobProposalSpec(ctx, c.gqlClient, id, force)
}

func (c *client) CancelJobProposalSpec(ctx context.Context, id string) (*generated.CancelJobProposalSpecResponse, error) {
return generated.CancelJobProposalSpec(ctx, c.gqlClient, id)
}

func (c *client) RejectJobProposalSpec(ctx context.Context, id string) (*generated.RejectJobProposalSpecResponse, error) {
return generated.RejectJobProposalSpec(ctx, c.gqlClient, id)
}

func (c *client) UpdateJobProposalSpecDefinition(ctx context.Context, id string, cmd generated.UpdateJobProposalSpecDefinitionInput) (*generated.UpdateJobProposalSpecDefinitionResponse, error) {
return generated.UpdateJobProposalSpecDefinition(ctx, c.gqlClient, id, cmd)
}

func (c *client) login() error {
b, err := json.Marshal(c.credentials)
if err != nil {
return fmt.Errorf("failed to marshal credentials: %w", err)
}

payload := strings.NewReader(string(b))

req, err := http.NewRequest("POST", c.endpoints.Sessions, payload)
if err != nil {
return err
}

req.Header.Add("Content-Type", "application/json")

res, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer res.Body.Close()

cookieHeader := res.Header.Get("Set-Cookie")
if cookieHeader == "" {
return fmt.Errorf("no cookie found in header")
}

c.cookie = strings.Split(cookieHeader, ";")[0]
return nil
}
20 changes: 20 additions & 0 deletions integration-tests/web/sdk/client/internal/doer/doer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package doer

import "net/http"

type authed struct {
cookie string
wrapped *http.Client
}

func NewAuthed(cookie string) *authed {
return &authed{
cookie: cookie,
wrapped: http.DefaultClient,
}
}

func (a *authed) Do(req *http.Request) (*http.Response, error) {
req.Header.Set("cookie", a.cookie)
return a.wrapped.Do(req)
}
15 changes: 15 additions & 0 deletions integration-tests/web/sdk/genqlient.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Default genqlient config; for full documentation see:
# https://github.com/Khan/genqlient/blob/main/docs/genqlient.yaml
schema: ./internal/schema.graphql

operations:
- ./internal/genqlient.graphql

generated: ./internal/generated/generated.go

bindings:
Time:
type: string

package_bindings:
- package: github.com/smartcontractkit/chainlink/v2/core/web/gqlscalar
Loading

0 comments on commit 5e33b2f

Please sign in to comment.