github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/ans/ans.go (about) 1 package ans 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 11 "github.com/SAP/jenkins-library/pkg/xsuaa" 12 "github.com/pkg/errors" 13 ) 14 15 // ANS holds the setup for the xsuaa service to retrieve a bearer token for authorization and 16 // the URL to the SAP Alert Notification Service backend 17 type ANS struct { 18 XSUAA xsuaa.XSUAA 19 URL string 20 } 21 22 // Client to send the event to the SAP Alert Notification Service 23 type Client interface { 24 Send(event Event) error 25 CheckCorrectSetup() error 26 SetServiceKey(serviceKey ServiceKey) 27 } 28 29 // ServiceKey holds the information about the SAP Alert Notification Service to send the events to 30 type ServiceKey struct { 31 Url string `json:"url"` 32 ClientId string `json:"client_id"` 33 ClientSecret string `json:"client_secret"` 34 OauthUrl string `json:"oauth_url"` 35 } 36 37 // UnmarshallServiceKeyJSON unmarshalls the given json service key string. 38 func UnmarshallServiceKeyJSON(serviceKeyJSON string) (ansServiceKey ServiceKey, err error) { 39 if err = json.Unmarshal([]byte(serviceKeyJSON), &ansServiceKey); err != nil { 40 err = errors.Wrap(err, "error unmarshalling ANS serviceKey") 41 } 42 return 43 } 44 45 // SetServiceKey sets the xsuaa service key 46 func (ans *ANS) SetServiceKey(serviceKey ServiceKey) { 47 ans.XSUAA = xsuaa.XSUAA{ 48 OAuthURL: serviceKey.OauthUrl, 49 ClientID: serviceKey.ClientId, 50 ClientSecret: serviceKey.ClientSecret, 51 } 52 ans.URL = serviceKey.Url 53 } 54 55 // CheckCorrectSetup of the SAP Alert Notification Service 56 func (ans *ANS) CheckCorrectSetup() error { 57 const testPath = "/cf/consumer/v1/matched-events" 58 entireUrl := strings.TrimRight(ans.URL, "/") + testPath 59 60 response, err := ans.sendRequest(http.MethodGet, entireUrl, nil) 61 if err != nil { 62 return err 63 } 64 65 return handleStatusCode(entireUrl, http.StatusOK, response) 66 } 67 68 // Send an event to the SAP Alert Notification Service 69 func (ans *ANS) Send(event Event) error { 70 const eventPath = "/cf/producer/v1/resource-events" 71 entireUrl := strings.TrimRight(ans.URL, "/") + eventPath 72 73 requestBody, err := json.Marshal(event) 74 if err != nil { 75 return err 76 } 77 78 response, err := ans.sendRequest(http.MethodPost, entireUrl, bytes.NewBuffer(requestBody)) 79 if err != nil { 80 return err 81 } 82 83 return handleStatusCode(entireUrl, http.StatusAccepted, response) 84 } 85 86 func (ans *ANS) sendRequest(method, url string, body io.Reader) (response *http.Response, err error) { 87 request, err := ans.newRequest(method, url, body) 88 if err != nil { 89 return 90 } 91 92 httpClient := http.Client{} 93 return httpClient.Do(request) 94 } 95 96 func (ans *ANS) newRequest(method, url string, body io.Reader) (request *http.Request, err error) { 97 header := make(http.Header) 98 if err = ans.XSUAA.SetAuthHeaderIfNotPresent(&header); err != nil { 99 return 100 } 101 102 request, err = http.NewRequest(method, url, body) 103 if err != nil { 104 return 105 } 106 request.Header.Add(authHeaderKey, header.Get(authHeaderKey)) 107 request.Header.Add("Content-Type", "application/json") 108 109 return 110 } 111 112 func handleStatusCode(requestedUrl string, expectedStatus int, response *http.Response) error { 113 if response.StatusCode != expectedStatus { 114 statusCodeError := fmt.Errorf("ANS http request to '%s' failed. Did not get expected status code %d; instead got %d", 115 requestedUrl, expectedStatus, response.StatusCode) 116 responseBody, err := readResponseBody(response) 117 if err != nil { 118 err = errors.Wrapf(err, "%s; reading response body failed", statusCodeError.Error()) 119 } else { 120 err = fmt.Errorf("%s; response body: %s", statusCodeError.Error(), responseBody) 121 } 122 return err 123 } 124 return nil 125 } 126 127 func readResponseBody(response *http.Response) ([]byte, error) { 128 if response == nil { 129 return nil, errors.Errorf("did not retrieve an HTTP response") 130 } 131 defer response.Body.Close() 132 bodyText, readErr := io.ReadAll(response.Body) 133 if readErr != nil { 134 return nil, errors.Wrap(readErr, "HTTP response body could not be read") 135 } 136 return bodyText, nil 137 }