1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2025-03-25 21:39:13 +02:00

ApiKeyValueMapDownload Command ()

* ApiKeyValueMapDownload Command

* CodeReview Fixes
This commit is contained in:
Mayur Belur Mohan 2021-11-15 18:18:14 +05:30 committed by GitHub
parent cf62c35b1a
commit d3b090e63e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 424 additions and 1 deletions

@ -0,0 +1,81 @@
package cmd
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"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/telemetry"
"github.com/pkg/errors"
)
func apiKeyValueMapDownload(config apiKeyValueMapDownloadOptions, 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{}
// 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 := runApiKeyValueMapDownload(&config, telemetryData, httpClient)
if err != nil {
log.Entry().WithError(err).Fatal("step execution failed")
}
}
func runApiKeyValueMapDownload(config *apiKeyValueMapDownloadOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender) error {
clientOptions := piperhttp.ClientOptions{}
header := make(http.Header)
header.Add("Accept", "application/json")
serviceKey, err := cpi.ReadCpiServiceKey(config.APIServiceKey)
if err != nil {
return err
}
downloadkeyValueMapArtifactURL := fmt.Sprintf("%s/apiportal/api/1.0/Management.svc/KeyMapEntries('%s')", serviceKey.OAuth.Host, config.KeyValueMapName)
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)
httpMethod := http.MethodGet
downloadResp, httpErr := httpClient.SendRequest(httpMethod, downloadkeyValueMapArtifactURL, nil, header, nil)
if httpErr != nil {
return errors.Wrapf(httpErr, "HTTP %v request to %v failed with error", httpMethod, downloadkeyValueMapArtifactURL)
}
if downloadResp == nil {
return errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
}
if downloadResp != nil && downloadResp.Body != nil {
defer downloadResp.Body.Close()
}
if downloadResp.StatusCode == 200 {
csvFilePath := config.DownloadPath
file, err := os.Create(csvFilePath)
if err != nil {
return errors.Wrapf(err, "Failed to create api key value map CSV file")
}
_, err = io.Copy(file, downloadResp.Body)
if err != nil {
return err
}
return nil
}
responseBody, readErr := ioutil.ReadAll(downloadResp.Body)
if readErr != nil {
return errors.Wrapf(readErr, "HTTP response body could not be read, Response status code : %v", downloadResp.StatusCode)
}
log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code : %v", responseBody, downloadResp.StatusCode)
return errors.Errorf("api Key value map download failed, Response Status code: %v", downloadResp.StatusCode)
}

@ -0,0 +1,171 @@
// 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 apiKeyValueMapDownloadOptions struct {
APIServiceKey string `json:"apiServiceKey,omitempty"`
KeyValueMapName string `json:"keyValueMapName,omitempty"`
DownloadPath string `json:"downloadPath,omitempty"`
}
// ApiKeyValueMapDownloadCommand Download a specific Key Value Map from the API Portal
func ApiKeyValueMapDownloadCommand() *cobra.Command {
const STEP_NAME = "apiKeyValueMapDownload"
metadata := apiKeyValueMapDownloadMetadata()
var stepConfig apiKeyValueMapDownloadOptions
var startTime time.Time
var logCollector *log.CollectorHook
var createApiKeyValueMapDownloadCmd = &cobra.Command{
Use: STEP_NAME,
Short: "Download a specific Key Value Map from the API Portal",
Long: `With this step you can download a specific Key Value Map from the API Portal, which returns a zip file with the Key Value Map contents in to current workspace using the OData API.
Learn more about the SAP API Management API for downloading an Key Value Map 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 {
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) {
telemetryData := telemetry.CustomData{}
telemetryData.ErrorCode = "1"
handler := func() {
config.RemoveVaultSecretFiles()
telemetryData.Duration = fmt.Sprintf("%v", time.Since(startTime).Milliseconds())
telemetryData.ErrorCategory = log.GetErrorCategory().String()
telemetry.Send(&telemetryData)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Send(&telemetryData, logCollector)
}
}
log.DeferExitHandler(handler)
defer handler()
telemetry.Initialize(GeneralConfig.NoTelemetry, STEP_NAME)
if len(GeneralConfig.HookConfig.SplunkConfig.Dsn) > 0 {
splunk.Initialize(GeneralConfig.CorrelationID,
GeneralConfig.HookConfig.SplunkConfig.Dsn,
GeneralConfig.HookConfig.SplunkConfig.Token,
GeneralConfig.HookConfig.SplunkConfig.Index,
GeneralConfig.HookConfig.SplunkConfig.SendLogs)
}
apiKeyValueMapDownload(stepConfig, &telemetryData)
telemetryData.ErrorCode = "0"
log.Entry().Info("SUCCESS")
},
}
addApiKeyValueMapDownloadFlags(createApiKeyValueMapDownloadCmd, &stepConfig)
return createApiKeyValueMapDownloadCmd
}
func addApiKeyValueMapDownloadFlags(cmd *cobra.Command, stepConfig *apiKeyValueMapDownloadOptions) {
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.KeyValueMapName, "keyValueMapName", os.Getenv("PIPER_keyValueMapName"), "Specifies the name of the Key Value Map.")
cmd.Flags().StringVar(&stepConfig.DownloadPath, "downloadPath", os.Getenv("PIPER_downloadPath"), "Specifies Key Value Map download CSV file location.")
cmd.MarkFlagRequired("apiServiceKey")
cmd.MarkFlagRequired("keyValueMapName")
cmd.MarkFlagRequired("downloadPath")
}
// retrieve step metadata
func apiKeyValueMapDownloadMetadata() config.StepData {
var theMetaData = config.StepData{
Metadata: config.StepMetadata{
Name: "apiKeyValueMapDownload",
Aliases: []config.Alias{},
Description: "Download a specific Key Value Map from 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: "keyValueMapName",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_keyValueMapName"),
},
{
Name: "downloadPath",
ResourceRef: []config.ResourceReference{},
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
Aliases: []config.Alias{},
Default: os.Getenv("PIPER_downloadPath"),
},
},
},
},
}
return theMetaData
}

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

@ -0,0 +1,68 @@
package cmd
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRunApiKeyValueMapDownload(t *testing.T) {
t.Parallel()
t.Run("Successfull Download of API Key Value Map", func(t *testing.T) {
file, err := ioutil.TempFile("", "CustKVM.json")
if err != nil {
t.FailNow()
}
defer os.RemoveAll(file.Name()) // clean up
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiKeyValueMapDownloadOptions{
APIServiceKey: apiServiceKey,
KeyValueMapName: "CustKVM",
DownloadPath: file.Name(),
}
httpClient := httpMockCpis{CPIFunction: "APIKeyValueMapDownload", ResponseBody: ``, TestType: "Positive"}
errResp := runApiKeyValueMapDownload(&config, nil, &httpClient)
if assert.NoError(t, errResp) {
t.Run("check file", func(t *testing.T) {
assert.Equal(t, fileExists(file.Name()), true)
})
t.Run("check url", func(t *testing.T) {
assert.Equal(t, "https://demo/apiportal/api/1.0/Management.svc/KeyMapEntries('CustKVM')", httpClient.URL)
})
t.Run("check method", func(t *testing.T) {
assert.Equal(t, "GET", httpClient.Method)
})
}
})
t.Run("Failed case of API Key Value Map Download", func(t *testing.T) {
apiServiceKey := `{
"oauth": {
"url": "https://demo",
"clientid": "demouser",
"clientsecret": "******",
"tokenurl": "https://demo/oauth/token"
}
}`
config := apiKeyValueMapDownloadOptions{
APIServiceKey: apiServiceKey,
KeyValueMapName: "CustKVM",
DownloadPath: "",
}
httpClient := httpMockCpis{CPIFunction: "APIKeyValueMapDownloadFailure", ResponseBody: ``, TestType: "Negative"}
err := runApiKeyValueMapDownload(&config, nil, &httpClient)
assert.EqualError(t, err, "HTTP GET request to https://demo/apiportal/api/1.0/Management.svc/KeyMapEntries('CustKVM') failed with error: Service not Found")
})
}

@ -22,6 +22,7 @@ func GetAllStepMetadata() map[string]config.StepData {
"abapEnvironmentPullGitRepo": abapEnvironmentPullGitRepoMetadata(),
"abapEnvironmentRunATCCheck": abapEnvironmentRunATCCheckMetadata(),
"abapEnvironmentRunAUnitTest": abapEnvironmentRunAUnitTestMetadata(),
"apiKeyValueMapDownload": apiKeyValueMapDownloadMetadata(),
"apiProxyDownload": apiProxyDownloadMetadata(),
"batsExecuteTests": batsExecuteTestsMetadata(),
"checkmarxExecuteScan": checkmarxExecuteScanMetadata(),

@ -167,6 +167,7 @@ func Execute() {
rootCmd.AddCommand(AbapEnvironmentRunAUnitTestCommand())
rootCmd.AddCommand(CheckStepActiveCommand())
rootCmd.AddCommand(ApiProxyDownloadCommand())
rootCmd.AddCommand(ApiKeyValueMapDownloadCommand())
addRootFlags(rootCmd)

@ -0,0 +1,30 @@
# ${docGenStepName}
## ${docGenDescription}
## Prerequisites
## ${docGenParameters}
## ${docGenConfiguration}
## ${docJenkinsPluginDependencies}
## Example
Example configuration for the use in a `Jenkinsfile`.
```groovy
apiKeyValueMapDownload script: this
```
Example for the use in a YAML configuration file (such as `.pipeline/config.yaml`).
```yaml
steps:
<...>
apiKeyValueMapDownload:
apimApiServiceKeyCredentialsId: 'MY_API_SERVICE_KEY'
apiProxyName: 'MY_API_KEY_VALUE_MAP_NAME'
downloadPath: MY_API_KEY_VALUE_MAP_CSV_FILE_DOWNLOAD_PATH
```

@ -65,6 +65,7 @@ nav:
- abapEnvironmentPullGitRepo: steps/abapEnvironmentPullGitRepo.md
- abapEnvironmentRunATCCheck: steps/abapEnvironmentRunATCCheck.md
- abapEnvironmentRunAUnitTest: steps/abapEnvironmentRunAUnitTest.md
- apiKeyValueMapDownload: steps/apiKeyValueMapDownload.md
- apiProxyDownload: steps/apiProxyDownload.md
- artifactPrepareVersion: steps/artifactPrepareVersion.md
- batsExecuteTests: steps/batsExecuteTests.md

@ -26,7 +26,7 @@ func GetCPIFunctionMockResponse(functionName, testType string) (*http.Response,
return GetNegativeCaseHTTPResponseBodyAndErrorNil()
}
return GetParameterKeyMissingResponseBody()
case "IntegrationArtifactGetMplStatus":
case "IntegrationArtifactGetMplStatus", "APIKeyValueMapDownload":
return GetIntegrationArtifactGetMplStatusCommandMockResponse(testType)
case "IntegrationArtifactGetServiceEndpoint":
return GetIntegrationArtifactGetServiceEndpointCommandMockResponse(testType)

@ -0,0 +1,41 @@
metadata:
name: apiKeyValueMapDownload
description: Download a specific Key Value Map from the API Portal
longDescription: |
With this step you can download a specific Key Value Map from the API Portal, which returns a zip file with the Key Value Map contents in to current workspace using the OData API.
Learn more about the SAP API Management API for downloading an Key Value Map artifact [here](https://help.sap.com/viewer/66d066d903c2473f81ec33acfe2ccdb4/Cloud/en-US/e26b3320cd534ae4bc743af8013a8abb.html).
spec:
inputs:
secrets:
- name: apimApiServiceKeyCredentialsId
description: Jenkins secret text credential ID containing the service key to the API Management Runtime service instance of plan 'api'
type: jenkins
params:
- name: apiServiceKey
type: string
description: Service key JSON string to access the API Management Runtime service instance of plan 'api'
scope:
- PARAMETERS
mandatory: true
secret: true
resourceRef:
- name: apimApiServiceKeyCredentialsId
type: secret
param: apiServiceKey
- name: keyValueMapName
type: string
description: Specifies the name of the Key Value Map.
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true
- name: downloadPath
type: string
description: Specifies Key Value Map download CSV file location.
scope:
- PARAMETERS
- STAGES
- STEPS
mandatory: true

@ -202,6 +202,7 @@ public class CommonStepsTest extends BasePiperTest{
'transportRequestUploadCTS', //implementing new golang pattern without fields
'isChangeInDevelopment', //implementing new golang pattern without fields
'apiProxyDownload', //implementing new golang pattern without fields
'apiKeyValueMapDownload', //implementing new golang pattern without fields
]
@Test

@ -0,0 +1,11 @@
import groovy.transform.Field
@Field String STEP_NAME = getClass().getName()
@Field String METADATA_FILE = 'metadata/apiKeyValueMapDownload.yaml'
void call(Map parameters = [:]) {
List credentials = [
[type: 'token', id: 'apimApiServiceKeyCredentialsId', env: ['PIPER_apiServiceKey']]
]
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials)
}