github.com/replicatedhq/ship@v0.55.0/pkg/e2e/vendor_client.go (about) 1 package e2e 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/url" 10 11 "github.com/go-kit/kit/log" 12 "github.com/go-kit/kit/log/level" 13 multierror "github.com/hashicorp/go-multierror" 14 "github.com/pkg/errors" 15 ) 16 17 // stolen from devops and other places 18 type GraphQLClient struct { 19 GQLServer *url.URL 20 Token string 21 Logger log.Logger 22 } 23 24 // GraphQLRequest is a json-serializable request to the graphql server 25 type GraphQLRequest struct { 26 Query string `json:"query"` 27 Variables map[string]string `json:"variables"` 28 OperationName string `json:"operationName"` 29 } 30 31 // GraphQLError represents an error returned by the graphql server 32 type GraphQLError struct { 33 Locations []map[string]interface{} `json:"locations"` 34 Message string `json:"message"` 35 Code string `json:"code"` 36 } 37 38 // GraphQLResponsePromoteRelease is the top-level response object from the graphql server 39 type GraphQLResponsePromoteRelease struct { 40 Data *ShipReleaseResult `json:"data,omitempty"` 41 Errors []GraphQLError `json:"errors,omitempty"` 42 } 43 44 type ShipReleaseResult struct { 45 PromoteResult map[string]interface{} `json:"promoteRelease"` 46 } 47 48 // GraphQLResponsePromoteRelease is the top-level response object from the graphql server 49 type GraphQLResponseCreateChannel struct { 50 Data *ShipCreateChannelResult `json:"data,omitempty"` 51 Errors []GraphQLError `json:"errors,omitempty"` 52 } 53 54 type ShipCustomer struct { 55 InstallationID string `json:"installationId"` 56 ID string `json:"id"` 57 } 58 59 type ShipChannel struct { 60 Name string `json:"name"` 61 ID string `json:"id"` 62 Customers []ShipCustomer `json:"customers"` 63 } 64 65 type ShipCreateChannelResult struct { 66 CreateChannel ShipChannel `json:"createChannel"` 67 } 68 69 type ShipAssignCustomerResult struct { 70 AssignCustomerToChannel ShipChannel `json:"assignCustomerToChannel"` 71 } 72 73 type ShipChannelListResult struct { 74 SearchChannels []ShipChannel `json:"searchChannels"` 75 } 76 77 type GraphQLResponseListChannel struct { 78 Data *ShipChannelListResult `json:"data,omitempty"` 79 Errors []GraphQLError `json:"errors,omitempty"` 80 } 81 82 type GraphQLResponseAssignCustomer struct { 83 Data *ShipAssignCustomerResult `json:"data,omitempty"` 84 Errors []GraphQLError `json:"errors,omitempty"` 85 } 86 87 func (r GraphQLResponseListChannel) GraphQLError() []GraphQLError { 88 return r.Errors 89 } 90 func (r GraphQLResponseCreateChannel) GraphQLError() []GraphQLError { 91 return r.Errors 92 } 93 func (r GraphQLResponsePromoteRelease) GraphQLError() []GraphQLError { 94 return r.Errors 95 } 96 97 func (r GraphQLResponseAssignCustomer) GraphQLError() []GraphQLError { 98 return r.Errors 99 } 100 101 type Errer interface { 102 GraphQLError() []GraphQLError 103 } 104 105 func (c *GraphQLClient) GetOrCreateChannel(name string) (*ShipChannel, error) { 106 requestObj := GraphQLRequest{ 107 Query: ` 108 query($channelName: String!) { 109 searchChannels(channelName: $channelName) { 110 id name 111 } 112 }`, 113 Variables: map[string]string{"channelName": name}, 114 } 115 response := GraphQLResponseListChannel{} 116 err := c.executeRequest(requestObj, &response) 117 if err != nil { 118 return nil, errors.Wrapf(err, "execute request") 119 } 120 121 if err := c.checkErrors(response); err != nil { 122 return nil, err 123 } 124 125 if response.Data != nil && len(response.Data.SearchChannels) != 0 { 126 return &response.Data.SearchChannels[0], nil 127 } 128 129 channel, err := c.CreateChannel(name) 130 return channel, errors.Wrap(err, "create channel") 131 } 132 133 func (c *GraphQLClient) PromoteRelease( 134 spec, 135 channelID, 136 semver, 137 releaseNotes string, 138 ) (*ShipReleaseResult, error) { 139 140 requestObj := GraphQLRequest{ 141 Query: ` 142 mutation($channelId: ID!, $semver: String!, $spec: String!, $releaseNotes: String) { 143 promoteRelease( 144 channelId: $channelId 145 semver: $semver 146 spec: $spec 147 releaseNotes: $releaseNotes 148 ) { 149 id } 150 }`, 151 Variables: map[string]string{ 152 "spec": spec, 153 "channelId": channelID, 154 "semver": semver, 155 "releaseNotes": releaseNotes, 156 }, 157 } 158 response := GraphQLResponsePromoteRelease{} 159 err := c.executeRequest(requestObj, &response) 160 if err != nil { 161 return nil, errors.Wrapf(err, "execute request") 162 } 163 if err := c.checkErrors(response); err != nil { 164 return nil, err 165 } 166 167 return response.Data, nil 168 } 169 170 func (c *GraphQLClient) CreateChannel(name string) (*ShipChannel, error) { 171 requestObj := GraphQLRequest{ 172 Query: ` 173 mutation($channelName: String!) { 174 createChannel(channelName: $channelName) { 175 id 176 name 177 } 178 }`, 179 Variables: map[string]string{ 180 "channelName": name, 181 }, 182 } 183 response := GraphQLResponseCreateChannel{} 184 err := c.executeRequest(requestObj, &response) 185 if err != nil { 186 return nil, errors.Wrapf(err, "execute request") 187 } 188 if err := c.checkErrors(response); err != nil { 189 return nil, err 190 } 191 192 return &response.Data.CreateChannel, nil 193 } 194 195 func (c *GraphQLClient) EnsureCustomerOnChannel(customerId string, channelId string) (string, error) { 196 requestObj := GraphQLRequest{ 197 Query: ` 198 mutation($customerId: ID!, $channelId: ID!) { 199 assignCustomerToChannel(customerId: $customerId, channelId: $channelId) { 200 id 201 name 202 customers { 203 id 204 installationId 205 } 206 } 207 }`, 208 Variables: map[string]string{ 209 "customerId": customerId, 210 "channelId": channelId, 211 }, 212 } 213 response := GraphQLResponseAssignCustomer{} 214 err := c.executeRequest(requestObj, &response) 215 if err != nil { 216 return "", errors.Wrapf(err, "execute request") 217 } 218 if err := c.checkErrors(response); err != nil { 219 return "", err 220 } 221 222 for _, customer := range response.Data.AssignCustomerToChannel.Customers { 223 if customer.ID == customerId { 224 return customer.InstallationID, nil 225 } 226 227 } 228 return "", errors.Errorf("no matching customers returned when assigning customer %s to channel %s", customerId, channelId) 229 230 } 231 232 func (c *GraphQLClient) executeRequest( 233 requestObj GraphQLRequest, 234 deserializeTarget interface{}, 235 ) error { 236 debug := log.With(level.Debug(c.Logger), "type", "graphQLClient", "semver") 237 body, err := json.Marshal(requestObj) 238 if err != nil { 239 return errors.Wrap(err, "marshal body") 240 } 241 bodyReader := bytes.NewReader(body) 242 req, err := http.NewRequest("POST", c.GQLServer.String(), bodyReader) 243 if err != nil { 244 return errors.Wrap(err, "marshal body") 245 } 246 req.Header.Set("Content-Type", "application/json") 247 req.Header.Set("Authorization", c.Token) 248 client := http.DefaultClient 249 resp, err := client.Do(req) 250 if err != nil { 251 return errors.Wrap(err, "marshal body") 252 } 253 if resp == nil { 254 return errors.New("nil response from gql") 255 } 256 if resp.Body == nil { 257 return errors.New("nil response.Body from gql") 258 } 259 responseBody, err := ioutil.ReadAll(resp.Body) 260 debug.Log("body", responseBody) 261 if err != nil { 262 return errors.Wrap(err, "marshal body") 263 } 264 if err := json.Unmarshal(responseBody, deserializeTarget); err != nil { 265 return errors.Wrap(err, "unmarshal response") 266 } 267 268 return nil 269 } 270 271 func (c *GraphQLClient) checkErrors(errer Errer) error { 272 if errer.GraphQLError() != nil && len(errer.GraphQLError()) > 0 { 273 var multiErr *multierror.Error 274 for _, err := range errer.GraphQLError() { 275 multiErr = multierror.Append(multiErr, fmt.Errorf("%s: %s", err.Code, err.Message)) 276 } 277 if multiErr == nil { 278 return fmt.Errorf("expected %d gql errors but none found", len(errer.GraphQLError())) 279 } 280 return multiErr.ErrorOrNil() 281 } 282 return nil 283 }