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 }