github.com/SAP/jenkins-library@v1.362.0/cmd/integrationArtifactResource.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 b64 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io" 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 := io.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 }