github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/notification/client.go (about)

     1  package notification
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  
    11  	kebError "github.com/kyma-project/kyma-environment-broker/internal/error"
    12  )
    13  
    14  const (
    15  	PathCreateEvent             string = "/createMaintenanceEvent"
    16  	PathUpdateEvent             string = "/updateMaintenanceEvent"
    17  	PathCancelEvent             string = "/cancelMaintenanceEvent"
    18  	KubernetesMaintenanceNumber string = "0"
    19  	KymaMaintenanceNumber       string = "1"
    20  	UnderMaintenanceEventState  string = "1"
    21  	FinishedMaintenanceState    string = "2"
    22  	CancelledMaintenanceState   string = "3"
    23  )
    24  
    25  type (
    26  	ClientConfig struct {
    27  		URL string
    28  	}
    29  
    30  	Client struct {
    31  		config     ClientConfig
    32  		httpClient *http.Client
    33  	}
    34  
    35  	Request struct {
    36  		Method  string
    37  		Path    string
    38  		Body    io.Reader
    39  		Headers map[string]string
    40  		Delete  bool
    41  	}
    42  
    43  	NotificationTenant struct {
    44  		InstanceID string `json:"instanceId"`
    45  		StartDate  string `json:"startDateTime,omitempty"`
    46  		EndDate    string `json:"endDateTime,omitempty"`
    47  		State      string `json:"eventState,omitempty"`
    48  	}
    49  
    50  	CreateEventRequest struct {
    51  		OrchestrationID string               `json:"orchestrationId"`
    52  		EventType       string               `json:"eventType"`
    53  		Tenants         []NotificationTenant `json:"tenants"`
    54  	}
    55  
    56  	UpdateEventRequest struct {
    57  		OrchestrationID string               `json:"orchestrationId"`
    58  		Tenants         []NotificationTenant `json:"tenants"`
    59  	}
    60  
    61  	CancelEventRequest struct {
    62  		OrchestrationID string `json:"orchestrationId"`
    63  	}
    64  )
    65  
    66  func NewClient(cli *http.Client, cfg ClientConfig) *Client {
    67  	return &Client{
    68  		config:     cfg,
    69  		httpClient: cli,
    70  	}
    71  }
    72  
    73  func (c *Client) CreateEvent(payload CreateEventRequest) error {
    74  	return c.callPost(PathCreateEvent, payload)
    75  }
    76  
    77  func (c *Client) UpdateEvent(payload UpdateEventRequest) error {
    78  	return c.callPatch(PathUpdateEvent, payload)
    79  }
    80  
    81  func (c *Client) CancelEvent(payload CancelEventRequest) error {
    82  	return c.callPatch(PathCancelEvent, payload)
    83  }
    84  
    85  func (c *Client) callPatch(path string, payload interface{}) (err error) {
    86  	request, err := c.jsonRequest(path, http.MethodPatch, payload)
    87  	if err != nil {
    88  		return fmt.Errorf("while creating json request for path %s: %w", path, err)
    89  	}
    90  
    91  	response, err := c.do(request)
    92  	defer func() {
    93  		if closeErr := c.closeResponseBody(response); closeErr != nil {
    94  			err = kebError.AsTemporaryError(closeErr, "while closing response body for call method")
    95  		}
    96  	}()
    97  	if err != nil {
    98  		return fmt.Errorf("while making request for path %s: %w", path, err)
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  func (c *Client) callPost(path string, payload interface{}) (err error) {
   105  	request, err := c.jsonRequest(path, http.MethodPost, payload)
   106  	if err != nil {
   107  		return fmt.Errorf("while creating json request for path %s: %w", path, err)
   108  	}
   109  
   110  	response, err := c.do(request)
   111  	defer func() {
   112  		if closeErr := c.closeResponseBody(response); closeErr != nil {
   113  			err = kebError.AsTemporaryError(closeErr, "while closing response body for call method")
   114  		}
   115  	}()
   116  	if err != nil {
   117  		return fmt.Errorf("while making request for path %s: %w", path, err)
   118  	}
   119  	return nil
   120  }
   121  
   122  func (c *Client) jsonRequest(path string, method string, payload interface{}) (*Request, error) {
   123  	buffer := &bytes.Buffer{}
   124  	encoder := json.NewEncoder(buffer)
   125  	err := encoder.Encode(payload)
   126  	if err != nil {
   127  		return &Request{}, err
   128  	}
   129  
   130  	return &Request{
   131  		Method:  method,
   132  		Path:    path,
   133  		Body:    buffer,
   134  		Headers: map[string]string{"content-type": "application/json"},
   135  	}, nil
   136  }
   137  
   138  func (c *Client) do(sciReq *Request) (*http.Response, error) {
   139  	url := fmt.Sprintf("%s%s", c.config.URL, sciReq.Path)
   140  	req, err := http.NewRequest(sciReq.Method, url, sciReq.Body)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	req.Close = true
   146  	for h, v := range sciReq.Headers {
   147  		req.Header.Set(h, v)
   148  	}
   149  
   150  	response, err := c.httpClient.Do(req)
   151  	if err != nil {
   152  		return &http.Response{}, kebError.AsTemporaryError(err, "while making request")
   153  	}
   154  
   155  	switch response.StatusCode {
   156  	case http.StatusOK, http.StatusCreated, http.StatusNoContent:
   157  		return response, nil
   158  	case http.StatusNotFound:
   159  		if sciReq.Delete {
   160  			return response, nil
   161  		}
   162  	case http.StatusRequestTimeout:
   163  		return response, kebError.NewTemporaryError(c.responseErrorMessage(response))
   164  	}
   165  
   166  	if response.StatusCode >= http.StatusInternalServerError {
   167  		return response, kebError.NewTemporaryError(c.responseErrorMessage(response))
   168  	}
   169  	return response, fmt.Errorf("while sending request to IAS: %s", c.responseErrorMessage(response))
   170  }
   171  
   172  func (c *Client) closeResponseBody(response *http.Response) error {
   173  	if response == nil {
   174  		return nil
   175  	}
   176  	if response.Body == nil {
   177  		return nil
   178  	}
   179  	return response.Body.Close()
   180  }
   181  
   182  func (c *Client) responseErrorMessage(response *http.Response) string {
   183  	body, err := ioutil.ReadAll(response.Body)
   184  	if err != nil {
   185  		return fmt.Sprintf("unexpected status code %d cannot read body response: %s", response.StatusCode, err)
   186  	}
   187  	return fmt.Sprintf("unexpected status code %d with body: %s", response.StatusCode, string(body))
   188  }