You've already forked sap-jenkins-library
mirror of
https://github.com/SAP/jenkins-library.git
synced 2025-11-06 09:09:19 +02:00
[ABAP] Refactor steps to allow API migration (#4687)
* Initial API Manager * Intermediate part * Intermediate step * Fix utils tests * Adapt pull * Migrate Checkout * Refactor createTags * Refactoring * Setup tests for SAP_COM_0510 * Add tests * Refactor parsing * Add retry to clone * refactor * Refactor and tests * Fix function call * Adapt create tag tests * Adapt tests * Add tests * Fix tests * Fix test * Fix client mock * Add unit test comments * Add missing parameters * Branch not mandatory for clone * Improve switch branch trigger --------- Co-authored-by: tiloKo <70266685+tiloKo@users.noreply.github.com>
This commit is contained in:
@@ -167,6 +167,9 @@ func ReadConfigFile(path string) (file []byte, err error) {
|
||||
// GetHTTPResponse wraps the SendRequest function of piperhttp
|
||||
func GetHTTPResponse(requestType string, connectionDetails ConnectionDetailsHTTP, body []byte, client piperhttp.Sender) (*http.Response, error) {
|
||||
|
||||
log.Entry().Debugf("Request body: %s", string(body))
|
||||
log.Entry().Debugf("Request user: %s", connectionDetails.User)
|
||||
|
||||
header := make(map[string][]string)
|
||||
header["Content-Type"] = []string{"application/json"}
|
||||
header["Accept"] = []string{"application/json"}
|
||||
@@ -182,16 +185,20 @@ func GetHTTPResponse(requestType string, connectionDetails ConnectionDetailsHTTP
|
||||
// Further error details may be present in the response body of the HTTP response.
|
||||
// If the response body is parseable, the included details are wrapped around the original error from the HTTP repsponse.
|
||||
// If this is not possible, the original error is returned.
|
||||
func HandleHTTPError(resp *http.Response, err error, message string, connectionDetails ConnectionDetailsHTTP) error {
|
||||
func HandleHTTPError(resp *http.Response, err error, message string, connectionDetails ConnectionDetailsHTTP) (string, error) {
|
||||
|
||||
var errorText string
|
||||
var errorCode string
|
||||
var parsingError error
|
||||
if resp == nil {
|
||||
// Response is nil in case of a timeout
|
||||
log.Entry().WithError(err).WithField("ABAP Endpoint", connectionDetails.URL).Error("Request failed")
|
||||
|
||||
match, _ := regexp.MatchString(".*EOF$", err.Error())
|
||||
if match {
|
||||
AddDefaultDashedLine()
|
||||
AddDefaultDashedLine(1)
|
||||
log.Entry().Infof("%s", "A connection could not be established to the ABAP system. The typical root cause is the network configuration (firewall, IP allowlist, etc.)")
|
||||
AddDefaultDashedLine()
|
||||
AddDefaultDashedLine(1)
|
||||
}
|
||||
|
||||
log.Entry().Infof("Error message: %s,", err.Error())
|
||||
@@ -201,15 +208,15 @@ func HandleHTTPError(resp *http.Response, err error, message string, connectionD
|
||||
|
||||
log.Entry().WithField("StatusCode", resp.Status).WithField("User", connectionDetails.User).WithField("URL", connectionDetails.URL).Error(message)
|
||||
|
||||
errorText, errorCode, parsingError := GetErrorDetailsFromResponse(resp)
|
||||
errorText, errorCode, parsingError = GetErrorDetailsFromResponse(resp)
|
||||
if parsingError != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
abapError := errors.New(fmt.Sprintf("%s - %s", errorCode, errorText))
|
||||
err = errors.Wrap(abapError, err.Error())
|
||||
|
||||
}
|
||||
return err
|
||||
return errorCode, err
|
||||
}
|
||||
|
||||
func GetErrorDetailsFromResponse(resp *http.Response) (errorString string, errorCode string, err error) {
|
||||
@@ -249,8 +256,10 @@ func ConvertTime(logTimeStamp string) time.Time {
|
||||
}
|
||||
|
||||
// AddDefaultDashedLine adds 25 dashes
|
||||
func AddDefaultDashedLine() {
|
||||
log.Entry().Infof(strings.Repeat("-", 25))
|
||||
func AddDefaultDashedLine(j int) {
|
||||
for i := 1; i <= j; i++ {
|
||||
log.Entry().Infof(strings.Repeat("-", 25))
|
||||
}
|
||||
}
|
||||
|
||||
// AddDefaultDebugLine adds 25 dashes in debug
|
||||
@@ -370,6 +379,7 @@ type ClientMock struct {
|
||||
Error error
|
||||
NilResponse bool
|
||||
ErrorInsteadOfDump bool
|
||||
ErrorList []error
|
||||
}
|
||||
|
||||
// SetOptions sets clientOptions for a client mock
|
||||
@@ -383,8 +393,10 @@ func (c *ClientMock) SendRequest(method, url string, bdy io.Reader, hdr http.Hea
|
||||
}
|
||||
|
||||
var body []byte
|
||||
var responseError error
|
||||
if c.Body != "" {
|
||||
body = []byte(c.Body)
|
||||
responseError = c.Error
|
||||
} else {
|
||||
if c.ErrorInsteadOfDump && len(c.BodyList) == 0 {
|
||||
return nil, errors.New("No more bodies in the list")
|
||||
@@ -392,6 +404,12 @@ func (c *ClientMock) SendRequest(method, url string, bdy io.Reader, hdr http.Hea
|
||||
bodyString := c.BodyList[len(c.BodyList)-1]
|
||||
c.BodyList = c.BodyList[:len(c.BodyList)-1]
|
||||
body = []byte(bodyString)
|
||||
if len(c.ErrorList) == 0 {
|
||||
responseError = c.Error
|
||||
} else {
|
||||
responseError = c.ErrorList[len(c.ErrorList)-1]
|
||||
c.ErrorList = c.ErrorList[:len(c.ErrorList)-1]
|
||||
}
|
||||
}
|
||||
header := http.Header{}
|
||||
header.Set("X-Csrf-Token", c.Token)
|
||||
@@ -399,7 +417,7 @@ func (c *ClientMock) SendRequest(method, url string, bdy io.Reader, hdr http.Hea
|
||||
StatusCode: c.StatusCode,
|
||||
Header: header,
|
||||
Body: io.NopCloser(bytes.NewReader(body)),
|
||||
}, c.Error
|
||||
}, responseError
|
||||
}
|
||||
|
||||
// DownloadFile : Empty file download
|
||||
|
||||
@@ -309,7 +309,7 @@ func TestHandleHTTPError(t *testing.T) {
|
||||
receivedErr := errors.New(errorValue)
|
||||
message := "Custom Error Message"
|
||||
|
||||
err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
_, err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
assert.EqualError(t, err, fmt.Sprintf("%s: %s - %s", receivedErr.Error(), abapErrorCode, abapErrorMessage))
|
||||
log.Entry().Info(err.Error())
|
||||
})
|
||||
@@ -328,7 +328,7 @@ func TestHandleHTTPError(t *testing.T) {
|
||||
receivedErr := errors.New(errorValue)
|
||||
message := "Custom Error Message"
|
||||
|
||||
err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
_, err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
assert.EqualError(t, err, fmt.Sprintf("%s", receivedErr.Error()))
|
||||
log.Entry().Info(err.Error())
|
||||
})
|
||||
@@ -347,7 +347,7 @@ func TestHandleHTTPError(t *testing.T) {
|
||||
receivedErr := errors.New(errorValue)
|
||||
message := "Custom Error Message"
|
||||
|
||||
err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
_, err := HandleHTTPError(&resp, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
assert.EqualError(t, err, fmt.Sprintf("%s", receivedErr.Error()))
|
||||
log.Entry().Info(err.Error())
|
||||
})
|
||||
@@ -361,7 +361,7 @@ func TestHandleHTTPError(t *testing.T) {
|
||||
_, hook := test.NewNullLogger()
|
||||
log.RegisterHook(hook)
|
||||
|
||||
err := HandleHTTPError(nil, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
_, err := HandleHTTPError(nil, receivedErr, message, ConnectionDetailsHTTP{})
|
||||
|
||||
assert.EqualError(t, err, fmt.Sprintf("%s", receivedErr.Error()))
|
||||
assert.Equal(t, 5, len(hook.Entries), "Expected a different number of entries")
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
package abaputils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const failureMessageClonePull = "Could not pull the Repository / Software Component "
|
||||
const numberOfEntriesPerPage = 100000
|
||||
const logOutputStatusLength = 10
|
||||
const logOutputTimestampLength = 29
|
||||
|
||||
// PollEntity periodically polls the pull/import entity to get the status. Check if the import is still running
|
||||
func PollEntity(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender, pollIntervall time.Duration) (string, error) {
|
||||
// PollEntity periodically polls the action entity to get the status. Check if the import is still running
|
||||
func PollEntity(api SoftwareComponentApiInterface, pollIntervall time.Duration) (string, error) {
|
||||
|
||||
log.Entry().Info("Start polling the status...")
|
||||
var status string = "R"
|
||||
var statusCode string = "R"
|
||||
var err error
|
||||
|
||||
for {
|
||||
pullEntity, responseStatus, err := GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
|
||||
// pullEntity, responseStatus, err := api.GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
|
||||
statusCode, err = api.GetAction()
|
||||
if err != nil {
|
||||
return status, err
|
||||
return statusCode, err
|
||||
}
|
||||
status = pullEntity.Status
|
||||
log.Entry().WithField("StatusCode", responseStatus).Info("Status: " + pullEntity.Status + " - " + pullEntity.StatusDescription)
|
||||
if pullEntity.Status != "R" && pullEntity.Status != "Q" {
|
||||
|
||||
PrintLogs(repositoryName, connectionDetails, client)
|
||||
if statusCode != "R" && statusCode != "Q" {
|
||||
|
||||
PrintLogs(api)
|
||||
break
|
||||
}
|
||||
time.Sleep(pollIntervall)
|
||||
}
|
||||
return status, nil
|
||||
return statusCode, nil
|
||||
}
|
||||
|
||||
func PrintLogs(repositoryName string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) {
|
||||
connectionDetails.URL = connectionDetails.URL + "?$expand=to_Log_Overview"
|
||||
entity, _, err := GetStatus(failureMessageClonePull+repositoryName, connectionDetails, client)
|
||||
func PrintLogs(api SoftwareComponentApiInterface) {
|
||||
// connectionDetails.URL = connectionDetails.URL + "?$expand=to_Log_Overview"
|
||||
entity, err := api.GetLogOverview()
|
||||
if err != nil || len(entity.ToLogOverview.Results) == 0 {
|
||||
// return if no logs are available
|
||||
return
|
||||
@@ -60,14 +57,14 @@ func PrintLogs(repositoryName string, connectionDetails ConnectionDetailsHTTP, c
|
||||
|
||||
// Print Details
|
||||
for _, logEntryForDetails := range entity.ToLogOverview.Results {
|
||||
printLog(logEntryForDetails, connectionDetails, client)
|
||||
printLog(logEntryForDetails, api)
|
||||
}
|
||||
AddDefaultDashedLine()
|
||||
AddDefaultDashedLine(1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func printOverview(entity PullEntity) {
|
||||
func printOverview(entity ActionEntity) {
|
||||
|
||||
logOutputPhaseLength, logOutputLineLength := calculateLenghts(entity)
|
||||
|
||||
@@ -85,7 +82,7 @@ func printOverview(entity PullEntity) {
|
||||
printDashedLine(logOutputLineLength)
|
||||
}
|
||||
|
||||
func calculateLenghts(entity PullEntity) (int, int) {
|
||||
func calculateLenghts(entity ActionEntity) (int, int) {
|
||||
phaseLength := 22
|
||||
for _, logEntry := range entity.ToLogOverview.Results {
|
||||
if l := len(logEntry.Name); l > phaseLength {
|
||||
@@ -101,24 +98,18 @@ func printDashedLine(i int) {
|
||||
log.Entry().Infof(strings.Repeat("-", i))
|
||||
}
|
||||
|
||||
func printLog(logOverviewEntry LogResultsV2, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) {
|
||||
func printLog(logOverviewEntry LogResultsV2, api SoftwareComponentApiInterface) {
|
||||
|
||||
page := 0
|
||||
|
||||
printHeader(logOverviewEntry)
|
||||
|
||||
for {
|
||||
connectionDetails.URL = logOverviewEntry.ToLogProtocol.Deferred.URI + getLogProtocolQuery(page)
|
||||
entity, err := GetProtocol(failureMessageClonePull, connectionDetails, client)
|
||||
|
||||
printLogProtocolEntries(logOverviewEntry, entity)
|
||||
|
||||
logProtocolEntry, err := api.GetLogProtocol(logOverviewEntry, page)
|
||||
printLogProtocolEntries(logOverviewEntry, logProtocolEntry)
|
||||
page += 1
|
||||
if allLogsHaveBeenPrinted(entity, page, err) {
|
||||
if allLogsHaveBeenPrinted(logProtocolEntry, page, err) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func printLogProtocolEntries(logEntry LogResultsV2, entity LogProtocolResults) {
|
||||
@@ -126,12 +117,10 @@ func printLogProtocolEntries(logEntry LogResultsV2, entity LogProtocolResults) {
|
||||
sort.SliceStable(entity.Results, func(i, j int) bool {
|
||||
return entity.Results[i].ProtocolLine < entity.Results[j].ProtocolLine
|
||||
})
|
||||
|
||||
if logEntry.Status != `Success` {
|
||||
for _, entry := range entity.Results {
|
||||
log.Entry().Info(entry.Description)
|
||||
}
|
||||
|
||||
} else {
|
||||
for _, entry := range entity.Results {
|
||||
log.Entry().Debug(entry.Description)
|
||||
@@ -144,6 +133,8 @@ func allLogsHaveBeenPrinted(entity LogProtocolResults, page int, err error) bool
|
||||
numberOfProtocols, errConversion := strconv.Atoi(entity.Count)
|
||||
if errConversion == nil {
|
||||
allPagesHaveBeenRead = numberOfProtocols <= page*numberOfEntriesPerPage
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
return (err != nil || allPagesHaveBeenRead || reflect.DeepEqual(entity.Results, LogProtocolResults{}))
|
||||
}
|
||||
@@ -151,9 +142,9 @@ func allLogsHaveBeenPrinted(entity LogProtocolResults, page int, err error) bool
|
||||
func printHeader(logEntry LogResultsV2) {
|
||||
if logEntry.Status != `Success` {
|
||||
log.Entry().Infof("\n")
|
||||
AddDefaultDashedLine()
|
||||
AddDefaultDashedLine(1)
|
||||
log.Entry().Infof("%s (%v)", logEntry.Name, ConvertTime(logEntry.Timestamp))
|
||||
AddDefaultDashedLine()
|
||||
AddDefaultDashedLine(1)
|
||||
} else {
|
||||
log.Entry().Debugf("\n")
|
||||
AddDebugDashedLine()
|
||||
@@ -169,65 +160,6 @@ func getLogProtocolQuery(page int) string {
|
||||
return fmt.Sprintf("?$skip=%s&$top=%s&$inlinecount=allpages", fmt.Sprint(skip), fmt.Sprint(top))
|
||||
}
|
||||
|
||||
func GetStatus(failureMessage string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) (body PullEntity, status string, err error) {
|
||||
resp, err := GetHTTPResponse("GET", connectionDetails, nil, client)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
err = HandleHTTPError(resp, err, failureMessage, connectionDetails)
|
||||
if resp != nil {
|
||||
status = resp.Status
|
||||
}
|
||||
return body, status, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, _ := io.ReadAll(resp.Body)
|
||||
|
||||
marshallError := json.Unmarshal(bodyText, &abapResp)
|
||||
if marshallError != nil {
|
||||
return body, status, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
marshallError = json.Unmarshal(*abapResp["d"], &body)
|
||||
if marshallError != nil {
|
||||
return body, status, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(PullEntity{}, body) {
|
||||
log.Entry().WithField("StatusCode", resp.Status).Error(failureMessage)
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
var err = errors.New("Request to ABAP System not successful")
|
||||
return body, resp.Status, err
|
||||
}
|
||||
return body, resp.Status, nil
|
||||
}
|
||||
|
||||
func GetProtocol(failureMessage string, connectionDetails ConnectionDetailsHTTP, client piperhttp.Sender) (body LogProtocolResults, err error) {
|
||||
resp, err := GetHTTPResponse("GET", connectionDetails, nil, client)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
err = HandleHTTPError(resp, err, failureMessage, connectionDetails)
|
||||
return body, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, _ := io.ReadAll(resp.Body)
|
||||
|
||||
marshallError := json.Unmarshal(bodyText, &abapResp)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
marshallError = json.Unmarshal(*abapResp["d"], &body)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// GetRepositories for parsing one or multiple branches and repositories from repositories file or branchName and repositoryName configuration
|
||||
func GetRepositories(config *RepositoriesConfig, branchRequired bool) ([]Repository, error) {
|
||||
var repositories = make([]Repository, 0)
|
||||
@@ -313,118 +245,3 @@ func (repo *Repository) GetPullLogString() (logString string) {
|
||||
logString = "repository / software component '" + repo.Name + "'" + commitOrTag
|
||||
return logString
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Structs for the A4C_A2G_GHA service *
|
||||
****************************************/
|
||||
|
||||
// PullEntity struct for the Pull/Import entity A4C_A2G_GHA_SC_IMP
|
||||
type PullEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
UUID string `json:"uuid"`
|
||||
Namespace string `json:"namepsace"`
|
||||
ScName string `json:"sc_name"`
|
||||
ImportType string `json:"import_type"`
|
||||
BranchName string `json:"branch_name"`
|
||||
StartedByUser string `json:"user_name"`
|
||||
Status string `json:"status"`
|
||||
StatusDescription string `json:"status_descr"`
|
||||
CommitID string `json:"commit_id"`
|
||||
StartTime string `json:"start_time"`
|
||||
ChangeTime string `json:"change_time"`
|
||||
ToExecutionLog AbapLogs `json:"to_Execution_log"`
|
||||
ToTransportLog AbapLogs `json:"to_Transport_log"`
|
||||
ToLogOverview AbapLogsV2 `json:"to_Log_Overview"`
|
||||
}
|
||||
|
||||
// BranchEntity struct for the Branch entity A4C_A2G_GHA_SC_BRANCH
|
||||
type BranchEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
ScName string `json:"sc_name"`
|
||||
Namespace string `json:"namepsace"`
|
||||
BranchName string `json:"branch_name"`
|
||||
ParentBranch string `json:"derived_from"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedOn string `json:"created_on"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CommitID string `json:"commit_id"`
|
||||
CommitMessage string `json:"commit_message"`
|
||||
LastCommitBy string `json:"last_commit_by"`
|
||||
LastCommitOn string `json:"last_commit_on"`
|
||||
}
|
||||
|
||||
// CloneEntity struct for the Clone entity A4C_A2G_GHA_SC_CLONE
|
||||
type CloneEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
UUID string `json:"uuid"`
|
||||
ScName string `json:"sc_name"`
|
||||
BranchName string `json:"branch_name"`
|
||||
ImportType string `json:"import_type"`
|
||||
Namespace string `json:"namepsace"`
|
||||
Status string `json:"status"`
|
||||
StatusDescription string `json:"status_descr"`
|
||||
StartedByUser string `json:"user_name"`
|
||||
StartTime string `json:"start_time"`
|
||||
ChangeTime string `json:"change_time"`
|
||||
}
|
||||
|
||||
// AbapLogs struct for ABAP logs
|
||||
type AbapLogs struct {
|
||||
Results []LogResults `json:"results"`
|
||||
}
|
||||
|
||||
type AbapLogsV2 struct {
|
||||
Results []LogResultsV2 `json:"results"`
|
||||
}
|
||||
|
||||
type LogResultsV2 struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
Index int `json:"log_index"`
|
||||
Name string `json:"log_name"`
|
||||
Status string `json:"type_of_found_issues"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ToLogProtocol LogProtocolDeferred `json:"to_Log_Protocol"`
|
||||
}
|
||||
|
||||
type LogProtocolDeferred struct {
|
||||
Deferred URI `json:"__deferred"`
|
||||
}
|
||||
|
||||
type URI struct {
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
type LogProtocolResults struct {
|
||||
Results []LogProtocol `json:"results"`
|
||||
Count string `json:"__count"`
|
||||
}
|
||||
|
||||
type LogProtocol struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
OverviewIndex int `json:"log_index"`
|
||||
ProtocolLine int `json:"index_no"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"descr"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// LogResults struct for Execution and Transport Log entities A4C_A2G_GHA_SC_LOG_EXE and A4C_A2G_GHA_SC_LOG_TP
|
||||
type LogResults struct {
|
||||
Index string `json:"index_no"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"descr"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// RepositoriesConfig struct for parsing one or multiple branches and repositories configurations
|
||||
type RepositoriesConfig struct {
|
||||
BranchName string
|
||||
CommitID string
|
||||
RepositoryName string
|
||||
RepositoryNames []string
|
||||
Repositories string
|
||||
}
|
||||
|
||||
type EntitySetsForManageGitRepository struct {
|
||||
EntitySets []string `json:"EntitySets"`
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -46,33 +45,25 @@ func TestPollEntity(t *testing.T) {
|
||||
logResultSuccess,
|
||||
`{"d" : { "status" : "S" } }`,
|
||||
`{"d" : { "status" : "R" } }`,
|
||||
`{"d" : { "status" : "Q" } }`,
|
||||
`{}`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
options := AbapEnvironmentOptions{
|
||||
CfAPIEndpoint: "https://api.endpoint.com",
|
||||
CfOrg: "testOrg",
|
||||
CfSpace: "testSpace",
|
||||
CfServiceInstance: "testInstance",
|
||||
CfServiceKeyName: "testServiceKey",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
|
||||
config := AbapEnvironmentCheckoutBranchOptions{
|
||||
AbapEnvOptions: options,
|
||||
RepositoryName: "testRepo1",
|
||||
}
|
||||
|
||||
con := ConnectionDetailsHTTP{
|
||||
User: "MY_USER",
|
||||
Password: "MY_PW",
|
||||
URL: "https://api.endpoint.com/Entity/",
|
||||
XCsrfToken: "MY_TOKEN",
|
||||
}
|
||||
status, _ := PollEntity(config.RepositoryName, con, client, 0)
|
||||
|
||||
swcManager := SoftwareComponentApiManager{Client: client}
|
||||
repo := Repository{Name: "testRepo1"}
|
||||
api, _ := swcManager.GetAPI(con, repo)
|
||||
|
||||
status, _ := PollEntity(api, 0)
|
||||
assert.Equal(t, "S", status)
|
||||
assert.Equal(t, 0, len(client.BodyList), "Not all requests were done")
|
||||
})
|
||||
@@ -87,33 +78,24 @@ func TestPollEntity(t *testing.T) {
|
||||
`{"d" : { "status" : "E" } }`,
|
||||
`{"d" : { "status" : "R" } }`,
|
||||
`{"d" : { "status" : "Q" } }`,
|
||||
`{}`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
options := AbapEnvironmentOptions{
|
||||
CfAPIEndpoint: "https://api.endpoint.com",
|
||||
CfOrg: "testOrg",
|
||||
CfSpace: "testSpace",
|
||||
CfServiceInstance: "testInstance",
|
||||
CfServiceKeyName: "testServiceKey",
|
||||
Username: "testUser",
|
||||
Password: "testPassword",
|
||||
}
|
||||
|
||||
config := AbapEnvironmentCheckoutBranchOptions{
|
||||
AbapEnvOptions: options,
|
||||
RepositoryName: "testRepo1",
|
||||
}
|
||||
|
||||
con := ConnectionDetailsHTTP{
|
||||
User: "MY_USER",
|
||||
Password: "MY_PW",
|
||||
URL: "https://api.endpoint.com/Entity/",
|
||||
XCsrfToken: "MY_TOKEN",
|
||||
}
|
||||
status, _ := PollEntity(config.RepositoryName, con, client, 0)
|
||||
|
||||
swcManager := SoftwareComponentApiManager{Client: client}
|
||||
repo := Repository{Name: "testRepo1"}
|
||||
api, _ := swcManager.GetAPI(con, repo)
|
||||
|
||||
status, _ := PollEntity(api, 0)
|
||||
assert.Equal(t, "E", status)
|
||||
assert.Equal(t, 0, len(client.BodyList), "Not all requests were done")
|
||||
})
|
||||
@@ -318,22 +300,3 @@ func TestCreateRequestBodies(t *testing.T) {
|
||||
assert.Equal(t, `{"sc_name":"/DMO/REPO", "tag_name":"myTag"}`, body, "Expected different body")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetStatus(t *testing.T) {
|
||||
t.Run("Graceful Exit", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
NilResponse: true,
|
||||
Error: errors.New("Backend Error"),
|
||||
StatusCode: 500,
|
||||
}
|
||||
connectionDetails := ConnectionDetailsHTTP{
|
||||
URL: "example.com",
|
||||
}
|
||||
|
||||
_, status, err := GetStatus("failure message", connectionDetails, client)
|
||||
|
||||
assert.Error(t, err, "Expected Error")
|
||||
assert.Equal(t, "", status)
|
||||
})
|
||||
}
|
||||
|
||||
369
pkg/abaputils/sap_com_0510.go
Normal file
369
pkg/abaputils/sap_com_0510.go
Normal file
@@ -0,0 +1,369 @@
|
||||
package abaputils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
"github.com/SAP/jenkins-library/pkg/log"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/utils/strings/slices"
|
||||
)
|
||||
|
||||
type SAP_COM_0510 struct {
|
||||
con ConnectionDetailsHTTP
|
||||
client piperhttp.Sender
|
||||
repository Repository
|
||||
path string
|
||||
cloneEntity string
|
||||
repositoryEntity string
|
||||
tagsEntity string
|
||||
checkoutAction string
|
||||
actionEntity string
|
||||
uuid string
|
||||
failureMessage string
|
||||
maxRetries int
|
||||
retryBaseSleepUnit time.Duration
|
||||
retryMaxSleepTime time.Duration
|
||||
retryAllowedErrorCodes []string
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) init(con ConnectionDetailsHTTP, client piperhttp.Sender, repo Repository) {
|
||||
api.con = con
|
||||
api.client = client
|
||||
api.repository = repo
|
||||
api.path = "/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY"
|
||||
api.cloneEntity = "/Clones"
|
||||
api.repositoryEntity = "/Repositories"
|
||||
api.tagsEntity = "/Tags"
|
||||
api.actionEntity = "/Pull"
|
||||
api.checkoutAction = "/checkout_branch"
|
||||
api.failureMessage = "The action of the Repository / Software Component " + api.repository.Name + " failed"
|
||||
api.maxRetries = 3
|
||||
api.setSleepTimeConfig(1*time.Second, 120*time.Second)
|
||||
api.retryAllowedErrorCodes = append(api.retryAllowedErrorCodes, "A4C_A2G/228")
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) getUUID() string {
|
||||
return api.uuid
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) CreateTag(tag Tag) error {
|
||||
|
||||
if reflect.DeepEqual(Tag{}, tag) {
|
||||
return errors.New("No Tag provided")
|
||||
}
|
||||
|
||||
con := api.con
|
||||
con.URL = api.con.URL + api.path + api.tagsEntity
|
||||
|
||||
requestBodyStruct := CreateTagBody{RepositoryName: api.repository.Name, CommitID: api.repository.CommitID, Tag: tag.TagName, Description: tag.TagDescription}
|
||||
jsonBody, err := json.Marshal(&requestBodyStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.triggerRequest(con, jsonBody)
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) CheckoutBranch() error {
|
||||
|
||||
if api.repository.Name == "" || api.repository.Branch == "" {
|
||||
return fmt.Errorf("Failed to trigger checkout: %w", errors.New("Repository and/or Branch Configuration is empty. Please make sure that you have specified the correct values"))
|
||||
}
|
||||
|
||||
// the request looks like: POST/sap/opu/odata/sap/MANAGE_GIT_REPOSITORY/checkout_branch?branch_name='newBranch'&sc_name=/DMO/GIT_REPOSITORY'
|
||||
checkoutConnectionDetails := api.con
|
||||
checkoutConnectionDetails.URL = api.con.URL + api.path + api.checkoutAction + `?branch_name='` + api.repository.Branch + `'&sc_name='` + api.repository.Name + `'`
|
||||
jsonBody := []byte(``)
|
||||
|
||||
return api.triggerRequest(checkoutConnectionDetails, jsonBody)
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) parseActionResponse(resp *http.Response, err error) (ActionEntity, error) {
|
||||
var body ActionEntity
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, errRead := io.ReadAll(resp.Body)
|
||||
if errRead != nil {
|
||||
return ActionEntity{}, err
|
||||
}
|
||||
if err := json.Unmarshal(bodyText, &abapResp); err != nil {
|
||||
return ActionEntity{}, err
|
||||
}
|
||||
if err := json.Unmarshal(*abapResp["d"], &body); err != nil {
|
||||
return ActionEntity{}, err
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(ActionEntity{}, body) {
|
||||
log.Entry().WithField("StatusCode", resp.Status).WithField("branchName", api.repository.Branch).Error("Could not switch to specified branch")
|
||||
err := errors.New("Request to ABAP System not successful")
|
||||
return ActionEntity{}, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) Pull() error {
|
||||
|
||||
// Trigger the Pull of a Repository
|
||||
if api.repository.Name == "" {
|
||||
return errors.New("An empty string was passed for the parameter 'repositoryName'")
|
||||
}
|
||||
|
||||
pullConnectionDetails := api.con
|
||||
pullConnectionDetails.URL = api.con.URL + api.path + api.actionEntity
|
||||
|
||||
jsonBody := []byte(api.repository.GetPullRequestBody())
|
||||
return api.triggerRequest(pullConnectionDetails, jsonBody)
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) GetLogProtocol(logOverviewEntry LogResultsV2, page int) (body LogProtocolResults, err error) {
|
||||
|
||||
connectionDetails := api.con
|
||||
connectionDetails.URL = logOverviewEntry.ToLogProtocol.Deferred.URI + getLogProtocolQuery(page)
|
||||
resp, err := GetHTTPResponse("GET", connectionDetails, nil, api.client)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
_, err = HandleHTTPError(resp, err, api.failureMessage, connectionDetails)
|
||||
return body, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, _ := io.ReadAll(resp.Body)
|
||||
|
||||
marshallError := json.Unmarshal(bodyText, &abapResp)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
marshallError = json.Unmarshal(*abapResp["d"], &body)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) GetLogOverview() (body ActionEntity, err error) {
|
||||
|
||||
connectionDetails := api.con
|
||||
connectionDetails.URL = api.con.URL + api.path + api.actionEntity + "(uuid=guid'" + api.getUUID() + "')" + "?$expand=to_Log_Overview"
|
||||
resp, err := GetHTTPResponse("GET", connectionDetails, nil, api.client)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
_, err = HandleHTTPError(resp, err, api.failureMessage, connectionDetails)
|
||||
return body, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse response
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, _ := io.ReadAll(resp.Body)
|
||||
|
||||
marshallError := json.Unmarshal(bodyText, &abapResp)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
marshallError = json.Unmarshal(*abapResp["d"], &body)
|
||||
if marshallError != nil {
|
||||
return body, errors.Wrap(marshallError, "Could not parse response from the ABAP Environment system")
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(ActionEntity{}, body) {
|
||||
log.Entry().WithField("StatusCode", resp.Status).Error(api.failureMessage)
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
var err = errors.New("Request to ABAP System not successful")
|
||||
return body, err
|
||||
}
|
||||
|
||||
abapStatusCode := body.Status
|
||||
log.Entry().Info("Status: " + abapStatusCode + " - " + body.StatusDescription)
|
||||
return body, nil
|
||||
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) GetAction() (string, error) {
|
||||
|
||||
connectionDetails := api.con
|
||||
connectionDetails.URL = api.con.URL + api.path + api.actionEntity + "(uuid=guid'" + api.getUUID() + "')"
|
||||
resp, err := GetHTTPResponse("GET", connectionDetails, nil, api.client)
|
||||
if err != nil {
|
||||
log.SetErrorCategory(log.ErrorInfrastructure)
|
||||
_, err = HandleHTTPError(resp, err, api.failureMessage, connectionDetails)
|
||||
return "E", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Parse Response
|
||||
body, parseError := api.parseActionResponse(resp, err)
|
||||
if parseError != nil {
|
||||
return "E", parseError
|
||||
}
|
||||
|
||||
api.uuid = body.UUID
|
||||
|
||||
abapStatusCode := body.Status
|
||||
log.Entry().Info("Status: " + abapStatusCode + " - " + body.StatusDescription)
|
||||
return abapStatusCode, nil
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) GetRepository() (bool, string, error) {
|
||||
|
||||
if api.repository.Name == "" {
|
||||
return false, "", errors.New("An empty string was passed for the parameter 'repositoryName'")
|
||||
}
|
||||
|
||||
swcConnectionDetails := api.con
|
||||
swcConnectionDetails.URL = api.con.URL + api.path + api.repositoryEntity + "('" + strings.Replace(api.repository.Name, "/", "%2F", -1) + "')"
|
||||
resp, err := GetHTTPResponse("GET", swcConnectionDetails, nil, api.client)
|
||||
if err != nil {
|
||||
_, errRepo := HandleHTTPError(resp, err, "Reading the Repository / Software Component failed", api.con)
|
||||
return false, "", errRepo
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var body RepositoryEntity
|
||||
var abapResp map[string]*json.RawMessage
|
||||
bodyText, errRead := io.ReadAll(resp.Body)
|
||||
if errRead != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(bodyText, &abapResp); err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if err := json.Unmarshal(*abapResp["d"], &body); err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if reflect.DeepEqual(RepositoryEntity{}, body) {
|
||||
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", api.repository.Name).WithField("branchName", api.repository.Branch).WithField("commitID", api.repository.CommitID).WithField("Tag", api.repository.Tag).Error("Could not Clone the Repository / Software Component")
|
||||
err := errors.New("Request to ABAP System not successful")
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
if body.AvailOnInst {
|
||||
return true, body.ActiveBranch, nil
|
||||
}
|
||||
return false, "", err
|
||||
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) Clone() error {
|
||||
|
||||
// Trigger the Clone of a Repository
|
||||
if api.repository.Name == "" {
|
||||
return errors.New("An empty string was passed for the parameter 'repositoryName'")
|
||||
}
|
||||
|
||||
cloneConnectionDetails := api.con
|
||||
cloneConnectionDetails.URL = api.con.URL + api.path + api.cloneEntity
|
||||
body := []byte(api.repository.GetCloneRequestBody())
|
||||
|
||||
return api.triggerRequest(cloneConnectionDetails, body)
|
||||
|
||||
}
|
||||
|
||||
func (api *SAP_COM_0510) triggerRequest(cloneConnectionDetails ConnectionDetailsHTTP, jsonBody []byte) error {
|
||||
var err error
|
||||
var body ActionEntity
|
||||
var resp *http.Response
|
||||
var errorCode string
|
||||
|
||||
for i := 0; i <= api.maxRetries; i++ {
|
||||
if i > 0 {
|
||||
sleepTime, err := api.getSleepTime(i + 5)
|
||||
if err != nil {
|
||||
// reached max retry duration
|
||||
break
|
||||
}
|
||||
log.Entry().Infof("Retrying in %s", sleepTime.String())
|
||||
time.Sleep(sleepTime)
|
||||
}
|
||||
resp, err = GetHTTPResponse("POST", cloneConnectionDetails, jsonBody, api.client)
|
||||
if err != nil {
|
||||
errorCode, err = HandleHTTPError(resp, err, "Triggering the action failed", api.con)
|
||||
if slices.Contains(api.retryAllowedErrorCodes, errorCode) {
|
||||
// Error Code allows for retry
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
log.Entry().WithField("StatusCode", resp.Status).WithField("repositoryName", api.repository.Name).WithField("branchName", api.repository.Branch).WithField("commitID", api.repository.CommitID).WithField("Tag", api.repository.Tag).Info("Triggered action of Repository / Software Component")
|
||||
|
||||
body, err = api.parseActionResponse(resp, err)
|
||||
break
|
||||
}
|
||||
api.uuid = body.UUID
|
||||
return err
|
||||
}
|
||||
|
||||
// initialRequest implements SoftwareComponentApiInterface.
|
||||
func (api *SAP_COM_0510) initialRequest() error {
|
||||
// Configuring the HTTP Client and CookieJar
|
||||
cookieJar, errorCookieJar := cookiejar.New(nil)
|
||||
if errorCookieJar != nil {
|
||||
return errors.Wrap(errorCookieJar, "Could not create a Cookie Jar")
|
||||
}
|
||||
|
||||
api.client.SetOptions(piperhttp.ClientOptions{
|
||||
MaxRequestDuration: 180 * time.Second,
|
||||
CookieJar: cookieJar,
|
||||
Username: api.con.User,
|
||||
Password: api.con.Password,
|
||||
})
|
||||
|
||||
headConnection := api.con
|
||||
headConnection.XCsrfToken = "fetch"
|
||||
headConnection.URL = api.con.URL + api.path
|
||||
|
||||
// Loging into the ABAP System - getting the x-csrf-token and cookies
|
||||
resp, err := GetHTTPResponse("HEAD", headConnection, nil, api.client)
|
||||
if err != nil {
|
||||
_, err = HandleHTTPError(resp, err, "Authentication on the ABAP system failed", api.con)
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
log.Entry().WithField("StatusCode", resp.Status).WithField("ABAP Endpoint", api.con).Debug("Authentication on the ABAP system successful")
|
||||
api.con.XCsrfToken = resp.Header.Get("X-Csrf-Token")
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSleepTime Should return the Fibonacci numbers in the define time unit up to the defined maximum duration
|
||||
func (api *SAP_COM_0510) getSleepTime(n int) (time.Duration, error) {
|
||||
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
} else if n == 1 {
|
||||
return 1 * api.retryBaseSleepUnit, nil
|
||||
} else if n < 0 {
|
||||
return 0, errors.New("Negative numbers are not allowed")
|
||||
}
|
||||
var result, i int
|
||||
prev := 0
|
||||
next := 1
|
||||
for i = 2; i <= n; i++ {
|
||||
result = prev + next
|
||||
prev = next
|
||||
next = result
|
||||
}
|
||||
sleepTime := time.Duration(result) * api.retryBaseSleepUnit
|
||||
|
||||
if sleepTime > api.retryMaxSleepTime {
|
||||
return 0, errors.New("Exceeded max sleep time")
|
||||
}
|
||||
return sleepTime, nil
|
||||
}
|
||||
|
||||
// setSleepTimeConfig sets the time unit (seconds, nanoseconds) and the maximum sleep duration
|
||||
func (api *SAP_COM_0510) setSleepTimeConfig(timeUnit time.Duration, maxSleepTime time.Duration) {
|
||||
api.retryBaseSleepUnit = timeUnit
|
||||
api.retryMaxSleepTime = maxSleepTime
|
||||
}
|
||||
483
pkg/abaputils/sap_com_0510_test.go
Normal file
483
pkg/abaputils/sap_com_0510_test.go
Normal file
@@ -0,0 +1,483 @@
|
||||
//go:build unit
|
||||
// +build unit
|
||||
|
||||
package abaputils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var con ConnectionDetailsHTTP
|
||||
var repo Repository
|
||||
|
||||
func init() {
|
||||
|
||||
con.User = "CC_USER"
|
||||
con.Password = "123abc"
|
||||
con.URL = "https://example.com"
|
||||
|
||||
repo.Name = "/DMO/REPO"
|
||||
repo.Branch = "main"
|
||||
|
||||
}
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
t.Run("Test retry success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Software component lifecycle activities in progress. Try again later..."} } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
ErrorList: []error{
|
||||
nil,
|
||||
errors.New("HTTP 400"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 120*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errAction := api.(*SAP_COM_0510).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}"))
|
||||
assert.NoError(t, errAction)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
|
||||
})
|
||||
|
||||
t.Run("Test retry not allowed", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/224", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
ErrorList: []error{
|
||||
nil,
|
||||
errors.New("HTTP 400"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 120*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errAction := api.(*SAP_COM_0510).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}"))
|
||||
assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/224 - Error Text")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
|
||||
})
|
||||
|
||||
t.Run("Test retry maxSleepTime", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
ErrorList: []error{
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 20*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
api.(*SAP_COM_0510).maxRetries = 20
|
||||
|
||||
errAction := api.(*SAP_COM_0510).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}"))
|
||||
assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/228 - Error Text")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
|
||||
assert.Equal(t, 6, len(client.BodyList), "Expected maxSleepTime to limit requests")
|
||||
})
|
||||
|
||||
t.Run("Test retry maxRetries", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Error Text"} } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
ErrorList: []error{
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
errors.New("HTTP 400"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 999*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
api.(*SAP_COM_0510).maxRetries = 3
|
||||
|
||||
errAction := api.(*SAP_COM_0510).triggerRequest(ConnectionDetailsHTTP{User: "CC_USER", Password: "abc123", URL: "https://example.com/path"}, []byte("{}"))
|
||||
assert.ErrorContains(t, errAction, "HTTP 400: A4C_A2G/228 - Error Text")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
|
||||
assert.Equal(t, 5, len(client.BodyList), "Expected maxRetries to limit requests")
|
||||
})
|
||||
|
||||
}
|
||||
func TestClone(t *testing.T) {
|
||||
t.Run("Test Clone Success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errClone := api.Clone()
|
||||
assert.NoError(t, errClone)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Clone Failure", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{ "d" : {} }`,
|
||||
`{ "d" : {} }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 120*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errClone := api.Clone()
|
||||
assert.ErrorContains(t, errClone, "Request to ABAP System not successful")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Clone Retry", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{"error" : { "code" : "A4C_A2G/228", "message" : { "lang" : "de", "value" : "Software component lifecycle activities in progress. Try again later..."} } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
ErrorList: []error{
|
||||
nil,
|
||||
errors.New("HTTP 400"),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
api.setSleepTimeConfig(time.Nanosecond, 120*time.Nanosecond)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errClone := api.Clone()
|
||||
assert.NoError(t, errClone)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPull(t *testing.T) {
|
||||
t.Run("Test Pull Success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errPull := api.Pull()
|
||||
assert.NoError(t, errPull)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Pull Failure", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{ "d" : {} }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errPull := api.Pull()
|
||||
assert.ErrorContains(t, errPull, "Request to ABAP System not successful")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckout(t *testing.T) {
|
||||
t.Run("Test Checkout Success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errCheckout := api.CheckoutBranch()
|
||||
assert.NoError(t, errCheckout)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Checkout Failure", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{ "d" : {} }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errCheckoput := api.CheckoutBranch()
|
||||
assert.ErrorContains(t, errCheckoput, "Request to ABAP System not successful")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetRepo(t *testing.T) {
|
||||
t.Run("Test GetRepo Success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "sc_name" : "testRepo1", "avail_on_inst" : true, "active_branch": "testBranch1" } }`,
|
||||
`{"d" : [] }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
cloned, activeBranch, errAction := api.GetRepository()
|
||||
assert.True(t, cloned)
|
||||
assert.Equal(t, "testBranch1", activeBranch)
|
||||
assert.NoError(t, errAction)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCreateTag(t *testing.T) {
|
||||
t.Run("Test Tag Success", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{"d" : { "status" : "R", "UUID" : "GUID" } }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errCreateTag := api.CreateTag(Tag{TagName: "myTag", TagDescription: "descr"})
|
||||
assert.NoError(t, errCreateTag)
|
||||
assert.Equal(t, "GUID", api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Tag Failure", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{ "d" : {} }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errCreateTag := api.CreateTag(Tag{TagName: "myTag", TagDescription: "descr"})
|
||||
assert.ErrorContains(t, errCreateTag, "Request to ABAP System not successful")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
|
||||
t.Run("Test Tag Empty", func(t *testing.T) {
|
||||
|
||||
client := &ClientMock{
|
||||
BodyList: []string{
|
||||
`{ "d" : {} }`,
|
||||
`{ }`,
|
||||
},
|
||||
Token: "myToken",
|
||||
StatusCode: 200,
|
||||
}
|
||||
|
||||
apiManager := &SoftwareComponentApiManager{Client: client, PollIntervall: 1 * time.Microsecond}
|
||||
|
||||
api, err := apiManager.GetAPI(con, repo)
|
||||
assert.NoError(t, err)
|
||||
assert.IsType(t, &SAP_COM_0510{}, api.(*SAP_COM_0510), "API has wrong type")
|
||||
|
||||
errCreateTag := api.CreateTag(Tag{})
|
||||
assert.ErrorContains(t, errCreateTag, "No Tag provided")
|
||||
assert.Empty(t, api.getUUID(), "API does not cotain correct UUID")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSleepTime(t *testing.T) {
|
||||
t.Run("Test Sleep Time", func(t *testing.T) {
|
||||
|
||||
api := SAP_COM_0510{
|
||||
retryMaxSleepTime: 120 * time.Nanosecond,
|
||||
retryBaseSleepUnit: 1 * time.Nanosecond,
|
||||
}
|
||||
|
||||
expectedResults := make([]time.Duration, 12)
|
||||
expectedResults[0] = 0
|
||||
expectedResults[1] = 1
|
||||
expectedResults[2] = 1
|
||||
expectedResults[3] = 2
|
||||
expectedResults[4] = 3
|
||||
expectedResults[5] = 5
|
||||
expectedResults[6] = 8
|
||||
expectedResults[7] = 13
|
||||
expectedResults[8] = 21
|
||||
expectedResults[9] = 34
|
||||
expectedResults[10] = 55
|
||||
expectedResults[11] = 89
|
||||
results := make([]time.Duration, 12)
|
||||
var err error
|
||||
|
||||
for i := 0; i <= 11; i++ {
|
||||
|
||||
results[i], err = api.getSleepTime(i)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.ElementsMatch(t, expectedResults, results)
|
||||
|
||||
_, err = api.getSleepTime(-10)
|
||||
assert.Error(t, err)
|
||||
|
||||
_, err = api.getSleepTime(12)
|
||||
assert.ErrorContains(t, err, "Exceeded max sleep time")
|
||||
})
|
||||
}
|
||||
194
pkg/abaputils/softwareComponentApiManager.go
Normal file
194
pkg/abaputils/softwareComponentApiManager.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package abaputils
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
piperhttp "github.com/SAP/jenkins-library/pkg/http"
|
||||
)
|
||||
|
||||
type SoftwareComponentApiManagerInterface interface {
|
||||
GetAPI(con ConnectionDetailsHTTP, repo Repository) (SoftwareComponentApiInterface, error)
|
||||
GetPollIntervall() time.Duration
|
||||
}
|
||||
|
||||
type SoftwareComponentApiManager struct {
|
||||
Client piperhttp.Sender
|
||||
PollIntervall time.Duration
|
||||
}
|
||||
|
||||
func (manager *SoftwareComponentApiManager) GetAPI(con ConnectionDetailsHTTP, repo Repository) (SoftwareComponentApiInterface, error) {
|
||||
sap_com_0510 := SAP_COM_0510{}
|
||||
sap_com_0510.init(con, manager.Client, repo)
|
||||
|
||||
// Initialize all APIs, use the one that returns a response
|
||||
// Currently SAP_COM_0510, later SAP_COM_0948
|
||||
err := sap_com_0510.initialRequest()
|
||||
return &sap_com_0510, err
|
||||
}
|
||||
|
||||
func (manager *SoftwareComponentApiManager) GetPollIntervall() time.Duration {
|
||||
if manager.PollIntervall == 0 {
|
||||
manager.PollIntervall = 5 * time.Second
|
||||
}
|
||||
return manager.PollIntervall
|
||||
}
|
||||
|
||||
type SoftwareComponentApiInterface interface {
|
||||
init(con ConnectionDetailsHTTP, client piperhttp.Sender, repo Repository)
|
||||
initialRequest() error
|
||||
setSleepTimeConfig(timeUnit time.Duration, maxSleepTime time.Duration)
|
||||
getSleepTime(n int) (time.Duration, error)
|
||||
getUUID() string
|
||||
Clone() error
|
||||
Pull() error
|
||||
CheckoutBranch() error
|
||||
GetRepository() (bool, string, error)
|
||||
GetAction() (string, error)
|
||||
GetLogOverview() (ActionEntity, error)
|
||||
GetLogProtocol(LogResultsV2, int) (body LogProtocolResults, err error)
|
||||
CreateTag(tag Tag) error
|
||||
}
|
||||
|
||||
/****************************************
|
||||
* Structs for the A4C_A2G_GHA service *
|
||||
****************************************/
|
||||
|
||||
// ActionEntity struct for the Pull/Import entity A4C_A2G_GHA_SC_IMP
|
||||
type ActionEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
UUID string `json:"uuid"`
|
||||
Namespace string `json:"namepsace"`
|
||||
ScName string `json:"sc_name"`
|
||||
ImportType string `json:"import_type"`
|
||||
BranchName string `json:"branch_name"`
|
||||
StartedByUser string `json:"user_name"`
|
||||
Status string `json:"status"`
|
||||
StatusDescription string `json:"status_descr"`
|
||||
CommitID string `json:"commit_id"`
|
||||
StartTime string `json:"start_time"`
|
||||
ChangeTime string `json:"change_time"`
|
||||
ToExecutionLog AbapLogs `json:"to_Execution_log"`
|
||||
ToTransportLog AbapLogs `json:"to_Transport_log"`
|
||||
ToLogOverview AbapLogsV2 `json:"to_Log_Overview"`
|
||||
}
|
||||
|
||||
// BranchEntity struct for the Branch entity A4C_A2G_GHA_SC_BRANCH
|
||||
type BranchEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
ScName string `json:"sc_name"`
|
||||
Namespace string `json:"namepsace"`
|
||||
BranchName string `json:"branch_name"`
|
||||
ParentBranch string `json:"derived_from"`
|
||||
CreatedBy string `json:"created_by"`
|
||||
CreatedOn string `json:"created_on"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CommitID string `json:"commit_id"`
|
||||
CommitMessage string `json:"commit_message"`
|
||||
LastCommitBy string `json:"last_commit_by"`
|
||||
LastCommitOn string `json:"last_commit_on"`
|
||||
}
|
||||
|
||||
// CloneEntity struct for the Clone entity A4C_A2G_GHA_SC_CLONE
|
||||
type CloneEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
UUID string `json:"uuid"`
|
||||
ScName string `json:"sc_name"`
|
||||
BranchName string `json:"branch_name"`
|
||||
ImportType string `json:"import_type"`
|
||||
Namespace string `json:"namepsace"`
|
||||
Status string `json:"status"`
|
||||
StatusDescription string `json:"status_descr"`
|
||||
StartedByUser string `json:"user_name"`
|
||||
StartTime string `json:"start_time"`
|
||||
ChangeTime string `json:"change_time"`
|
||||
}
|
||||
|
||||
type RepositoryEntity struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
ScName string `json:"sc_name"`
|
||||
ActiveBranch string `json:"active_branch"`
|
||||
AvailOnInst bool `json:"avail_on_inst"`
|
||||
}
|
||||
|
||||
// AbapLogs struct for ABAP logs
|
||||
type AbapLogs struct {
|
||||
Results []LogResults `json:"results"`
|
||||
}
|
||||
|
||||
type AbapLogsV2 struct {
|
||||
Results []LogResultsV2 `json:"results"`
|
||||
}
|
||||
|
||||
type LogResultsV2 struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
Index int `json:"log_index"`
|
||||
Name string `json:"log_name"`
|
||||
Status string `json:"type_of_found_issues"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
ToLogProtocol LogProtocolDeferred `json:"to_Log_Protocol"`
|
||||
}
|
||||
|
||||
type LogProtocolDeferred struct {
|
||||
Deferred URI `json:"__deferred"`
|
||||
}
|
||||
|
||||
type URI struct {
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
type LogProtocolResults struct {
|
||||
Results []LogProtocol `json:"results"`
|
||||
Count string `json:"__count"`
|
||||
}
|
||||
|
||||
type LogProtocol struct {
|
||||
Metadata AbapMetadata `json:"__metadata"`
|
||||
OverviewIndex int `json:"log_index"`
|
||||
ProtocolLine int `json:"index_no"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"descr"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// LogResults struct for Execution and Transport Log entities A4C_A2G_GHA_SC_LOG_EXE and A4C_A2G_GHA_SC_LOG_TP
|
||||
type LogResults struct {
|
||||
Index string `json:"index_no"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"descr"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
// RepositoriesConfig struct for parsing one or multiple branches and repositories configurations
|
||||
type RepositoriesConfig struct {
|
||||
BranchName string
|
||||
CommitID string
|
||||
RepositoryName string
|
||||
RepositoryNames []string
|
||||
Repositories string
|
||||
}
|
||||
|
||||
type EntitySetsForManageGitRepository struct {
|
||||
EntitySets []string `json:"EntitySets"`
|
||||
}
|
||||
|
||||
type CreateTagBacklog struct {
|
||||
RepositoryName string
|
||||
CommitID string
|
||||
Tags []Tag
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
TagName string
|
||||
TagDescription string
|
||||
}
|
||||
|
||||
type CreateTagBody struct {
|
||||
RepositoryName string `json:"sc_name"`
|
||||
CommitID string `json:"commit_id"`
|
||||
Tag string `json:"tag_name"`
|
||||
Description string `json:"tag_description"`
|
||||
}
|
||||
|
||||
type CreateTagResponse struct {
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
Reference in New Issue
Block a user