github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/pkg/cpi/commonUtils.go (about)

     1  package cpi
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"mime"
     8  	"net/http"
     9  	"os"
    10  	"path/filepath"
    11  
    12  	"github.com/SAP/jenkins-library/pkg/log"
    13  
    14  	"github.com/Jeffail/gabs/v2"
    15  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // CommonUtils for CPI
    20  type CommonUtils interface {
    21  	GetBearerToken() (string, error)
    22  }
    23  
    24  // HttpCPIUtils for CPI
    25  type HttpCPIUtils interface {
    26  	HandleHTTPFileDownloadResponse() error
    27  }
    28  
    29  // HTTPUploadUtils for CPI
    30  type HTTPUploadUtils interface {
    31  	HandleHTTPFileUploadResponse() error
    32  	HandleHTTPGetRequestResponse() (string, error)
    33  }
    34  
    35  // TokenParameters struct
    36  type TokenParameters struct {
    37  	TokenURL, Username, Password string
    38  	Client                       piperhttp.Sender
    39  }
    40  
    41  // HttpParameters struct
    42  type HttpFileDownloadRequestParameters struct {
    43  	ErrMessage, FileDownloadPath string
    44  	Response                     *http.Response
    45  }
    46  
    47  // HTTPFileUploadRequestParameters struct
    48  type HttpFileUploadRequestParameters struct {
    49  	ErrMessage, FilePath, HTTPMethod, HTTPURL, SuccessMessage string
    50  	Response                                                  *http.Response
    51  	HTTPErr                                                   error
    52  }
    53  
    54  // ServiceKey contains information about a CPI service key
    55  type ServiceKey struct {
    56  	OAuth OAuth `json:"oauth"`
    57  }
    58  
    59  // OAuth is inside a CPI service key and contains more needed information
    60  type OAuth struct {
    61  	Host                  string `json:"url"`
    62  	OAuthTokenProviderURL string `json:"tokenurl"`
    63  	ClientID              string `json:"clientid"`
    64  	ClientSecret          string `json:"clientsecret"`
    65  }
    66  
    67  // ReadCpiServiceKey unmarshalls the give json service key string.
    68  func ReadCpiServiceKey(serviceKeyJSON string) (cpiServiceKey ServiceKey, err error) {
    69  	// parse
    70  	err = json.Unmarshal([]byte(serviceKeyJSON), &cpiServiceKey)
    71  	if err != nil {
    72  		err = errors.Wrap(err, "error unmarshalling serviceKey")
    73  		return
    74  	}
    75  
    76  	log.Entry().Info("CPI serviceKey read successfully")
    77  	return
    78  }
    79  
    80  // GetBearerToken -Provides the bearer token for making CPI OData calls
    81  func (tokenParameters TokenParameters) GetBearerToken() (string, error) {
    82  
    83  	httpClient := tokenParameters.Client
    84  
    85  	clientOptions := piperhttp.ClientOptions{
    86  		Username: tokenParameters.Username,
    87  		Password: tokenParameters.Password,
    88  	}
    89  	httpClient.SetOptions(clientOptions)
    90  
    91  	header := make(http.Header)
    92  	header.Add("Accept", "application/json")
    93  	tokenFinalURL := fmt.Sprintf("%s?grant_type=client_credentials", tokenParameters.TokenURL)
    94  	method := "POST"
    95  	resp, httpErr := httpClient.SendRequest(method, tokenFinalURL, nil, header, nil)
    96  	if httpErr != nil {
    97  		return "", errors.Wrapf(httpErr, "HTTP %v request to %v failed with error", method, tokenFinalURL)
    98  	}
    99  	if resp != nil && resp.Body != nil {
   100  		defer resp.Body.Close()
   101  	}
   102  
   103  	if resp == nil {
   104  		return "", errors.Errorf("did not retrieve a HTTP response")
   105  	}
   106  
   107  	if resp.StatusCode != 200 {
   108  		return "", errors.Errorf("did not retrieve a valid HTTP response code: %v", httpErr)
   109  	}
   110  
   111  	bodyText, readErr := io.ReadAll(resp.Body)
   112  	if readErr != nil {
   113  		return "", errors.Wrap(readErr, "HTTP response body could not be read")
   114  	}
   115  	jsonResponse, parsingErr := gabs.ParseJSON([]byte(bodyText))
   116  	if parsingErr != nil {
   117  		return "", errors.Wrapf(parsingErr, "HTTP response body could not be parsed as JSON: %v", string(bodyText))
   118  	}
   119  	token := jsonResponse.Path("access_token").Data().(string)
   120  	return token, nil
   121  }
   122  
   123  // HandleHTTPFileDownloadResponse - Handle the file download response for http multipart response
   124  func (httpFileDownloadRequestParameters HttpFileDownloadRequestParameters) HandleHTTPFileDownloadResponse() error {
   125  	response := httpFileDownloadRequestParameters.Response
   126  	contentDisposition := response.Header.Get("Content-Disposition")
   127  	disposition, params, err := mime.ParseMediaType(contentDisposition)
   128  	if err != nil {
   129  		return errors.Wrapf(err, "failed to read filename from http response headers, Content-Disposition %s", disposition)
   130  	}
   131  	filename := params["filename"]
   132  
   133  	if response != nil && response.Body != nil {
   134  		defer response.Body.Close()
   135  	}
   136  
   137  	if response.StatusCode == 200 {
   138  		workspaceRelativePath := httpFileDownloadRequestParameters.FileDownloadPath
   139  		err = os.MkdirAll(workspaceRelativePath, 0755)
   140  		// handling error while creating a workspce directoy for file download, if one not exist already!
   141  		if err != nil {
   142  			return errors.Wrapf(err, "Failed to create workspace directory")
   143  		}
   144  		zipFileName := filepath.Join(workspaceRelativePath, filename)
   145  		file, err := os.Create(zipFileName)
   146  		// handling error while creating a file in the filesystem
   147  		if err != nil {
   148  			return errors.Wrap(err, "failed to create zip archive of api proxy")
   149  		}
   150  		_, err = io.Copy(file, response.Body)
   151  		if err != nil {
   152  			return err
   153  		}
   154  		return nil
   155  	}
   156  	responseBody, readErr := io.ReadAll(response.Body)
   157  	if readErr != nil {
   158  		return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", response.StatusCode)
   159  	}
   160  	log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code : %v", responseBody, response.StatusCode)
   161  	return errors.Errorf("%s, Response Status code: %v", httpFileDownloadRequestParameters.ErrMessage, response.StatusCode)
   162  }
   163  
   164  // HandleHTTPFileUploadResponse - Handle the file upload response
   165  func (httpFileUploadRequestParameters HttpFileUploadRequestParameters) HandleHTTPFileUploadResponse() error {
   166  	response := httpFileUploadRequestParameters.Response
   167  	httpErr := httpFileUploadRequestParameters.HTTPErr
   168  	if response != nil && response.Body != nil {
   169  		defer response.Body.Close()
   170  	}
   171  
   172  	if response == nil {
   173  		return errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
   174  	}
   175  	responseCode := response.StatusCode
   176  
   177  	if (responseCode == http.StatusOK) || (responseCode == http.StatusCreated) {
   178  		log.Entry().
   179  			WithField("Created Artifact", httpFileUploadRequestParameters.FilePath).
   180  			Info(httpFileUploadRequestParameters.SuccessMessage)
   181  		return nil
   182  	}
   183  	if httpErr != nil {
   184  		responseBody, readErr := io.ReadAll(response.Body)
   185  		if readErr != nil {
   186  			return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", response.StatusCode)
   187  		}
   188  		log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), response.StatusCode)
   189  		return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpFileUploadRequestParameters.HTTPMethod, httpFileUploadRequestParameters.HTTPURL, string(responseBody))
   190  	}
   191  	return errors.Errorf("%s, Response Status code: %v", httpFileUploadRequestParameters.ErrMessage, response.StatusCode)
   192  }
   193  
   194  // HandleHTTPGetRequestResponse - Handle the GET Request response data
   195  func (httpGetRequestParameters HttpFileUploadRequestParameters) HandleHTTPGetRequestResponse() (string, error) {
   196  	response := httpGetRequestParameters.Response
   197  	httpErr := httpGetRequestParameters.HTTPErr
   198  	if response != nil && response.Body != nil {
   199  		defer response.Body.Close()
   200  	}
   201  
   202  	if response == nil {
   203  		return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
   204  	}
   205  	if response.StatusCode == http.StatusOK {
   206  		responseBody, readErr := io.ReadAll(response.Body)
   207  		if readErr != nil {
   208  			return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", response.StatusCode)
   209  		}
   210  		return string(responseBody), nil
   211  	}
   212  	if httpErr != nil {
   213  		responseBody, readErr := io.ReadAll(response.Body)
   214  		if readErr != nil {
   215  			return "", errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", response.StatusCode)
   216  		}
   217  		log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), response.StatusCode)
   218  		return "", errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpGetRequestParameters.HTTPMethod, httpGetRequestParameters.HTTPURL, string(responseBody))
   219  	}
   220  	return "", errors.Errorf("%s, Response Status code: %v", httpGetRequestParameters.ErrMessage, response.StatusCode)
   221  }