1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-11-06 09:09:19 +02:00

ApiProxyUpload Command (#3295)

* ApiProxyUpload Command

* Code Review Fixes

* CodeReview Changes

* CodeReview Fixes

* YAML fixes

* CodeReview Fix

* Code Review Fixes

* CodeReview Fixes

* Code Climate Fixes

* Code Review Fixes

* Code Review Fixes

Co-authored-by: Roland Stengel <r.stengel@sap.com>
Co-authored-by: Oliver Feldmann <oliver.feldmann@sap.com>
This commit is contained in:
Mayur Belur Mohan
2021-12-28 14:01:50 +05:30
committed by GitHub
parent b5da011200
commit f27cb4e482
13 changed files with 500 additions and 1 deletions

75
cmd/apiProxyUpload.go Normal file
View File

@@ -0,0 +1,75 @@
package cmd
import (
"bytes"
b64 "encoding/base64"
"fmt"
"net/http"
"strings"
"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"
)
func apiProxyUpload(config apiProxyUploadOptions, 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 := runApiProxyUpload(&config, telemetryData, fileUtils, httpClient)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runApiProxyUpload(config *apiProxyUploadOptions, telemetryData *telemetry.CustomData, fileUtils piperutils.FileUtils, httpClient piperhttp.Sender) error {
serviceKey, err := cpi.ReadCpiServiceKey(config.APIServiceKey)
if err != nil {
return err
}
clientOptions := piperhttp.ClientOptions{}
tokenParameters := cpi.TokenParameters{TokenURL: serviceKey.OAuth.OAuthTokenProviderURL, Username: serviceKey.OAuth.ClientID, Password: serviceKey.OAuth.ClientSecret, Client: httpClient}
token, tokenErr := cpi.CommonUtils.GetBearerToken(tokenParameters)
if tokenErr != nil {
return errors.Wrap(tokenErr, "failed to fetch Bearer Token")
}
clientOptions.Token = fmt.Sprintf("Bearer %s", token)
httpClient.SetOptions(clientOptions)
httpMethod := http.MethodPost
uploadApiProxyStatusURL := fmt.Sprintf("%s/apiportal/api/1.0/Transport.svc/APIProxies", serviceKey.OAuth.Host)
header := make(http.Header)
header.Add("Accept", "application/zip")
fileContent, readError := fileUtils.FileRead(config.FilePath)
if readError != nil {
return errors.Wrapf(readError, "Error reading file")
}
if !strings.Contains(config.FilePath, "zip") {
return errors.New("not valid zip archive")
}
payload := []byte(b64.StdEncoding.EncodeToString(fileContent))
apiProxyUploadStatusResp, httpErr := httpClient.SendRequest(httpMethod, uploadApiProxyStatusURL, bytes.NewBuffer(payload), header, nil)
failureMessage := "Failed to upload API Proxy artefact"
successMessage := "Successfully created api proxy artefact in API Portal"
httpFileUploadRequestParameters := cpi.HttpFileUploadRequestParameters{
ErrMessage: failureMessage,
FilePath: config.FilePath,
Response: apiProxyUploadStatusResp,
HTTPMethod: httpMethod,
HTTPURL: uploadApiProxyStatusURL,
HTTPErr: httpErr,
SuccessMessage: successMessage}
return cpi.HTTPUploadUtils.HandleHTTPFileUploadResponse(httpFileUploadRequestParameters)
}

View File

@@ -0,0 +1,164 @@
// Code generated by piper's step-generator. DO NOT EDIT.
package cmd
import (
"fmt"
"os"
"time"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/splunk"
"github.com/SAP/jenkins-library/pkg/telemetry"
"github.com/SAP/jenkins-library/pkg/validation"
"github.com/spf13/cobra"
)
type apiProxyUploadOptions struct {
APIServiceKey string `json:"apiServiceKey,omitempty"`
FilePath string `json:"filePath,omitempty"`
}
// ApiProxyUploadCommand Upload an api proxy artifact in to the API Portal
func ApiProxyUploadCommand() *cobra.Command {
const STEP_NAME = "apiProxyUpload"
metadata := apiProxyUploadMetadata()
var stepConfig apiProxyUploadOptions
var startTime time.Time
var logCollector *log.CollectorHook
var splunkClient *splunk.Splunk
telemetryClient := &telemetry.Telemetry{}
var createApiProxyUploadCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Upload an api proxy artifact in to the API Portal",
Long: `With this step you can upload an api proxy artifact in to the API Portal using the OData API.
Learn more about the SAP API Management API for uploading an api proxy artifact [here](https://help.sap.com/viewer/66d066d903c2473f81ec33acfe2ccdb4/Cloud/en-US/e26b3320cd534ae4bc743af8013a8abb.html).`,
PreRunE: func(cmd *cobra.Command, _ []string) error {
startTime = time.Now()
log.SetStepName(STEP_NAME)
log.SetVerbose(GeneralConfig.Verbose)
GeneralConfig.GitHubAccessTokens = ResolveAccessTokens(GeneralConfig.GitHubTokens)
path, _ := os.Getwd()
fatalHook := &log.FatalHook{CorrelationID: GeneralConfig.CorrelationID, Path: path}
log.RegisterHook(fatalHook)
err := PrepareConfig(cmd, &metadata, STEP_NAME, &stepConfig, config.OpenPiperFile)
if err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
log.RegisterSecret(stepConfig.APIServiceKey)
if len(GeneralConfig.HookConfig.SentryConfig.Dsn) > 0 {
sentryHook := log.NewSentryHook(GeneralConfig.HookConfig.SentryConfig.Dsn, GeneralConfig.CorrelationID)
log.RegisterHook(&sentryHook)
}
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunkClient = &splunk.Splunk{}
logCollector = &log.CollectorHook{CorrelationID: GeneralConfig.CorrelationID}
log.RegisterHook(logCollector)
}
validation, err := validation.New(validation.WithJSONNamesForStructFields(), validation.WithPredefinedErrorMessages())
if err != nil {
return err
}
if err = validation.ValidateStruct(stepConfig); err != nil {
log.SetErrorCategory(log.ErrorConfiguration)
return err
}
return nil
},
Run: func(_ *cobra.Command, _ []string) {
stepTelemetryData := telemetry.CustomData{}
stepTelemetryData.ErrorCode = "1"
handler := func() {
config.RemoveVaultSecretFiles()
stepTelemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
stepTelemetryData.ErrorCategory = log.GetErrorCategory().String()
stepTelemetryData.PiperCommitHash = GitCommit
telemetryClient.SetData(&stepTelemetryData)
telemetryClient.Send()
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunkClient.Send(telemetryClient.GetData(), logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetryClient.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunkClient.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.Dsn,
GeneralConfig.HookConfig.SplunkConfig.Token,
GeneralConfig.HookConfig.SplunkConfig.Index,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
}
apiProxyUpload(stepConfig, &stepTelemetryData)
stepTelemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addApiProxyUploadFlags(createApiProxyUploadCmd, &stepConfig)
return createApiProxyUploadCmd
}
func addApiProxyUploadFlags(cmd *cobra.Command, stepConfig *apiProxyUploadOptions) {
cmd.Flags().StringVar(&stepConfig.APIServiceKey, "apiServiceKey", os.Getenv("PIPER_apiServiceKey"), "Service key JSON string to access the API Management Runtime service instance of plan 'api'")
cmd.Flags().StringVar(&stepConfig.FilePath, "filePath", os.Getenv("PIPER_filePath"), "Specifies api proxy zip artifact relative file path")
cmd.MarkFlagRequired("apiServiceKey")
cmd.MarkFlagRequired("filePath")
}
// retrieve step metadata
func apiProxyUploadMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "apiProxyUpload",
Aliases: []config.Alias{},
Description: "Upload an api proxy artifact in to the API Portal",
},
Spec: config.StepSpec{
Inputs: config.StepInputs{
Secrets: []config.StepSecrets{
{Name: "apimApiServiceKeyCredentialsId", Description: "Jenkins secret text credential ID containing the service key to the API Management Runtime service instance of plan 'api'", Type: "jenkins"},
},
Parameters: []config.StepParameters{
{
Name: "apiServiceKey",
ResourceRef: []config.ResourceReference{
{
Name: "apimApiServiceKeyCredentialsId",
Param: "apiServiceKey",
Type: "secret",
},
},
Scope: []string{"PARAMETERS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_apiServiceKey"),
},
{
Name: "filePath",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_filePath"),
},
},
},
},
}
return theMetaData
}

View File

@@ -0,0 +1,17 @@
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestApiProxyUploadCommand(t *testing.T) {
t.Parallel()
testCmd := ApiProxyUploadCommand()
// only high level testing performed - details are tested in step generation procedure
assert.Equal(t, "apiProxyUpload", testCmd.Use, "command name incorrect")
}

125
cmd/apiProxyUpload_test.go Normal file
View File

@@ -0,0 +1,125 @@
package cmd
import (
"path/filepath"
"testing"
"github.com/SAP/jenkins-library/pkg/mock"
"github.com/stretchr/testify/assert"
)
func TestRunApiProxyUpload(t *testing.T) {
t.Parallel()
t.Run("Successfull Api Proxy Create Test", func(t *testing.T) {
filesMock := mock.FilesMock{}
path := filepath.Join("tempDir", "apiproxy.zip")
filesMock.AddFile(path, []byte("dummy content"))
exists, err := filesMock.FileExists(path)
if err != nil {
t.Fatal("Failed to create temporary file")
}
assert.True(t, exists)
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiProxyUploadOptions{
APIServiceKey: apiServiceKey,
FilePath: path,
}
httpClient := httpMockCpis{CPIFunction: "ApiProxyUpload", ResponseBody: ``, TestType: "ApiProxyUploadPositiveCase"}
err = runApiProxyUpload(&config, nil, &filesMock, &httpClient)
if assert.NoError(t, err) {
t.Run("check url", func(t *testing.T) {
assert.Equal(t, "https://demo/apiportal/api/1.0/Transport.svc/APIProxies", httpClient.URL)
})
t.Run("check method", func(t *testing.T) {
assert.Equal(t, "POST", httpClient.Method)
})
}
})
t.Run("Failed case of API Proxy Create Test", func(t *testing.T) {
filesMock := mock.FilesMock{}
path := filepath.Join("tempDir", "apiproxy.zip")
filesMock.AddFile(path, []byte("dummy content"))
exists, err := filesMock.FileExists(path)
assert.NoError(t, err)
assert.True(t, exists)
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiProxyUploadOptions{
APIServiceKey: apiServiceKey,
FilePath: path,
}
httpClient := httpMockCpis{CPIFunction: "ApiProxyArtifactFail", ResponseBody: ``, TestType: "NegativeApiProxyArtifactUploadResBody"}
uploadErr := runApiProxyUpload(&config, nil, &filesMock, &httpClient)
assert.EqualError(t, uploadErr, "HTTP POST request to https://demo/apiportal/api/1.0/Transport.svc/APIProxies failed with error: : Service not Found")
})
t.Run("file not exist", func(t *testing.T) {
filesMock := mock.FilesMock{}
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiProxyUploadOptions{
APIServiceKey: apiServiceKey,
FilePath: "",
}
httpClient := httpMockCpis{CPIFunction: "ApiProxyArtifactFail", ResponseBody: ``, TestType: "NegativeApiProxyArtifactUploadResBody"}
uploadErr := runApiProxyUpload(&config, nil, &filesMock, &httpClient)
assert.EqualError(t, uploadErr, "Error reading file: could not read ''")
})
t.Run("file not zip", func(t *testing.T) {
filesMock := mock.FilesMock{}
path := filepath.Join("tempDir", "apiproxy.pptx")
filesMock.AddFile(path, []byte("dummy content"))
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiProxyUploadOptions{
APIServiceKey: apiServiceKey,
FilePath: path,
}
httpClient := httpMockCpis{CPIFunction: "ApiProxyArtifactFail", ResponseBody: ``, TestType: "NegativeApiProxyArtifactUploadResBody"}
uploadErr := runApiProxyUpload(&config, nil, &filesMock, &httpClient)
assert.EqualError(t, uploadErr, "not valid zip archive")
})
}

View File

@@ -25,6 +25,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"abapEnvironmentRunAUnitTest": abapEnvironmentRunAUnitTestMetadata(),
"apiKeyValueMapDownload": apiKeyValueMapDownloadMetadata(),
"apiProxyDownload": apiProxyDownloadMetadata(),
"apiProxyUpload": apiProxyUploadMetadata(),
"artifactPrepareVersion": artifactPrepareVersionMetadata(),
"batsExecuteTests": batsExecuteTestsMetadata(),
"checkmarxExecuteScan": checkmarxExecuteScanMetadata(),

View File

@@ -175,6 +175,7 @@ func Execute() {
rootCmd.AddCommand(ShellExecuteCommand())
rootCmd.AddCommand(ApiProxyDownloadCommand())
rootCmd.AddCommand(ApiKeyValueMapDownloadCommand())
rootCmd.AddCommand(ApiProxyUploadCommand())
rootCmd.AddCommand(GradleExecuteBuildCommand())
addRootFlags(rootCmd)