-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathclient.go
194 lines (167 loc) · 5.06 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package dagger
import (
"context"
"io"
"github.com/Khan/genqlient/graphql"
"github.com/vektah/gqlparser/v2/gqlerror"
"dagger.io/dagger/engineconn"
"dagger.io/dagger/querybuilder"
)
// Client is the Dagger Engine Client
type Client struct {
conn engineconn.EngineConn
query *querybuilder.Selection
client graphql.Client
}
// ClientOpt holds a client option
type ClientOpt interface {
setClientOpt(cfg *engineconn.Config)
}
type clientOptFunc func(cfg *engineconn.Config)
func (fn clientOptFunc) setClientOpt(cfg *engineconn.Config) {
fn(cfg)
}
// WithWorkdir sets the engine workdir
func WithWorkdir(path string) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.Workdir = path
})
}
// WithLogOutput sets the progress writer
func WithLogOutput(writer io.Writer) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.LogOutput = writer
})
}
// WithConn sets the engine connection explicitly
func WithConn(conn engineconn.EngineConn) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.Conn = conn
})
}
// WithVersionOverride requests a specific schema version from the engine.
// Calling this may cause the schema to be out-of-sync from the codegen - this
// option is likely *not* desirable for most use cases.
//
// This only has effect when connecting via the CLI, and is only exposed for
// testing purposes.
func WithVersionOverride(version string) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.VersionOverride = version
})
}
// WithVerbosity sets the verbosity level for the progress output
func WithVerbosity(level int) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.Verbosity = level
})
}
// WithRunnerHost sets the runner host URL for provisioning and connecting to
// an engine.
//
// This only has effect when connecting via the CLI, and is only exposed for
// testing purposes.
func WithRunnerHost(runnerHost string) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.RunnerHost = runnerHost
})
}
func WithServeCurrentModule(serveCurrentModule bool) ClientOpt {
return clientOptFunc(func(cfg *engineconn.Config) {
cfg.ServeCurrentModule = serveCurrentModule
})
}
// Connect to a Dagger Engine
func Connect(ctx context.Context, opts ...ClientOpt) (*Client, error) {
cfg := &engineconn.Config{}
for _, o := range opts {
o.setClientOpt(cfg)
}
conn, err := engineconn.Get(ctx, cfg)
if err != nil {
return nil, err
}
gql := errorWrappedClient{graphql.NewClient("http://"+conn.Host()+"/query", conn)}
c := &Client{
query: querybuilder.Query().Client(gql),
client: gql,
conn: conn,
}
return c, nil
}
// GraphQLClient returns the underlying graphql.Client
func (c *Client) GraphQLClient() graphql.Client {
return c.client
}
func (c *Client) QueryBuilder() *querybuilder.Selection {
return c.query
}
// Close the engine connection
func (c *Client) Close() error {
if c.conn != nil {
return c.conn.Close()
}
return nil
}
// Do sends a GraphQL request to the engine
func (c *Client) Do(ctx context.Context, req *Request, resp *Response) error {
r := graphql.Response{}
if resp != nil {
r.Data = resp.Data
r.Errors = resp.Errors
r.Extensions = resp.Extensions
}
return c.client.MakeRequest(ctx, &graphql.Request{
Query: req.Query,
Variables: req.Variables,
OpName: req.OpName,
}, &r)
}
// Request contains all the values required to build queries executed by
// the graphql.Client.
//
// Typically, GraphQL APIs will accept a JSON payload of the form
//
// {"query": "query myQuery { ... }", "variables": {...}}`
//
// and Request marshals to this format. However, MakeRequest may
// marshal the data in some other way desired by the backend.
type Request struct {
// The literal string representing the GraphQL query, e.g.
// `query myQuery { myField }`.
Query string `json:"query"`
// A JSON-marshalable value containing the variables to be sent
// along with the query, or nil if there are none.
Variables interface{} `json:"variables,omitempty"`
// The GraphQL operation name. The server typically doesn't
// require this unless there are multiple queries in the
// document, but genqlient sets it unconditionally anyway.
OpName string `json:"operationName"`
}
// Response that contains data returned by the GraphQL API.
//
// Typically, GraphQL APIs will return a JSON payload of the form
//
// {"data": {...}, "errors": {...}}
//
// It may additionally contain a key named "extensions", that
// might hold GraphQL protocol extensions. Extensions and Errors
// are optional, depending on the values returned by the server.
type Response struct {
Data interface{} `json:"data"`
Extensions map[string]interface{} `json:"extensions,omitempty"`
Errors gqlerror.List `json:"errors,omitempty"`
}
type errorWrappedClient struct {
graphql.Client
}
func (c errorWrappedClient) MakeRequest(ctx context.Context, req *graphql.Request, resp *graphql.Response) error {
err := c.Client.MakeRequest(ctx, req, resp)
if err != nil {
if e := getCustomError(err); e != nil {
return e
}
return err
}
return nil
}