mirror of
https://github.com/SAP/jenkins-library.git
synced 2024-11-28 08:49:44 +02:00
ApiProxyList Command (#3794)
* ApiProxyList Command * CodeReview Fixes * CodeReview Fixes * CodeReview FIxes * CodeReview Fixes * CodeReview FIxes * CodeReview Fixes * fixing unit test * doc fixes * Update documentation/docs/steps/apiProxyList.md Co-authored-by: Oliver Feldmann <oliver.feldmann@sap.com> * CodeReview Fixes * CodeReview Fixes * CodeReview Fixes * codereview fix Co-authored-by: Oliver Feldmann <oliver.feldmann@sap.com>
This commit is contained in:
parent
8768d2bb74
commit
cdea4b7713
58
cmd/apiProxyList.go
Normal file
58
cmd/apiProxyList.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/SAP/jenkins-library/pkg/apim"
|
||||||
|
"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 apiProxyList(config apiProxyListOptions, telemetryData *telemetry.CustomData, commonPipelineEnvironment *apiProxyListCommonPipelineEnvironment) {
|
||||||
|
httpClient := &piperhttp.Client{}
|
||||||
|
err := runApiProxyList(&config, telemetryData, httpClient, commonPipelineEnvironment)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Fatal("step execution failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runApiProxyList(config *apiProxyListOptions, telemetryData *telemetry.CustomData, httpClient piperhttp.Sender, commonPipelineEnvironment *apiProxyListCommonPipelineEnvironment) error {
|
||||||
|
apimData := apim.Bundle{APIServiceKey: config.APIServiceKey, Client: httpClient}
|
||||||
|
err := apim.Utils.InitAPIM(&apimData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return getApiProxyList(config, apimData, commonPipelineEnvironment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getApiProxyList(config *apiProxyListOptions, apistruct apim.Bundle, commonPipelineEnvironment *apiProxyListCommonPipelineEnvironment) error {
|
||||||
|
httpClient := apistruct.Client
|
||||||
|
httpMethod := http.MethodGet
|
||||||
|
odataFilterInputs := apim.OdataParameters{Filter: config.Filter, Search: config.Search,
|
||||||
|
Top: config.Top, Skip: config.Skip, Orderby: config.Orderby,
|
||||||
|
Select: config.Select, Expand: config.Expand}
|
||||||
|
odataFilters, urlErr := apim.OdataUtils.MakeOdataQuery(&odataFilterInputs)
|
||||||
|
if urlErr != nil {
|
||||||
|
return errors.Wrap(urlErr, "failed to create odata filter")
|
||||||
|
}
|
||||||
|
getApiProxyListURL := fmt.Sprintf("%s/apiportal/api/1.0/Management.svc/APIProxies%s", apistruct.Host, odataFilters)
|
||||||
|
header := make(http.Header)
|
||||||
|
header.Add("Accept", "application/json")
|
||||||
|
apiProxyListResp, httpErr := httpClient.SendRequest(httpMethod, getApiProxyListURL, nil, header, nil)
|
||||||
|
failureMessage := "Failed to get List of API Proxy"
|
||||||
|
successMessage := "Successfully retrieved the api proxy list from API Portal"
|
||||||
|
httpGetRequestParameters := cpi.HttpFileUploadRequestParameters{
|
||||||
|
ErrMessage: failureMessage,
|
||||||
|
Response: apiProxyListResp,
|
||||||
|
HTTPMethod: httpMethod,
|
||||||
|
HTTPURL: getApiProxyListURL,
|
||||||
|
HTTPErr: httpErr,
|
||||||
|
SuccessMessage: successMessage}
|
||||||
|
resp, err := cpi.HTTPUploadUtils.HandleHTTPGetRequestResponse(httpGetRequestParameters)
|
||||||
|
commonPipelineEnvironment.custom.APIProxyList = resp
|
||||||
|
return err
|
||||||
|
}
|
282
cmd/apiProxyList_generated.go
Normal file
282
cmd/apiProxyList_generated.go
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
// Code generated by piper's step-generator. DO NOT EDIT.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SAP/jenkins-library/pkg/config"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/log"
|
||||||
|
"github.com/SAP/jenkins-library/pkg/piperenv"
|
||||||
|
"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 apiProxyListOptions struct {
|
||||||
|
APIServiceKey string `json:"apiServiceKey,omitempty"`
|
||||||
|
Top int `json:"top,omitempty"`
|
||||||
|
Skip int `json:"skip,omitempty"`
|
||||||
|
Filter string `json:"filter,omitempty"`
|
||||||
|
Count bool `json:"count,omitempty"`
|
||||||
|
Search string `json:"search,omitempty"`
|
||||||
|
Orderby string `json:"orderby,omitempty"`
|
||||||
|
Select string `json:"select,omitempty"`
|
||||||
|
Expand string `json:"expand,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiProxyListCommonPipelineEnvironment struct {
|
||||||
|
custom struct {
|
||||||
|
APIProxyList string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *apiProxyListCommonPipelineEnvironment) persist(path, resourceName string) {
|
||||||
|
content := []struct {
|
||||||
|
category string
|
||||||
|
name string
|
||||||
|
value interface{}
|
||||||
|
}{
|
||||||
|
{category: "custom", name: "apiProxyList", value: p.custom.APIProxyList},
|
||||||
|
}
|
||||||
|
|
||||||
|
errCount := 0
|
||||||
|
for _, param := range content {
|
||||||
|
err := piperenv.SetResourceParameter(path, resourceName, filepath.Join(param.category, param.name), param.value)
|
||||||
|
if err != nil {
|
||||||
|
log.Entry().WithError(err).Error("Error persisting piper environment.")
|
||||||
|
errCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if errCount > 0 {
|
||||||
|
log.Entry().Error("failed to persist Piper environment")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApiProxyListCommand Get the List of an API Proxy from the API Portal
|
||||||
|
func ApiProxyListCommand() *cobra.Command {
|
||||||
|
const STEP_NAME = "apiProxyList"
|
||||||
|
|
||||||
|
metadata := apiProxyListMetadata()
|
||||||
|
var stepConfig apiProxyListOptions
|
||||||
|
var startTime time.Time
|
||||||
|
var commonPipelineEnvironment apiProxyListCommonPipelineEnvironment
|
||||||
|
var logCollector *log.CollectorHook
|
||||||
|
var splunkClient *splunk.Splunk
|
||||||
|
telemetryClient := &telemetry.Telemetry{}
|
||||||
|
|
||||||
|
var createApiProxyListCmd = &cobra.Command{
|
||||||
|
Use: STEP_NAME,
|
||||||
|
Short: "Get the List of an API Proxy from the API Portal",
|
||||||
|
Long: `With this step you can get list of all API Proxy from the API Portal using the OData API. Learn more about the API Management API for getting list of 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() {
|
||||||
|
commonPipelineEnvironment.persist(GeneralConfig.EnvRootPath, "commonPipelineEnvironment")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
apiProxyList(stepConfig, &stepTelemetryData, &commonPipelineEnvironment)
|
||||||
|
stepTelemetryData.ErrorCode = "0"
|
||||||
|
log.Entry().Info("SUCCESS")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
addApiProxyListFlags(createApiProxyListCmd, &stepConfig)
|
||||||
|
return createApiProxyListCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func addApiProxyListFlags(cmd *cobra.Command, stepConfig *apiProxyListOptions) {
|
||||||
|
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().IntVar(&stepConfig.Top, "top", 0, "Show only the first n items.")
|
||||||
|
cmd.Flags().IntVar(&stepConfig.Skip, "skip", 0, "Skip the first n items.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.Filter, "filter", os.Getenv("PIPER_filter"), "Filter items by property values.")
|
||||||
|
cmd.Flags().BoolVar(&stepConfig.Count, "count", false, "Include count of items.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.Search, "search", os.Getenv("PIPER_search"), "Search items by search phrases.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.Orderby, "orderby", os.Getenv("PIPER_orderby"), "Order by property values.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.Select, "select", os.Getenv("PIPER_select"), "Select properties to be returned.")
|
||||||
|
cmd.Flags().StringVar(&stepConfig.Expand, "expand", os.Getenv("PIPER_expand"), "Expand related entities.")
|
||||||
|
|
||||||
|
cmd.MarkFlagRequired("apiServiceKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve step metadata
|
||||||
|
func apiProxyListMetadata() config.StepData {
|
||||||
|
var theMetaData = config.StepData{
|
||||||
|
Metadata: config.StepMetadata{
|
||||||
|
Name: "apiProxyList",
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Description: "Get the List of an API Proxy 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: "top",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "int",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "skip",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "int",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "filter",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_filter"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "count",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "bool",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "search",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_search"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "orderby",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_orderby"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "select",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_select"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "expand",
|
||||||
|
ResourceRef: []config.ResourceReference{},
|
||||||
|
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
|
||||||
|
Type: "string",
|
||||||
|
Mandatory: false,
|
||||||
|
Aliases: []config.Alias{},
|
||||||
|
Default: os.Getenv("PIPER_expand"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outputs: config.StepOutputs{
|
||||||
|
Resources: []config.StepResources{
|
||||||
|
{
|
||||||
|
Name: "commonPipelineEnvironment",
|
||||||
|
Type: "piperEnvironment",
|
||||||
|
Parameters: []map[string]interface{}{
|
||||||
|
{"name": "custom/apiProxyList"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return theMetaData
|
||||||
|
}
|
17
cmd/apiProxyList_generated_test.go
Normal file
17
cmd/apiProxyList_generated_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApiProxyListCommand(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCmd := ApiProxyListCommand()
|
||||||
|
|
||||||
|
// only high level testing performed - details are tested in step generation procedure
|
||||||
|
assert.Equal(t, "apiProxyList", testCmd.Use, "command name incorrect")
|
||||||
|
|
||||||
|
}
|
54
cmd/apiProxyList_test.go
Normal file
54
cmd/apiProxyList_test.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SAP/jenkins-library/pkg/apim"
|
||||||
|
apimhttp "github.com/SAP/jenkins-library/pkg/apim"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunApiProxyList(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Get API Proxy List successfull test", func(t *testing.T) {
|
||||||
|
config := getDefaultOptionsForApiProxyList()
|
||||||
|
httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 200, ResponseBody: `{"some": "test"}`}
|
||||||
|
seOut := apiProxyListCommonPipelineEnvironment{}
|
||||||
|
apim := apim.Bundle{APIServiceKey: config.APIServiceKey, Client: httpClientMock}
|
||||||
|
// test
|
||||||
|
err := getApiProxyList(&config, apim, &seOut)
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.EqualValues(t, seOut.custom.APIProxyList, "{\"some\": \"test\"}")
|
||||||
|
t.Run("check url", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "/apiportal/api/1.0/Management.svc/APIProxies?filter=isCopy+eq+false&$orderby=name&$skip=1&$top=4", httpClientMock.URL)
|
||||||
|
})
|
||||||
|
t.Run("check method", func(t *testing.T) {
|
||||||
|
assert.Equal(t, "GET", httpClientMock.Method)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Get API Proxy List failed test", func(t *testing.T) {
|
||||||
|
config := getDefaultOptionsForApiProxyList()
|
||||||
|
httpClientMock := &apimhttp.HttpMockAPIM{StatusCode: 400}
|
||||||
|
seOut := apiProxyListCommonPipelineEnvironment{}
|
||||||
|
apim := apim.Bundle{APIServiceKey: config.APIServiceKey, Client: httpClientMock}
|
||||||
|
// test
|
||||||
|
err := getApiProxyList(&config, apim, &seOut)
|
||||||
|
// assert
|
||||||
|
assert.EqualError(t, err, "HTTP GET request to /apiportal/api/1.0/Management.svc/APIProxies?filter=isCopy+eq+false&$orderby=name&$skip=1&$top=4 failed with error: : Bad Request")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultOptionsForApiProxyList() apiProxyListOptions {
|
||||||
|
return apiProxyListOptions{
|
||||||
|
APIServiceKey: apimhttp.GetServiceKey(),
|
||||||
|
Top: 4,
|
||||||
|
Skip: 1,
|
||||||
|
Filter: "isCopy eq false",
|
||||||
|
Orderby: "name",
|
||||||
|
}
|
||||||
|
}
|
@ -30,6 +30,7 @@ func GetAllStepMetadata() map[string]config.StepData {
|
|||||||
"apiProviderDownload": apiProviderDownloadMetadata(),
|
"apiProviderDownload": apiProviderDownloadMetadata(),
|
||||||
"apiProviderUpload": apiProviderUploadMetadata(),
|
"apiProviderUpload": apiProviderUploadMetadata(),
|
||||||
"apiProxyDownload": apiProxyDownloadMetadata(),
|
"apiProxyDownload": apiProxyDownloadMetadata(),
|
||||||
|
"apiProxyList": apiProxyListMetadata(),
|
||||||
"apiProxyUpload": apiProxyUploadMetadata(),
|
"apiProxyUpload": apiProxyUploadMetadata(),
|
||||||
"artifactPrepareVersion": artifactPrepareVersionMetadata(),
|
"artifactPrepareVersion": artifactPrepareVersionMetadata(),
|
||||||
"awsS3Upload": awsS3UploadMetadata(),
|
"awsS3Upload": awsS3UploadMetadata(),
|
||||||
|
@ -187,6 +187,7 @@ func Execute() {
|
|||||||
rootCmd.AddCommand(PythonBuildCommand())
|
rootCmd.AddCommand(PythonBuildCommand())
|
||||||
rootCmd.AddCommand(AzureBlobUploadCommand())
|
rootCmd.AddCommand(AzureBlobUploadCommand())
|
||||||
rootCmd.AddCommand(AwsS3UploadCommand())
|
rootCmd.AddCommand(AwsS3UploadCommand())
|
||||||
|
rootCmd.AddCommand(ApiProxyListCommand())
|
||||||
|
|
||||||
addRootFlags(rootCmd)
|
addRootFlags(rootCmd)
|
||||||
|
|
||||||
|
49
documentation/docs/steps/apiProxyList.md
Normal file
49
documentation/docs/steps/apiProxyList.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# ${docGenStepName}
|
||||||
|
|
||||||
|
## ${docGenDescription}
|
||||||
|
|
||||||
|
With this step, you can retrieve all the API proxies from the API portal. An API Proxy is a concept in API Management, capability of SAP Integration Suite, which anonymizes any HTTP endpoints like REST, OData, or SOAP and enhance it with policies and routes.
|
||||||
|
|
||||||
|
An API proxy is a discrete representation of an API. It is implemented as a set of configuration files, policies, and code snippets that rely on the resource information provided by API Management. For more information, see the document [API Proxy](https://help.sap.com/doc/654e5912ee554d46bcc6347599fb2096/CLOUD/en-US/Unit%2004.3%20-%20API%20Proxy%20-%20API%20Resource.pdf/)
|
||||||
|
|
||||||
|
To consume the ApiProxyList step, proceed as follows:
|
||||||
|
|
||||||
|
* Copy the SAP API management service key from the SAP BTP cockpit. Go to instance and subscriptions → service API Management, API portal, which was created under apiportal-apiaccess plan.
|
||||||
|
* Store your service key created for API Management in the Jenkins server as a secret text.
|
||||||
|
* Create a new Jenkins pipeline designated for the ApiProxyList step.
|
||||||
|
* Execute the pipeline and validate the step exection results as explained in the blog [Integration Suite Piper commands](https://blogs.sap.com/2022/01/05/orking-with-integration-suite-piper-commands/)
|
||||||
|
* Use the ApiProxyList step to get the api proxy list from the API portal.
|
||||||
|
* ApiProxyList only supports GET operation.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
## ${docGenParameters}
|
||||||
|
|
||||||
|
## ${docGenConfiguration}
|
||||||
|
|
||||||
|
## ${docJenkinsPluginDependencies}
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Configuration example for a `Jenkinsfile`:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
apiProxyList script: this
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration example for a YAML file(for example `.pipeline/config.yaml`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
<...>
|
||||||
|
apiProxyList:
|
||||||
|
apimApiServiceKeyCredentialsId: 'MY_API_SERVICE_KEY'
|
||||||
|
Top: MY_API_PROXY_GET_N_ENTITIES
|
||||||
|
Skip: MY_API_PROXY_SKIP_N_ENTITIES
|
||||||
|
Filter: MY_API_PROXY_FILTER_BY_ENTITY_FIELD
|
||||||
|
Orderby: MY_API_PROXY_ORDER_BY_ENTITY_FIELD
|
||||||
|
Count: MY_API_PROXY_ORDER_ENTITY_COUNT
|
||||||
|
Search: MY_API_PROXY_SEARCH_BY_ENTITY_FIELD
|
||||||
|
Select: MY_API_PROXY_SELECT_BY_ENTITY_FIELD
|
||||||
|
Expand: MY_API_PROXY_EXPAND_BY_ENTITY_FIELD
|
||||||
|
```
|
@ -72,6 +72,7 @@ nav:
|
|||||||
- apiKeyValueMapDownload: steps/apiKeyValueMapDownload.md
|
- apiKeyValueMapDownload: steps/apiKeyValueMapDownload.md
|
||||||
- apiKeyValueMapUpload: steps/apiKeyValueMapUpload.md
|
- apiKeyValueMapUpload: steps/apiKeyValueMapUpload.md
|
||||||
- apiProxyDownload: steps/apiProxyDownload.md
|
- apiProxyDownload: steps/apiProxyDownload.md
|
||||||
|
- apiProxyList: steps/apiProxyList.md
|
||||||
- apiProviderDownload: steps/apiProviderDownload.md
|
- apiProviderDownload: steps/apiProviderDownload.md
|
||||||
- apiProviderUpload: steps/apiProviderUpload.md
|
- apiProviderUpload: steps/apiProviderUpload.md
|
||||||
- apiProxyUpload: steps/apiProxyUpload.md
|
- apiProxyUpload: steps/apiProxyUpload.md
|
||||||
|
1
go.mod
1
go.mod
@ -250,6 +250,7 @@ require (
|
|||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
github.com/oracle/oci-go-sdk v13.1.0+incompatible // indirect
|
github.com/oracle/oci-go-sdk v13.1.0+incompatible // indirect
|
||||||
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect
|
github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c // indirect
|
||||||
|
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -1557,6 +1557,8 @@ github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otz
|
|||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
|
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c h1:Gcce/r5tSQeprxswXXOwQ/RBU1bjQWVd9dB7QKoPXBE=
|
||||||
|
github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c/go.mod h1:1iCZ0433JJMecYqCa+TdWA9Pax8MGl4ByuNDZ7eSnQY=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
@ -3,10 +3,12 @@ package apim
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/SAP/jenkins-library/pkg/cpi"
|
"github.com/SAP/jenkins-library/pkg/cpi"
|
||||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||||
"github.com/SAP/jenkins-library/pkg/xsuaa"
|
"github.com/SAP/jenkins-library/pkg/xsuaa"
|
||||||
|
"github.com/pasztorpisti/qs"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +18,18 @@ type Utils interface {
|
|||||||
IsPayloadJSON() bool
|
IsPayloadJSON() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//OdataUtils for apim
|
||||||
|
type OdataUtils interface {
|
||||||
|
MakeOdataQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//OdataParameters struct
|
||||||
|
type OdataParameters struct {
|
||||||
|
Filter, Search string
|
||||||
|
Top, Skip int
|
||||||
|
Orderby, Select, Expand string
|
||||||
|
}
|
||||||
|
|
||||||
//Bundle struct
|
//Bundle struct
|
||||||
type Bundle struct {
|
type Bundle struct {
|
||||||
APIServiceKey, Host, Payload string
|
APIServiceKey, Host, Payload string
|
||||||
@ -51,3 +65,15 @@ func (apim *Bundle) IsPayloadJSON() bool {
|
|||||||
var js json.RawMessage
|
var js json.RawMessage
|
||||||
return json.Unmarshal([]byte(apim.Payload), &js) == nil
|
return json.Unmarshal([]byte(apim.Payload), &js) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (odataFilters *OdataParameters) MakeOdataQuery() (string, error) {
|
||||||
|
|
||||||
|
customMarshaler := qs.NewMarshaler(&qs.MarshalOptions{
|
||||||
|
DefaultMarshalPresence: qs.OmitEmpty,
|
||||||
|
})
|
||||||
|
values, encodeErr := customMarshaler.Marshal(odataFilters)
|
||||||
|
if encodeErr == nil && len(values) > 0 {
|
||||||
|
values = "?" + strings.ReplaceAll(values, "&", "&$")
|
||||||
|
}
|
||||||
|
return values, encodeErr
|
||||||
|
}
|
||||||
|
27
pkg/apim/APIMUtility_test.go
Normal file
27
pkg/apim/APIMUtility_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package apim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOdataQueryInitExisting(t *testing.T) {
|
||||||
|
t.Run("MakeOdataQuery- Success Test", func(t *testing.T) {
|
||||||
|
odataFilterInputs := OdataParameters{Filter: "isCopy eq false", Search: "",
|
||||||
|
Top: 4, Skip: 1, Orderby: "name",
|
||||||
|
Select: "", Expand: ""}
|
||||||
|
odataFilters, err := OdataUtils.MakeOdataQuery(&odataFilterInputs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "?filter=isCopy+eq+false&$orderby=name&$skip=1&$top=4", odataFilters)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("MakeOdataQuery- empty odata filters Test", func(t *testing.T) {
|
||||||
|
odataFilterInputs := OdataParameters{Filter: "", Search: "",
|
||||||
|
Top: 0, Skip: 0, Orderby: "",
|
||||||
|
Select: "", Expand: ""}
|
||||||
|
odataFilters, err := OdataUtils.MakeOdataQuery(&odataFilterInputs)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "", odataFilters)
|
||||||
|
})
|
||||||
|
}
|
@ -30,6 +30,7 @@ type HttpCPIUtils interface {
|
|||||||
//HTTPUploadUtils for CPI
|
//HTTPUploadUtils for CPI
|
||||||
type HTTPUploadUtils interface {
|
type HTTPUploadUtils interface {
|
||||||
HandleHTTPFileUploadResponse() error
|
HandleHTTPFileUploadResponse() error
|
||||||
|
HandleHTTPGetRequestResponse() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TokenParameters struct
|
//TokenParameters struct
|
||||||
@ -190,3 +191,32 @@ func (httpFileUploadRequestParameters HttpFileUploadRequestParameters) HandleHTT
|
|||||||
}
|
}
|
||||||
return errors.Errorf("%s, Response Status code: %v", httpFileUploadRequestParameters.ErrMessage, response.StatusCode)
|
return errors.Errorf("%s, Response Status code: %v", httpFileUploadRequestParameters.ErrMessage, response.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleHTTPGetRequestResponse - Handle the GET Request response data
|
||||||
|
func (httpGetRequestParameters HttpFileUploadRequestParameters) HandleHTTPGetRequestResponse() (string, error) {
|
||||||
|
response := httpGetRequestParameters.Response
|
||||||
|
httpErr := httpGetRequestParameters.HTTPErr
|
||||||
|
if response != nil && response.Body != nil {
|
||||||
|
defer response.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
return "", errors.Errorf("did not retrieve a HTTP response: %v", httpErr)
|
||||||
|
}
|
||||||
|
if response.StatusCode == http.StatusOK {
|
||||||
|
responseBody, readErr := ioutil.ReadAll(response.Body)
|
||||||
|
if readErr != nil {
|
||||||
|
return "", errors.Wrapf(readErr, "HTTP response body could not be read, response status code: %v", response.StatusCode)
|
||||||
|
}
|
||||||
|
return string(responseBody), nil
|
||||||
|
}
|
||||||
|
if httpErr != nil {
|
||||||
|
responseBody, readErr := ioutil.ReadAll(response.Body)
|
||||||
|
if readErr != nil {
|
||||||
|
return "", errors.Wrapf(readErr, "HTTP response body could not be read, Response status code: %v", response.StatusCode)
|
||||||
|
}
|
||||||
|
log.Entry().Errorf("a HTTP error occurred! Response body: %v, Response status code: %v", string(responseBody), response.StatusCode)
|
||||||
|
return "", errors.Wrapf(httpErr, "HTTP %v request to %v failed with error: %v", httpGetRequestParameters.HTTPMethod, httpGetRequestParameters.HTTPURL, string(responseBody))
|
||||||
|
}
|
||||||
|
return "", errors.Errorf("%s, Response Status code: %v", httpGetRequestParameters.ErrMessage, response.StatusCode)
|
||||||
|
}
|
||||||
|
94
resources/metadata/apiProxyList.yaml
Normal file
94
resources/metadata/apiProxyList.yaml
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
metadata:
|
||||||
|
name: apiProxyList
|
||||||
|
description: Get the List of an API Proxy from the API Portal
|
||||||
|
longDescription: |
|
||||||
|
With this step you can get list of all API Proxy from the API Portal using the OData API. Learn more about the API Management API for getting list of an API proxy 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: top
|
||||||
|
type: int
|
||||||
|
description: Show only the first n items.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: skip
|
||||||
|
type: int
|
||||||
|
description: Skip the first n items.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: filter
|
||||||
|
type: string
|
||||||
|
description: Filter items by property values.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: count
|
||||||
|
type: bool
|
||||||
|
description: Include count of items.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: search
|
||||||
|
type: string
|
||||||
|
description: Search items by search phrases.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: orderby
|
||||||
|
type: string
|
||||||
|
description: Order by property values.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: select
|
||||||
|
type: string
|
||||||
|
description: Select properties to be returned.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
- name: expand
|
||||||
|
type: string
|
||||||
|
description: Expand related entities.
|
||||||
|
scope:
|
||||||
|
- PARAMETERS
|
||||||
|
- STAGES
|
||||||
|
- STEPS
|
||||||
|
mandatory: false
|
||||||
|
outputs:
|
||||||
|
resources:
|
||||||
|
- name: commonPipelineEnvironment
|
||||||
|
type: piperEnvironment
|
||||||
|
params:
|
||||||
|
- name: custom/apiProxyList
|
@ -217,6 +217,8 @@ public class CommonStepsTest extends BasePiperTest{
|
|||||||
'apiKeyValueMapUpload', //implementing new golang pattern without fields
|
'apiKeyValueMapUpload', //implementing new golang pattern without fields
|
||||||
'apiProviderUpload', //implementing new golang pattern without fields
|
'apiProviderUpload', //implementing new golang pattern without fields
|
||||||
'pythonBuild', //implementing new golang pattern without fields
|
'pythonBuild', //implementing new golang pattern without fields
|
||||||
|
'awsS3Upload',
|
||||||
|
'apiProxyList', //implementing new golang pattern without fields
|
||||||
'azureBlobUpload',
|
'azureBlobUpload',
|
||||||
'awsS3Upload'
|
'awsS3Upload'
|
||||||
]
|
]
|
||||||
|
11
vars/apiProxyList.groovy
Normal file
11
vars/apiProxyList.groovy
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import groovy.transform.Field
|
||||||
|
|
||||||
|
@Field String STEP_NAME = getClass().getName()
|
||||||
|
@Field String METADATA_FILE = 'metadata/apiProxyList.yaml'
|
||||||
|
|
||||||
|
void call(Map parameters = [:]) {
|
||||||
|
List credentials = [
|
||||||
|
[type: 'token', id: 'apimApiServiceKeyCredentialsId', env: ['PIPER_apiServiceKey']]
|
||||||
|
]
|
||||||
|
piperExecuteBin(parameters, STEP_NAME, METADATA_FILE, credentials)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user