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  }