1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-16 11:09:33 +02:00
sap-jenkins-library/cmd/integrationArtifactResource.go
Mayur Belur Mohan 3a4f756573
IntegrationArtifact Fixes for issue #3121 (#3286)
Co-authored-by: Roland Stengel <r.stengel@sap.com>
2021-11-18 09:08:03 +01:00

233 lines
9.5 KiB
Go

package cmd
import (
"bytes"
b64 "encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"strings"
"github.com/Jeffail/gabs/v2"
"github.com/SAP/jenkins-library/pkg/cpi"
piperhttp "github.com/SAP/jenkins-library/pkg/http"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/pkg/errors"
)
type integrationArtifactResourceData struct {
Method string
URL string
IFlowID string
ScsMessage string
FlrMessage string
StatusCode int
}
func integrationArtifactResource(config integrationArtifactResourceOptions, telemetryData *telemetry.CustomData) {
// Utils can be used wherever the command.ExecRunner interface is expected.
// It can also be used for example as a mavenExecRunner.
httpClient := &piperhttp.Client{}
fileUtils := &piperutils.Files{}
// For HTTP calls import piperhttp "github.com/SAP/jenkins-library/pkg/http"
// and use a &piperhttp.Client{} in a custom system
// Example: step checkmarxExecuteScan.go
// Error situations should be bubbled up until they reach the line below which will then stop execution
// through the log.Entry().Fatal() call leading to an os.Exit(1) in the end.
err := runIntegrationArtifactResource(&config, telemetryData, fileUtils, httpClient)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runIntegrationArtifactResource(config *integrationArtifactResourceOptions, telemetryData *telemetry.CustomData, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) error {
serviceKey, err := cpi.ReadCpiServiceKey(config.APIServiceKey)
if err != nil {
return err
}
clientOptions := piperhttp.ClientOptions{}
header := make(http.Header)
header.Add("Accept", "application/json")
tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpClient}
token, err := cpi.CommonUtils.GetBearerToken(tokenParameters)
if err != nil {
return errors.Wrap(err, "failed to fetch Bearer Token")
}
clientOptions.Token = fmt.Sprintf("Bearer %s", token)
httpClient.SetOptions(clientOptions)
mode := strings.ToLower(strings.TrimSpace(config.Operation))
switch mode {
case "create":
return UploadIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
case "update":
return UpdateIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
case "delete":
return DeleteIntegrationArtifactResource(config, httpClient, fileUtils, serviceKey.OAuth.Host)
default:
return errors.New("invalid input for resource operation")
}
}
//UploadIntegrationArtifactResource - Upload new resource file to existing integration flow design time artefact
func UploadIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
httpMethod := "POST"
uploadIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/Resources", apiHost, config.IntegrationFlowID, "Active")
header := make(http.Header)
header.Add("content-type", "application/json")
payload, jsonError := GetJSONPayload(config, "create", fileUtils)
if jsonError != nil {
return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.ResourcePath)
}
uploadIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, uploadIflowStatusURL, payload, header, nil)
successMessage := "Successfully create a new resource file in the integration flow artefact"
failureMessage := "Failed to create a new resource file in the integration flow artefact"
integrationArtifactResourceData := integrationArtifactResourceData{
Method: httpMethod,
URL: uploadIflowStatusURL,
IFlowID: config.IntegrationFlowID,
ScsMessage: successMessage,
FlrMessage: failureMessage,
StatusCode: http.StatusCreated,
}
return HttpResponseHandler(uploadIflowStatusResp, httpErr, &integrationArtifactResourceData)
}
//UpdateIntegrationArtifactResource - Update integration artifact resource file
func UpdateIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
httpMethod := "PUT"
header := make(http.Header)
header.Add("content-type", "application/json")
fileName := filepath.Base(config.ResourcePath)
fileExt := GetResourceFileExtension(fileName)
if fileExt == "" {
return errors.New("invalid file extension in resource file")
}
updateIflowStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/$links/Resources(Name='%s',ResourceType='%s')", apiHost, config.IntegrationFlowID, "Active", fileName, fileExt)
payload, jsonError := GetJSONPayload(config, "update", fileUtils)
if jsonError != nil {
return errors.Wrapf(jsonError, "Failed to get json payload for file %v, failed with error", config.ResourcePath)
}
updateIflowStatusResp, httpErr := httpClient.SendRequest(httpMethod, updateIflowStatusURL, payload, header, nil)
successMessage := "Successfully updated resource file of the integration flow artefact"
failureMessage := "Failed to update rsource file of the integration flow artefact"
integrationArtifactResourceData := integrationArtifactResourceData{
Method: httpMethod,
URL: updateIflowStatusURL,
IFlowID: config.IntegrationFlowID,
ScsMessage: successMessage,
FlrMessage: failureMessage,
StatusCode: http.StatusOK,
}
return HttpResponseHandler(updateIflowStatusResp, httpErr, &integrationArtifactResourceData)
}
//DeleteIntegrationArtifactResource - Delete integration artifact resource file
func DeleteIntegrationArtifactResource(config *integrationArtifactResourceOptions, httpClient piperhttp.Sender, fileUtils piperutils.FileUtils, apiHost string) error {
httpMethod := "DELETE"
header := make(http.Header)
header.Add("content-type", "application/json")
fileName := filepath.Base(config.ResourcePath)
fileExt := GetResourceFileExtension(fileName)
if fileExt == "" {
return errors.New("invalid file extension in resource file")
}
deleteIflowResourceStatusURL := fmt.Sprintf("%s/api/v1/IntegrationDesigntimeArtifacts(Id='%s',Version='%s')/$links/Resources(Name='%s',ResourceType='%s')", apiHost, config.IntegrationFlowID, "Active", fileName, fileExt)
deleteIflowResourceStatusResp, httpErr := httpClient.SendRequest(httpMethod, deleteIflowResourceStatusURL, nil, header, nil)
successMessage := "Successfully deleted a resource file in the integration flow artefact"
failureMessage := "Failed to delete a resource file in the integration flow artefact"
integrationArtifactResourceData := integrationArtifactResourceData{
Method: httpMethod,
URL: deleteIflowResourceStatusURL,
IFlowID: config.IntegrationFlowID,
ScsMessage: successMessage,
FlrMessage: failureMessage,
StatusCode: http.StatusOK,
}
return HttpResponseHandler(deleteIflowResourceStatusResp, httpErr, &integrationArtifactResourceData)
}
//GetJSONPayload -return http payload as byte array
func GetJSONPayload(config *integrationArtifactResourceOptions, mode string, fileUtils piperutils.FileUtils) (*bytes.Buffer, error) {
fileContent, readError := fileUtils.FileRead(config.ResourcePath)
if readError != nil {
return nil, errors.Wrapf(readError, "Error reading file")
}
fileName := filepath.Base(config.ResourcePath)
jsonObj := gabs.New()
if mode == "create" {
jsonObj.Set(fileName, "Name")
jsonObj.Set(GetResourceFileExtension(fileName), "ResourceType")
jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ResourceContent")
} else if mode == "update" {
jsonObj.Set(b64.StdEncoding.EncodeToString(fileContent), "ResourceContent")
} else {
return nil, fmt.Errorf("Unkown node: '%s'", mode)
}
jsonBody, jsonErr := json.Marshal(jsonObj)
if jsonErr != nil {
return nil, errors.Wrapf(jsonErr, "json payload is invalid for integration flow artifact %q", config.IntegrationFlowID)
}
return bytes.NewBuffer(jsonBody), nil
}
//GetResourceFileExtension -return resource file extension
func GetResourceFileExtension(filename string) string {
fileExtension := filepath.Ext(filename)
switch fileExtension {
case ".xsl":
return "xslt"
case ".gsh", ".groovy":
return "groovy"
case ".js":
return "js"
case ".jar":
return "jar"
default:
return ""
}
}
//HttpResponseHandler - handle http response object
func HttpResponseHandler(resp *http.Response, httpErr error, integrationArtifactResourceData *integrationArtifactResourceData) error {
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
if resp == nil {
return errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
}
if resp.StatusCode == integrationArtifactResourceData.StatusCode {
log.Entry().
WithField("IntegrationFlowID", integrationArtifactResourceData.IFlowID).
Info(integrationArtifactResourceData.ScsMessage)
return nil
}
if httpErr != nil {
responseBody, readErr := ioutil.ReadAll(resp.Body)
if readErr != nil {
return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", resp.StatusCode)
}
log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), resp.StatusCode)
return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", integrationArtifactResourceData.Method, integrationArtifactResourceData.URL, string(responseBody))
}
return errors.Errorf("%s, Response Status code: %v", integrationArtifactResourceData.FlrMessage, resp.StatusCode)
}