github.com/jaylevin/jenkins-library@v1.230.4/cmd/integrationArtifactResource.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	b64 "encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"github.com/Jeffail/gabs/v2"
    14  	"github.com/SAP/jenkins-library/pkg/cpi"
    15  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    16  	"github.com/SAP/jenkins-library/pkg/log"
    17  	"github.com/SAP/jenkins-library/pkg/piperutils"
    18  	"github.com/SAP/jenkins-library/pkg/telemetry"
    19  	"github.com/pkg/errors"
    20  )
    21  
    22  type integrationArtifactResourceData struct {
    23  	Method     string
    24  	URL        string
    25  	IFlowID    string
    26  	ScsMessage string
    27  	FlrMessage string
    28  	StatusCode int
    29  }
    30  
    31  func integrationArtifactResource(config integrationArtifactResourceOptions, telemetryData *telemetry.CustomData) {
    32  	// Utils can be used wherever the command.ExecRunner interface is expected.
    33  	// It can also be used for example as a mavenExecRunner.
    34  	httpClient := &piperhttp.Client{}
    35  	fileUtils := &piperutils.Files{}
    36  
    37  	// For HTTP calls import  piperhttp "github.com/SAP/jenkins-library/pkg/http"
    38  	// and use a  &piperhttp.Client{} in a custom system
    39  	// Example: step checkmarxExecuteScan.go
    40  
    41  	// Error situations should be bubbled up until they reach the line below which will then stop execution
    42  	// through the log.Entry().Fatal() call leading to an os.Exit(1) in the end.
    43  	err := runIntegrationArtifactResource(&config, telemetryData, fileUtils, httpClient)
    44  	if err != nil {
    45  		log.Entry().WithError(err).Fatal("step execution failed")
    46  	}
    47  }
    48  
    49  func runIntegrationArtifactResource(config *integrationArtifactResourceOptions, telemetryData *telemetry.CustomData, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) error {
    50  	serviceKey, err := cpi.ReadCpiServiceKey(config.APIServiceKey)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	clientOptions := piperhttp.ClientOptions{}
    56  	header := make(http.Header)
    57  	header.Add("Accept", "application/json")
    58  	tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpClient}
    59  	token, err := cpi.CommonUtils.GetBearerToken(tokenParameters)
    60  	if err != nil {
    61  		return errors.Wrap(err, "failed to fetch Bearer Token")
    62  	}
    63  	clientOptions.Token = fmt.Sprintf("Bearer %s", token)
    64  	httpClient.SetOptions(clientOptions)
    65  	mode := strings.ToLower(strings.TrimSpace(config.Operation))
    66  	switch mode {
    67  	case "create":
    68  		return UploadIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
    69  	case "update":
    70  		return UpdateIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
    71  	case "delete":
    72  		return DeleteIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
    73  	default:
    74  		return errors.New("invalid input for resource operation")
    75  	}
    76  }
    77  
    78  //UploadIntegrationArtifactResource - Upload new resource file to existing integration flow design time artefact
    79  func UploadIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
    80  	httpMethod := "POST"
    81  	uploadIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/Resources", apiHost, config.IntegrationFlowID, "Active")
    82  	header := make(http.Header)
    83  	header.Add("content-type", "application/json")
    84  	payload, jsonError := GetJSONPayload(config, "create", fileUtils)
    85  	if jsonError != nil {
    86  		return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.ResourcePath)
    87  	}
    88  
    89  	uploadIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, uploadIflowStatusURL, payload, header, nil)
    90  
    91  	successMessage := "Successfully create a new resource file in the integration flow artefact"
    92  	failureMessage := "Failed to create a new resource file in the integration flow artefact"
    93  	integrationArtifactResourceData := integrationArtifactResourceData{
    94  		Method:     httpMethod,
    95  		URL:        uploadIflowStatusURL,
    96  		IFlowID:    config.IntegrationFlowID,
    97  		ScsMessage: successMessage,
    98  		FlrMessage: failureMessage,
    99  		StatusCode: http.StatusCreated,
   100  	}
   101  
   102  	return HttpResponseHandler(uploadIflowStatusResp, httpErr, &integrationArtifactResourceData)
   103  }
   104  
   105  //UpdateIntegrationArtifactResource - Update integration artifact resource file
   106  func UpdateIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
   107  	httpMethod := "PUT"
   108  	header := make(http.Header)
   109  	header.Add("content-type", "application/json")
   110  	fileName := filepath.Base(config.ResourcePath)
   111  	fileExt := GetResourceFileExtension(fileName)
   112  	if fileExt == "" {
   113  		return errors.New("invalid file extension in resource file")
   114  	}
   115  	updateIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/$links/Resources(Name='%s',ResourceType='%s')", apiHost, config.IntegrationFlowID, "Active", fileName, fileExt)
   116  	payload, jsonError := GetJSONPayload(config, "update", fileUtils)
   117  	if jsonError != nil {
   118  		return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.ResourcePath)
   119  	}
   120  	updateIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, updateIflowStatusURL, payload, header, nil)
   121  
   122  	successMessage := "Successfully updated resource file of the integration flow artefact"
   123  	failureMessage := "Failed to update rsource file of the integration flow artefact"
   124  	integrationArtifactResourceData := integrationArtifactResourceData{
   125  		Method:     httpMethod,
   126  		URL:        updateIflowStatusURL,
   127  		IFlowID:    config.IntegrationFlowID,
   128  		ScsMessage: successMessage,
   129  		FlrMessage: failureMessage,
   130  		StatusCode: http.StatusOK,
   131  	}
   132  
   133  	return HttpResponseHandler(updateIflowStatusResp, httpErr, &integrationArtifactResourceData)
   134  }
   135  
   136  //DeleteIntegrationArtifactResource - Delete integration artifact resource file
   137  func DeleteIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
   138  	httpMethod := "DELETE"
   139  	header := make(http.Header)
   140  	header.Add("content-type", "application/json")
   141  	fileName := filepath.Base(config.ResourcePath)
   142  	fileExt := GetResourceFileExtension(fileName)
   143  	if fileExt == "" {
   144  		return errors.New("invalid file extension in resource file")
   145  	}
   146  	deleteIflowResourceStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/$links/Resources(Name='%s',ResourceType='%s')", apiHost, config.IntegrationFlowID, "Active", fileName, fileExt)
   147  	deleteIflowResourceStatusResp, httpErr := httpClient.SendRequest(httpMethod, deleteIflowResourceStatusURL, nil, header, nil)
   148  
   149  	successMessage := "Successfully deleted a resource file in the integration flow artefact"
   150  	failureMessage := "Failed to delete a resource file in the integration flow artefact"
   151  	integrationArtifactResourceData := integrationArtifactResourceData{
   152  		Method:     httpMethod,
   153  		URL:        deleteIflowResourceStatusURL,
   154  		IFlowID:    config.IntegrationFlowID,
   155  		ScsMessage: successMessage,
   156  		FlrMessage: failureMessage,
   157  		StatusCode: http.StatusOK,
   158  	}
   159  
   160  	return HttpResponseHandler(deleteIflowResourceStatusResp, httpErr, &integrationArtifactResourceData)
   161  }
   162  
   163  //GetJSONPayload -return http payload as byte array
   164  func GetJSONPayload(config *integrationArtifactResourceOptions, mode string, fileUtils piperutils.FileUtils) (*bytes.Buffer, error) {
   165  	fileContent, readError := fileUtils.FileRead(config.ResourcePath)
   166  	if readError != nil {
   167  		return nil, errors.Wrapf(readError, "Error reading file")
   168  	}
   169  	fileName := filepath.Base(config.ResourcePath)
   170  	jsonObj := gabs.New()
   171  	if mode == "create" {
   172  		jsonObj.Set(fileName, "Name")
   173  		jsonObj.Set(GetResourceFileExtension(fileName), "ResourceType")
   174  		jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ResourceContent")
   175  	} else if mode == "update" {
   176  		jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ResourceContent")
   177  	} else {
   178  		return nil, fmt.Errorf("Unkown node: '%s'", mode)
   179  	}
   180  
   181  	jsonBody, jsonErr := json.Marshal(jsonObj)
   182  
   183  	if jsonErr != nil {
   184  		return nil, errors.Wrapf(jsonErr, "json payload is invalid for integration flow artifact %q", config.IntegrationFlowID)
   185  	}
   186  	return bytes.NewBuffer(jsonBody), nil
   187  }
   188  
   189  //GetResourceFileExtension -return resource file extension
   190  func GetResourceFileExtension(filename string) string {
   191  	fileExtension := filepath.Ext(filename)
   192  	switch fileExtension {
   193  	case ".xsl":
   194  		return "xslt"
   195  	case ".gsh", ".groovy":
   196  		return "groovy"
   197  	case ".js":
   198  		return "js"
   199  	case ".jar":
   200  		return "jar"
   201  	default:
   202  		return ""
   203  	}
   204  }
   205  
   206  //HttpResponseHandler - handle http response object
   207  func HttpResponseHandler(resp *http.Response, httpErr error, integrationArtifactResourceData *integrationArtifactResourceData) error {
   208  
   209  	if resp != nil && resp.Body != nil {
   210  		defer resp.Body.Close()
   211  	}
   212  
   213  	if resp == nil {
   214  		return errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
   215  	}
   216  
   217  	if resp.StatusCode == integrationArtifactResourceData.StatusCode {
   218  		log.Entry().
   219  			WithField("IntegrationFlowID", integrationArtifactResourceData.IFlowID).
   220  			Info(integrationArtifactResourceData.ScsMessage)
   221  		return nil
   222  	}
   223  	if httpErr != nil {
   224  		responseBody, readErr := ioutil.ReadAll(resp.Body)
   225  		if readErr != nil {
   226  			return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", resp.StatusCode)
   227  		}
   228  		log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), resp.StatusCode)
   229  		return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", integrationArtifactResourceData.Method, integrationArtifactResourceData.URL, string(responseBody))
   230  	}
   231  	return errors.Errorf("%s, Response Status code: %v", integrationArtifactResourceData.FlrMessage, resp.StatusCode)
   232  }