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  }