github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/gctsExecuteABAPQualityChecks.go (about)

     1  package cmd
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/xml"
     6  	"fmt"
     7  	"html"
     8  	"net/http"
     9  	"net/http/cookiejar"
    10  	"net/url"
    11  	"os"
    12  	"regexp"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/SAP/jenkins-library/pkg/command"
    17  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    18  	"github.com/SAP/jenkins-library/pkg/log"
    19  	"github.com/SAP/jenkins-library/pkg/telemetry"
    20  	"github.com/pkg/errors"
    21  )
    22  
    23  var atcFailure, aUnitFailure bool
    24  
    25  func gctsExecuteABAPQualityChecks(config gctsExecuteABAPQualityChecksOptions, telemetryData *telemetry.CustomData) {
    26  
    27  	// for command execution use Command
    28  	c := command.Command{}
    29  	// reroute command output to logging framework
    30  	c.Stdout(log.Writer())
    31  	c.Stderr(log.Writer())
    32  
    33  	httpClient := &piperhttp.Client{}
    34  
    35  	// error situations should stop execution through log.Entry().Fatal() call which leads to an os.Exit(1) in the end
    36  	err := rungctsExecuteABAPQualityChecks(&config, httpClient)
    37  	if err != nil {
    38  		log.Entry().WithError(err).Fatal("step execution failed")
    39  	}
    40  
    41  	if aUnitFailure || atcFailure {
    42  
    43  		log.Entry().Fatal("step execution failed")
    44  
    45  	}
    46  
    47  }
    48  
    49  func rungctsExecuteABAPQualityChecks(config *gctsExecuteABAPQualityChecksOptions, httpClient piperhttp.Sender) error {
    50  
    51  	const localChangedObjects = "localchangedobjects"
    52  	const remoteChangedObjects = "remotechangedobjects"
    53  	const localChangedPackages = "localchangedpackages"
    54  	const remoteChangedPackages = "remotechangedpackages"
    55  	const repository = "repository"
    56  	const packages = "packages"
    57  
    58  	cookieJar, cookieErr := cookiejar.New(nil)
    59  	if cookieErr != nil {
    60  		return errors.Wrap(cookieErr, "creating a cookie jar failed")
    61  	}
    62  
    63  	maxRetries := -1
    64  	clientOptions := piperhttp.ClientOptions{
    65  		CookieJar:                 cookieJar,
    66  		Username:                  config.Username,
    67  		Password:                  config.Password,
    68  		MaxRetries:                maxRetries,
    69  		TransportSkipVerification: config.SkipSSLVerification,
    70  	}
    71  
    72  	httpClient.SetOptions(clientOptions)
    73  
    74  	log.Entry().Infof("start of gctsExecuteABAPQualityChecks step with configuration values: %v", config)
    75  
    76  	var objects []repoObject
    77  	var err error
    78  
    79  	log.Entry().Info("scope:", config.Scope)
    80  
    81  	switch strings.ToLower(config.Scope) {
    82  	case localChangedObjects:
    83  		objects, err = getLocalObjects(config, httpClient)
    84  	case remoteChangedObjects:
    85  		objects, err = getRemoteObjects(config, httpClient)
    86  	case localChangedPackages:
    87  		objects, err = getLocalPackages(config, httpClient)
    88  	case remoteChangedPackages:
    89  		objects, err = getRemotePackages(config, httpClient)
    90  	case repository:
    91  		objects, err = getRepositoryObjects(config, httpClient)
    92  	case packages:
    93  		objects, err = getPackages(config, httpClient)
    94  	default:
    95  		log.Entry().Info("the specified scope does not exists, the default one will be used:" + repository)
    96  		objects, err = getRepositoryObjects(config, httpClient)
    97  	}
    98  
    99  	if err != nil {
   100  		log.Entry().WithError(err).Fatal("failure in get objects")
   101  	}
   102  
   103  	if objects == nil {
   104  		log.Entry().Warning("no object delta was found, therefore the step execution will stop")
   105  		return nil
   106  
   107  	}
   108  
   109  	log.Entry().Infof("objects to be checked:")
   110  	for _, object := range objects {
   111  		log.Entry().Info(object.Type, " ", object.Object)
   112  	}
   113  
   114  	if config.AUnitTest {
   115  
   116  		// wrapper for execution of AUnit Test
   117  		err := executeAUnitTest(config, httpClient, objects)
   118  
   119  		if err != nil {
   120  			log.Entry().WithError(err)
   121  
   122  		}
   123  
   124  		if aUnitFailure {
   125  
   126  			log.Entry().Error("unit test(s) has/have failed! Check " + config.AUnitResultsFileName + " for more information! If you have enabled Warnings-Next-Generation Plugin, you can see the issues there!")
   127  
   128  		} else {
   129  
   130  			log.Entry().Info("AUnit test run completed successfully. If there are any results from the run, the results are saved in " + config.AUnitResultsFileName)
   131  
   132  		}
   133  	}
   134  
   135  	if config.AtcCheck {
   136  
   137  		// wrapper for execution of ATCChecks
   138  		err = executeATCCheck(config, httpClient, objects)
   139  
   140  		if err != nil {
   141  			log.Entry().WithError(err).Fatal("execute ATC Check failed")
   142  		}
   143  
   144  		if atcFailure {
   145  
   146  			log.Entry().Error(" ATC issue(s) found! Check " + config.AtcResultsFileName + " for more information! If you have enabled Warnings-Next-Generation Plugin, you can see the issues there!")
   147  
   148  		} else {
   149  
   150  			log.Entry().Info("ATCCheck test run completed successfully. If there are any results from the run, the results are saved in " + config.AtcResultsFileName)
   151  
   152  		}
   153  
   154  	}
   155  
   156  	return nil
   157  
   158  }
   159  
   160  func getLocalObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   161  
   162  	var localObjects []repoObject
   163  	var localObject repoObject
   164  
   165  	log.Entry().Info("get local changed objects started")
   166  
   167  	if config.Commit == "" {
   168  
   169  		return []repoObject{}, errors.Errorf("For scope: localChangedObjects you need to specify a commit")
   170  
   171  	}
   172  
   173  	history, err := getHistory(config, client)
   174  	if err != nil {
   175  		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
   176  	}
   177  
   178  	if len(history.Result) == 0 {
   179  
   180  		return []repoObject{}, errors.Wrap(err, "no activities (from commit - to commit) were found")
   181  	}
   182  
   183  	fromCommit := history.Result[0].FromCommit
   184  	log.Entry().Info("from Commit: ", fromCommit)
   185  	toCommit := history.Result[0].ToCommit
   186  	log.Entry().Info("to Commit: ", toCommit)
   187  
   188  	// object delta between FromCommit and ToCommit retrieved from Activities Tab in gCTS
   189  	resp, err := getObjectDifference(config, fromCommit, toCommit, client)
   190  	if err != nil {
   191  		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
   192  	}
   193  
   194  	for _, object := range resp.Objects {
   195  		localObject.Object = object.Name
   196  		localObject.Type = object.Type
   197  		localObjects = append(localObjects, localObject)
   198  	}
   199  
   200  	log.Entry().Info("get local changed objects finished")
   201  
   202  	return localObjects, nil
   203  }
   204  
   205  func getRemoteObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   206  
   207  	var remoteObjects []repoObject
   208  	var remoteObject repoObject
   209  	var currentRemoteCommit string
   210  
   211  	log.Entry().Info("get remote changed objects started")
   212  
   213  	if config.Commit == "" {
   214  
   215  		return []repoObject{}, errors.Errorf("For scope: remoteChangedObjects you need to specify a commit")
   216  
   217  	}
   218  
   219  	commitList, err := getCommitList(config, client)
   220  
   221  	if err != nil {
   222  		return []repoObject{}, errors.Wrap(err, "get remote changed objects failed")
   223  	}
   224  
   225  	for i, commit := range commitList.Commits {
   226  		if commit.ID == config.Commit {
   227  			currentRemoteCommit = commitList.Commits[i+1].ID
   228  			break
   229  		}
   230  	}
   231  	if currentRemoteCommit == "" {
   232  		return []repoObject{}, errors.New("current remote commit was not found")
   233  
   234  	}
   235  	log.Entry().Info("current commit in the remote repository: ", currentRemoteCommit)
   236  	// object delta between the commit that triggered the pipeline and the current commit in the remote repository
   237  	resp, err := getObjectDifference(config, currentRemoteCommit, config.Commit, client)
   238  
   239  	if err != nil {
   240  		return []repoObject{}, errors.Wrap(err, "get remote changed objects failed")
   241  	}
   242  
   243  	for _, object := range resp.Objects {
   244  		remoteObject.Object = object.Name
   245  		remoteObject.Type = object.Type
   246  		remoteObjects = append(remoteObjects, remoteObject)
   247  	}
   248  
   249  	log.Entry().Info("get remote changed objects finished")
   250  
   251  	return remoteObjects, nil
   252  }
   253  
   254  func getLocalPackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   255  
   256  	var localPackages []repoObject
   257  	var localPackage repoObject
   258  
   259  	log.Entry().Info("get local changed packages started")
   260  
   261  	if config.Commit == "" {
   262  
   263  		return []repoObject{}, errors.Errorf("For scope: localChangedPackages you need to specify a commit")
   264  
   265  	}
   266  
   267  	history, err := getHistory(config, client)
   268  	if err != nil {
   269  		return []repoObject{}, errors.Wrap(err, "get local changed objects failed")
   270  	}
   271  
   272  	if len(history.Result) == 0 {
   273  
   274  		return []repoObject{}, errors.Wrap(err, "no activities (from commit - to commit) were found")
   275  	}
   276  
   277  	fromCommit := history.Result[0].FromCommit
   278  	log.Entry().Info("from Commit: ", fromCommit)
   279  	toCommit := history.Result[0].ToCommit
   280  	log.Entry().Info("to Commit: ", toCommit)
   281  
   282  	// object delta between FromCommit and ToCommit retrieved from Activities Tab in gCTS
   283  	resp, err := getObjectDifference(config, fromCommit, config.Commit, client)
   284  
   285  	if err != nil {
   286  		return []repoObject{}, errors.Wrap(err, "get local changed packages failed")
   287  
   288  	}
   289  
   290  	myPackages := map[string]bool{}
   291  
   292  	// objects are resolved into packages(DEVC)
   293  	for _, object := range resp.Objects {
   294  		objInfo, err := getObjectInfo(config, client, object.Name, object.Type)
   295  		if err != nil {
   296  			return []repoObject{}, errors.Wrap(err, "get local changed packages failed")
   297  		}
   298  		if myPackages[objInfo.Devclass] {
   299  
   300  		} else {
   301  			myPackages[objInfo.Devclass] = true
   302  			localPackage.Object = objInfo.Devclass
   303  			localPackage.Type = "DEVC"
   304  			localPackages = append(localPackages, localPackage)
   305  		}
   306  
   307  	}
   308  
   309  	log.Entry().Info("get local changed packages finished")
   310  	return localPackages, nil
   311  }
   312  
   313  func getRemotePackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   314  
   315  	var remotePackages []repoObject
   316  	var remotePackage repoObject
   317  	var currentRemoteCommit string
   318  
   319  	log.Entry().Info("get remote changed packages started")
   320  
   321  	if config.Commit == "" {
   322  
   323  		return []repoObject{}, errors.Errorf("For scope: remoteChangedPackages you need to specify a commit")
   324  
   325  	}
   326  
   327  	commitList, err := getCommitList(config, client)
   328  
   329  	if err != nil {
   330  		return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
   331  	}
   332  
   333  	for i, commit := range commitList.Commits {
   334  		if commit.ID == config.Commit {
   335  			currentRemoteCommit = commitList.Commits[i+1].ID
   336  			break
   337  		}
   338  	}
   339  
   340  	if currentRemoteCommit == "" {
   341  		return []repoObject{}, errors.Wrap(err, "current remote commit was not found")
   342  
   343  	}
   344  	log.Entry().Info("current commit in the remote repository: ", currentRemoteCommit)
   345  	//object delta between the commit that triggered the pipeline and the current commit in the remote repository
   346  	resp, err := getObjectDifference(config, currentRemoteCommit, config.Commit, client)
   347  	if err != nil {
   348  		return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
   349  	}
   350  
   351  	myPackages := map[string]bool{}
   352  	// objects are resolved into packages(DEVC)
   353  	for _, object := range resp.Objects {
   354  		objInfo, err := getObjectInfo(config, client, object.Name, object.Type)
   355  		if err != nil {
   356  			return []repoObject{}, errors.Wrap(err, "get remote changed packages failed")
   357  		}
   358  		if myPackages[objInfo.Devclass] {
   359  
   360  		} else {
   361  			myPackages[objInfo.Devclass] = true
   362  			remotePackage.Object = objInfo.Devclass
   363  			remotePackage.Type = "DEVC"
   364  			remotePackages = append(remotePackages, remotePackage)
   365  		}
   366  
   367  	}
   368  	log.Entry().Info("get remote changed packages finished")
   369  	return remotePackages, nil
   370  }
   371  
   372  func getRepositoryObjects(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   373  
   374  	log.Entry().Info("get repository objects started")
   375  
   376  	var repoResp repoObjectResponse
   377  
   378  	url := config.Host +
   379  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
   380  		"/objects?sap-client=" + config.Client
   381  
   382  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   383  
   384  	if urlErr != nil {
   385  
   386  		return nil, urlErr
   387  	}
   388  
   389  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
   390  
   391  	defer func() {
   392  		if resp != nil && resp.Body != nil {
   393  			resp.Body.Close()
   394  		}
   395  	}()
   396  
   397  	if httpErr != nil {
   398  		return []repoObject{}, errors.Wrap(httpErr, "could not get repository objects")
   399  	} else if resp == nil {
   400  		return []repoObject{}, errors.New("could not get repository objects: did not retrieve a HTTP response")
   401  	}
   402  
   403  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoResp)
   404  	if parsingErr != nil {
   405  		return []repoObject{}, errors.Errorf("%v", parsingErr)
   406  	}
   407  
   408  	var repositoryObjects []repoObject
   409  
   410  	// remove object type DEVC, because it is already included in scope packages
   411  	// also if you run ATC Checks for DEVC together with other object types, ATC checks will run only for DEVC
   412  	for _, object := range repoResp.Objects {
   413  
   414  		if object.Type != "DEVC" {
   415  			repositoryObjects = append(repositoryObjects, object)
   416  		}
   417  
   418  	}
   419  
   420  	log.Entry().Info("get repository objects finished")
   421  
   422  	// all objects that are part of the local repository
   423  	return repositoryObjects, nil
   424  }
   425  
   426  func getPackages(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) ([]repoObject, error) {
   427  
   428  	var packages []repoObject
   429  
   430  	log.Entry().Info("get packages started")
   431  
   432  	var repoResp repoObjectResponse
   433  
   434  	url := config.Host +
   435  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
   436  		"/objects?sap-client=" + config.Client
   437  
   438  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   439  
   440  	if urlErr != nil {
   441  
   442  		return nil, urlErr
   443  	}
   444  
   445  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
   446  
   447  	defer func() {
   448  		if resp != nil && resp.Body != nil {
   449  			resp.Body.Close()
   450  		}
   451  	}()
   452  
   453  	if httpErr != nil {
   454  		return []repoObject{}, errors.Wrap(httpErr, "get packages failed: could not get repository objects")
   455  	} else if resp == nil {
   456  		return []repoObject{}, errors.New("get packages failed: could not get repository objects: did not retrieve a HTTP response")
   457  	}
   458  
   459  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoResp)
   460  	if parsingErr != nil {
   461  		return []repoObject{}, errors.Errorf("%v", parsingErr)
   462  	}
   463  	// chose only DEVC from repository objects
   464  	for _, object := range repoResp.Objects {
   465  
   466  		if object.Type == "DEVC" {
   467  			packages = append(packages, object)
   468  		}
   469  
   470  	}
   471  
   472  	log.Entry().Info("get packages finished")
   473  	return packages, nil
   474  }
   475  
   476  func discoverServer(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (*http.Header, error) {
   477  
   478  	url := config.Host +
   479  		"/sap/bc/adt/core/discovery?sap-client=" + config.Client
   480  
   481  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   482  
   483  	if urlErr != nil {
   484  
   485  		return nil, urlErr
   486  	}
   487  
   488  	header := make(http.Header)
   489  	header.Add("Accept", "application/atomsvc+xml")
   490  	header.Add("x-csrf-token", "fetch")
   491  	header.Add("saml2", "disabled")
   492  
   493  	disc, httpErr := client.SendRequest("GET", url, nil, header, nil)
   494  
   495  	defer func() {
   496  		if disc != nil && disc.Body != nil {
   497  			disc.Body.Close()
   498  		}
   499  	}()
   500  
   501  	if httpErr != nil {
   502  		return nil, errors.Wrap(httpErr, "discovery of the ABAP server failed")
   503  	} else if disc == nil || disc.Header == nil {
   504  		return nil, errors.New("discovery of the ABAP server failed: did not retrieve a HTTP response")
   505  	}
   506  
   507  	return &disc.Header, nil
   508  }
   509  
   510  func executeAUnitTest(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objects []repoObject) error {
   511  
   512  	log.Entry().Info("execute ABAP Unit Test started")
   513  
   514  	var innerXml string
   515  	var result runResult
   516  
   517  	for _, object := range objects {
   518  
   519  		switch object.Type {
   520  		case "CLAS":
   521  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/` + url.QueryEscape(object.Object) + `"/>`
   522  		case "DEVC":
   523  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/repository/informationsystem/virtualfolders?selection=package%3a` + url.QueryEscape(object.Object) + `"/>`
   524  
   525  		}
   526  
   527  	}
   528  
   529  	var xmlBody = []byte(`<?xml version="1.0" encoding="UTF-8"?>
   530  		<aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
   531  			<external>
   532  				<coverage active="false"/>
   533  			</external>
   534  			<options>
   535  				<uriType value="semantic"/>
   536  				<testDeterminationStrategy appendAssignedTestsPreview="true" assignedTests="false" sameProgram="true"/>
   537  				<testRiskLevels critical="true" dangerous="true" harmless="true"/>
   538  				<testDurations long="true" medium="true" short="true"/>
   539  				<withNavigationUri enabled="false"/>
   540  			</options>
   541  			<adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
   542  			<objectSet kind="inclusive">
   543  		<adtcore:objectReferences>` +
   544  		innerXml +
   545  		`</adtcore:objectReferences>
   546  			</objectSet>
   547  			</adtcore:objectSets>
   548  		</aunit:runConfiguration>`)
   549  
   550  	resp, err := runAUnitTest(config, client, xmlBody)
   551  	if err != nil {
   552  		return errors.Wrap(err, "execute of Aunit test has failed")
   553  	}
   554  
   555  	parsingErr := piperhttp.ParseHTTPResponseBodyXML(resp, &result)
   556  	if parsingErr != nil {
   557  		log.Entry().Warning(parsingErr)
   558  		return nil
   559  	}
   560  
   561  	parsedRes, err := parseUnitResult(config, client, &result)
   562  
   563  	if err != nil {
   564  		log.Entry().Warning(err)
   565  		return nil
   566  	}
   567  
   568  	log.Entry().Info("execute ABAP Unit Test finished.", parsedRes.Text)
   569  
   570  	return nil
   571  }
   572  
   573  func runAUnitTest(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, xml []byte) (response *http.Response, err error) {
   574  
   575  	log.Entry().Info("run ABAP Unit Test started")
   576  	url := config.Host +
   577  		"/sap/bc/adt/abapunit/testruns?sap-client=" + config.Client
   578  
   579  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   580  
   581  	if urlErr != nil {
   582  
   583  		return nil, urlErr
   584  	}
   585  
   586  	discHeader, discError := discoverServer(config, client)
   587  
   588  	if discError != nil {
   589  		return response, errors.Wrap(discError, "run of unit tests failed")
   590  	}
   591  
   592  	if discHeader.Get("X-Csrf-Token") == "" {
   593  
   594  		return response, errors.Errorf("could not retrieve x-csrf-token from server")
   595  	}
   596  
   597  	header := make(http.Header)
   598  	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
   599  	header.Add("Accept", "application/xml")
   600  	header.Add("Content-Type", "application/vnd.sap.adt.abapunit.testruns.result.v1+xml")
   601  
   602  	response, httpErr := client.SendRequest("POST", url, bytes.NewBuffer(xml), header, nil)
   603  
   604  	if httpErr != nil {
   605  		return response, errors.Wrap(httpErr, "run of unit tests failed")
   606  	} else if response == nil {
   607  		return response, errors.New("run of unit tests failed: did not retrieve a HTTP response")
   608  	}
   609  
   610  	log.Entry().Info("run ABAP Unit Test finished")
   611  	return response, nil
   612  }
   613  
   614  func parseUnitResult(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, aUnitRunResult *runResult) (parsedResult checkstyle, err error) {
   615  
   616  	log.Entry().Info("parse ABAP Unit Result started")
   617  
   618  	var fileName string
   619  	var aUnitFile file
   620  	var aUnitError checkstyleError
   621  
   622  	parsedResult.Version = "1.0"
   623  
   624  	for _, program := range aUnitRunResult.Program {
   625  
   626  		objectType := program.Type[0:4]
   627  		objectName := program.Name
   628  
   629  		//syntax error in unit test or class
   630  		if program.Alerts.Alert.HasSyntaxErrors == "true" {
   631  
   632  			aUnitFailure = true
   633  			aUnitError.Source = objectName
   634  			aUnitError.Severity = "error"
   635  			log.Entry().Info("severity: ", aUnitError.Severity)
   636  			aUnitError.Message = html.UnescapeString(program.Alerts.Alert.Title + " " + program.Alerts.Alert.Details.Detail.AttrText)
   637  			log.Entry().Info("message: ", aUnitError.Message)
   638  			aUnitError.Line, err = findLine(config, client, program.Alerts.Alert.Stack.StackEntry.URI, objectName, objectType)
   639  			log.Entry().Error("line: ", aUnitError.Line)
   640  			if err != nil {
   641  				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")
   642  
   643  			}
   644  			fileName, err = getFileName(config, client, program.Alerts.Alert.Stack.StackEntry.URI, objectName)
   645  			log.Entry().Error("file path: ", aUnitError.Line)
   646  			if err != nil {
   647  				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")
   648  
   649  			}
   650  
   651  			aUnitFile.Error = append(aUnitFile.Error, aUnitError)
   652  			aUnitError = checkstyleError{}
   653  			log.Entry().Error("there is a syntax error", aUnitFile)
   654  		}
   655  
   656  		for _, testClass := range program.TestClasses.TestClass {
   657  
   658  			for _, testMethod := range testClass.TestMethods.TestMethod {
   659  
   660  				aUnitError.Source = testClass.Name + "/" + testMethod.Name
   661  
   662  				// unit test failure
   663  				if len(testMethod.Alerts.Alert) > 0 {
   664  
   665  					for _, testalert := range testMethod.Alerts.Alert {
   666  
   667  						switch testalert.Severity {
   668  						case "fatal":
   669  							log.Entry().Error("unit test " + aUnitError.Source + " has failed with severity fatal")
   670  							aUnitFailure = true
   671  							aUnitError.Severity = "error"
   672  						case "critical":
   673  							log.Entry().Error("unit test " + aUnitError.Source + " has failed with severity critical")
   674  							aUnitFailure = true
   675  							aUnitError.Severity = "error"
   676  						case "tolerable":
   677  							log.Entry().Warning("unit test " + aUnitError.Source + "  has failed with severity warning")
   678  							aUnitError.Severity = "warning"
   679  						default:
   680  							aUnitError.Severity = "info"
   681  
   682  						}
   683  
   684  						//unit test message is spread in different elements
   685  						for _, detail := range testalert.Details.Detail {
   686  							aUnitError.Message = aUnitError.Message + " " + detail.AttrText
   687  							for _, subdetail := range detail.Details.Detail {
   688  
   689  								aUnitError.Message = html.UnescapeString(aUnitError.Message + " " + subdetail.AttrText)
   690  								log.Entry().Info("message: ", aUnitError.Message)
   691  							}
   692  
   693  						}
   694  
   695  						aUnitError.Line, err = findLine(config, client, testalert.Stack.StackEntry.URI, objectName, objectType)
   696  						log.Entry().Info("line: ", aUnitError.Line)
   697  						if err != nil {
   698  
   699  							log.Entry().Warning(err)
   700  
   701  						}
   702  
   703  					}
   704  
   705  					aUnitFile.Error = append(aUnitFile.Error, aUnitError)
   706  					aUnitError = checkstyleError{}
   707  
   708  				} else {
   709  
   710  					log.Entry().Info("unit test:", aUnitError.Source, "- was successful")
   711  
   712  				}
   713  
   714  			}
   715  
   716  			fileName, err = getFileName(config, client, testClass.URI, objectName)
   717  			if err != nil {
   718  				return parsedResult, errors.Wrap(err, "parse AUnit Result failed")
   719  
   720  			}
   721  		}
   722  
   723  		aUnitFile.Name, err = constructPath(config, client, fileName, objectName, objectType)
   724  		log.Entry().Error("file path: ", aUnitFile.Name)
   725  		if err != nil {
   726  
   727  			return parsedResult, errors.Wrap(err, "parse AUnit Result failed")
   728  		}
   729  		parsedResult.File = append(parsedResult.File, aUnitFile)
   730  		aUnitFile = file{}
   731  
   732  	}
   733  
   734  	body, _ := xml.Marshal(parsedResult)
   735  
   736  	writeErr := os.WriteFile(config.AUnitResultsFileName, body, 0644)
   737  
   738  	if writeErr != nil {
   739  		log.Entry().Error("file AUnitResults.xml could not be created")
   740  		return parsedResult, fmt.Errorf("handling unit test results failed: %w", writeErr)
   741  	}
   742  
   743  	log.Entry().Info("parse ABAP Unit Result finished")
   744  	return parsedResult, nil
   745  
   746  }
   747  
   748  func executeATCCheck(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objects []repoObject) (error error) {
   749  
   750  	log.Entry().Info("execute ATC Check started")
   751  
   752  	var innerXml string
   753  	var result worklist
   754  
   755  	for _, object := range objects {
   756  
   757  		switch object.Type {
   758  
   759  		case "CLAS":
   760  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/` + url.QueryEscape(object.Object) + `"/>`
   761  		case "INTF":
   762  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/interfaces/` + object.Object + `"/>`
   763  		case "DEVC":
   764  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/repository/informationsystem/virtualfolders?selection=package%3a` + url.QueryEscape(object.Object) + `"/>`
   765  		case "FUGR":
   766  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/functions/groups/` + object.Object + `/source/main"/>`
   767  		case "TABL":
   768  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/tables/` + object.Object + `/source/main"/>`
   769  		case "DTEL":
   770  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/dataelements/` + object.Object + `"/>`
   771  		case "DOMA":
   772  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/ddic/domains/` + object.Object + `"/>`
   773  		case "MSAG":
   774  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/messageclass/` + object.Object + `"/>`
   775  		case "PROG":
   776  			innerXml = innerXml + `<adtcore:objectReference adtcore:uri="/sap/bc/adt/programs/programs/` + object.Object + `/source/main"/>`
   777  		default:
   778  			log.Entry().Warning("object Type " + object.Type + " is not supported!")
   779  
   780  		}
   781  
   782  	}
   783  
   784  	var xmlBody = []byte(`<?xml version="1.0" encoding="UTF-8"?>
   785  	<atc:run xmlns:atc="http://www.sap.com/adt/atc"
   786  	maximumVerdicts="100">
   787  			<objectSets xmlns:adtcore="http://www.sap.com/adt/core">
   788  			<objectSet kind="inclusive">
   789   		<adtcore:objectReferences>` + innerXml +
   790  		`</adtcore:objectReferences>
   791  			</objectSet>
   792  			</objectSets>
   793  				</atc:run>`)
   794  
   795  	worklist, err := getWorklist(config, client)
   796  	if err != nil {
   797  		return errors.Wrap(err, "execution of ATC Checks failed")
   798  	}
   799  
   800  	err = startATCRun(config, client, xmlBody, worklist)
   801  
   802  	if err != nil {
   803  		return errors.Wrap(err, "execution of ATC Checks failed")
   804  	}
   805  
   806  	resp, err := getATCRun(config, client, worklist)
   807  
   808  	if err != nil {
   809  		return errors.Wrap(err, "execution of ATC Checks failed")
   810  	}
   811  
   812  	parsingErr := piperhttp.ParseHTTPResponseBodyXML(resp, &result)
   813  	if parsingErr != nil {
   814  		log.Entry().Warning(parsingErr)
   815  		return nil
   816  	}
   817  
   818  	atcRes, err := parseATCCheckResult(config, client, &result)
   819  
   820  	if err != nil {
   821  		log.Entry().Error(err)
   822  		return errors.Wrap(err, "execution of ATC Checks failed")
   823  	}
   824  
   825  	log.Entry().Info("execute ATC Checks finished.", atcRes.Text)
   826  
   827  	return nil
   828  
   829  }
   830  func startATCRun(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, xml []byte, worklistID string) (err error) {
   831  
   832  	log.Entry().Info("ATC Run started")
   833  
   834  	discHeader, discError := discoverServer(config, client)
   835  	if discError != nil {
   836  		return errors.Wrap(discError, "start of ATC run failed")
   837  	}
   838  
   839  	if discHeader.Get("X-Csrf-Token") == "" {
   840  		return errors.Errorf("could not retrieve x-csrf-token from server")
   841  	}
   842  
   843  	header := make(http.Header)
   844  	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
   845  	header.Add("Accept", "application/xml")
   846  
   847  	url := config.Host +
   848  		"/sap/bc/adt/atc/runs?worklistId=" + worklistID + "&sap-client=" + config.Client
   849  
   850  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   851  
   852  	if urlErr != nil {
   853  
   854  		return urlErr
   855  	}
   856  
   857  	resp, httpErr := client.SendRequest("POST", url, bytes.NewBuffer(xml), header, nil)
   858  
   859  	defer func() {
   860  		if resp != nil && resp.Body != nil {
   861  			resp.Body.Close()
   862  		}
   863  	}()
   864  
   865  	if httpErr != nil {
   866  		return errors.Wrap(httpErr, "start of ATC run failed")
   867  	} else if resp == nil {
   868  		return errors.New("start of ATC run failed: did not retrieve a HTTP response")
   869  	}
   870  
   871  	log.Entry().Info("ATC Run finished")
   872  
   873  	return nil
   874  
   875  }
   876  
   877  func getATCRun(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, worklistID string) (response *http.Response, err error) {
   878  
   879  	log.Entry().Info("get ATC Run Results started")
   880  
   881  	header := make(http.Header)
   882  
   883  	url := config.Host +
   884  		"/sap/bc/adt/atc/worklists/" + worklistID + "?sap-client=" + config.Client
   885  
   886  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   887  
   888  	if urlErr != nil {
   889  
   890  		return nil, urlErr
   891  	}
   892  
   893  	header.Add("Accept", "application/atc.worklist.v1+xml")
   894  
   895  	resp, httpErr := client.SendRequest("GET", url, nil, header, nil)
   896  
   897  	if httpErr != nil {
   898  		return response, errors.Wrap(httpErr, "get ATC run failed")
   899  	} else if resp == nil {
   900  		return response, errors.New("get ATC run failed: did not retrieve a HTTP response")
   901  	}
   902  	log.Entry().Info("get ATC Run Results finished")
   903  	return resp, nil
   904  
   905  }
   906  
   907  func getWorklist(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (worklistID string, error error) {
   908  
   909  	url := config.Host +
   910  		"/sap/bc/adt/atc/worklists?checkVariant=" + config.AtcVariant + "&sap-client=" + config.Client
   911  	discHeader, discError := discoverServer(config, client)
   912  
   913  	url, urlErr := addQueryToURL(url, config.QueryParameters)
   914  
   915  	if urlErr != nil {
   916  
   917  		return worklistID, urlErr
   918  	}
   919  
   920  	if discError != nil {
   921  		return worklistID, errors.Wrap(discError, "get worklist failed")
   922  	}
   923  
   924  	if discHeader.Get("X-Csrf-Token") == "" {
   925  		return worklistID, errors.Errorf("could not retrieve x-csrf-token from server")
   926  	}
   927  
   928  	header := make(http.Header)
   929  	header.Add("x-csrf-token", discHeader.Get("X-Csrf-Token"))
   930  	header.Add("Accept", "*/*")
   931  
   932  	resp, httpErr := client.SendRequest("POST", url, nil, header, nil)
   933  	defer func() {
   934  		if resp != nil && resp.Body != nil {
   935  			resp.Body.Close()
   936  		}
   937  	}()
   938  
   939  	if httpErr != nil {
   940  		return worklistID, errors.Wrap(httpErr, "get worklist failed")
   941  	} else if resp == nil {
   942  		return worklistID, errors.New("get worklist failed: did not retrieve a HTTP response")
   943  	}
   944  	location := resp.Header["Location"][0]
   945  	locationSlice := strings.Split(location, "/")
   946  	worklistID = locationSlice[len(locationSlice)-1]
   947  	log.Entry().Info("worklist id for ATC check: ", worklistID)
   948  
   949  	return worklistID, nil
   950  }
   951  
   952  func parseATCCheckResult(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, response *worklist) (atcResults checkstyle, error error) {
   953  
   954  	log.Entry().Info("parse ATC Check Result started")
   955  
   956  	var atcFile file
   957  	var subObject string
   958  	var aTCUnitError checkstyleError
   959  
   960  	atcResults.Version = "1.0"
   961  
   962  	for _, object := range response.Objects.Object {
   963  
   964  		objectType := object.Type
   965  		objectName := object.Name
   966  
   967  		for _, atcworklist := range object.Findings.Finding {
   968  
   969  			log.Entry().Info("there is atc finding for object type: ", objectType+" object name: "+objectName)
   970  
   971  			path, err := url.PathUnescape(atcworklist.Location)
   972  
   973  			if err != nil {
   974  				return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
   975  
   976  			}
   977  
   978  			if len(atcworklist.Atcfinding) > 0 {
   979  
   980  				priority, err := strconv.Atoi(atcworklist.Priority)
   981  
   982  				if err != nil {
   983  					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
   984  
   985  				}
   986  
   987  				switch priority {
   988  				case 1:
   989  					atcFailure = true
   990  					aTCUnitError.Severity = "error"
   991  					log.Entry().Error("atc issue with priority: 1 ")
   992  				case 2:
   993  					atcFailure = true
   994  					aTCUnitError.Severity = "error"
   995  					log.Entry().Error("atc issue with priority: 2 ")
   996  				case 3:
   997  					aTCUnitError.Severity = "warning"
   998  					log.Entry().Warning("atc issue with priority: 3 ")
   999  				default:
  1000  					aTCUnitError.Severity = "info"
  1001  					log.Entry().Info("atc issue with low priority ")
  1002  				}
  1003  
  1004  				log.Entry().Error("severity: ", aTCUnitError.Severity)
  1005  
  1006  				if aTCUnitError.Line == "" {
  1007  
  1008  					aTCUnitError.Line, err = findLine(config, client, path, objectName, objectType)
  1009  					log.Entry().Info("line: ", aTCUnitError.Line)
  1010  
  1011  					if err != nil {
  1012  						log.Entry().Info(path)
  1013  						log.Entry().Warning(err)
  1014  
  1015  					}
  1016  
  1017  				}
  1018  
  1019  				if subObject != "" {
  1020  					aTCUnitError.Source = objectName + "/" + strings.ToUpper(subObject)
  1021  				} else {
  1022  					aTCUnitError.Source = objectName
  1023  				}
  1024  
  1025  				aTCUnitError.Message = html.UnescapeString(atcworklist.CheckTitle + " " + atcworklist.MessageTitle)
  1026  				log.Entry().Info("message: ", aTCUnitError.Message)
  1027  				atcFile.Error = append(atcFile.Error, aTCUnitError)
  1028  				aTCUnitError = checkstyleError{}
  1029  			}
  1030  
  1031  			if atcFile.Error[0].Message != "" {
  1032  
  1033  				fileName, err := getFileName(config, client, path, objectName)
  1034  
  1035  				if err != nil {
  1036  					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
  1037  				}
  1038  
  1039  				atcFile.Name, err = constructPath(config, client, fileName, objectName, objectType)
  1040  				log.Entry().Info("file path: ", atcFile.Name)
  1041  				if err != nil {
  1042  					return atcResults, errors.Wrap(err, "conversion of ATC check results to CheckStyle has failed")
  1043  				}
  1044  				atcResults.File = append(atcResults.File, atcFile)
  1045  				atcFile = file{}
  1046  
  1047  			}
  1048  
  1049  		}
  1050  	}
  1051  
  1052  	atcBody, _ := xml.Marshal(atcResults)
  1053  
  1054  	writeErr := os.WriteFile(config.AtcResultsFileName, atcBody, 0644)
  1055  
  1056  	if writeErr != nil {
  1057  		log.Entry().Error("ATCResults.xml could not be created")
  1058  		return atcResults, fmt.Errorf("handling atc results failed: %w", writeErr)
  1059  	}
  1060  	log.Entry().Info("parsing ATC check results to CheckStyle has finished.")
  1061  	return atcResults, writeErr
  1062  }
  1063  
  1064  func constructPath(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, fileName string, objectName string, objectType string) (filePath string, error error) {
  1065  
  1066  	targetDir, err := getTargetDir(config, client)
  1067  	if err != nil {
  1068  		return filePath, errors.Wrap(err, "path could not be constructed")
  1069  
  1070  	}
  1071  
  1072  	filePath = config.Workspace + "/" + targetDir + "/objects/" + strings.ToUpper(objectType) + "/" + strings.ToUpper(objectName) + "/" + fileName
  1073  	return filePath, nil
  1074  
  1075  }
  1076  
  1077  func findLine(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, path string, objectName string, objectType string) (line string, error error) {
  1078  
  1079  	regexLine := regexp.MustCompile(`.start=\d*`)
  1080  	regexMethod := regexp.MustCompile(`.name=[a-zA-Z0-9_-]*;`)
  1081  
  1082  	readableSource, err := checkReadableSource(config, client)
  1083  
  1084  	if err != nil {
  1085  
  1086  		return line, errors.Wrap(err, "could not find line in source code")
  1087  
  1088  	}
  1089  
  1090  	fileName, err := getFileName(config, client, path, objectName)
  1091  
  1092  	if err != nil {
  1093  
  1094  		return line, err
  1095  
  1096  	}
  1097  
  1098  	filePath, err := constructPath(config, client, fileName, objectName, objectType)
  1099  	if err != nil {
  1100  		return line, errors.Wrap(err, objectType+"/"+objectName+"could not find line in source code")
  1101  
  1102  	}
  1103  
  1104  	var absLine int
  1105  	if readableSource {
  1106  
  1107  		// the error line that we get from UnitTest Run or ATC Check is not aligned for the readable source, we need to calculated it
  1108  		rawfile, err := os.ReadFile(filePath)
  1109  
  1110  		if err != nil {
  1111  
  1112  			return line, errors.Wrapf(err, "could not find object in the workspace of your CI/CD tool ")
  1113  		}
  1114  
  1115  		file := string(rawfile)
  1116  
  1117  		splittedfile := strings.Split(file, "\n")
  1118  
  1119  		// CLAS/OSO - is unique identifier for protection section in CLAS
  1120  		if strings.Contains(path, "CLAS/OSO") {
  1121  
  1122  			for l, line := range splittedfile {
  1123  
  1124  				if strings.Contains(line, "protected section.") {
  1125  					absLine = l
  1126  					break
  1127  				}
  1128  
  1129  			}
  1130  
  1131  			// CLAS/OM - is unique identifier for method section in CLAS
  1132  		} else if strings.Contains(path, "CLAS/OM") {
  1133  
  1134  			methodName := regexMethod.FindString(path)
  1135  
  1136  			if methodName != "" {
  1137  				methodName = methodName[len(`.name=`) : len(methodName)-1]
  1138  
  1139  			}
  1140  
  1141  			for line, linecontent := range splittedfile {
  1142  
  1143  				if strings.Contains(linecontent, "method"+" "+methodName) {
  1144  					absLine = line
  1145  					break
  1146  				}
  1147  
  1148  			}
  1149  
  1150  			// CLAS/OSI - is unique identifier for private section in CLAS
  1151  		} else if strings.Contains(path, "CLAS/OSI") {
  1152  
  1153  			for line, linecontent := range splittedfile {
  1154  
  1155  				if strings.Contains(linecontent, "private section.") {
  1156  					absLine = line
  1157  					break
  1158  				}
  1159  
  1160  			}
  1161  
  1162  		}
  1163  
  1164  		errLine := regexLine.FindString(path)
  1165  
  1166  		if errLine != "" {
  1167  
  1168  			errLine, err := strconv.Atoi(errLine[len(`.start=`):])
  1169  			if err == nil {
  1170  				line = strconv.Itoa(absLine + errLine)
  1171  
  1172  			}
  1173  
  1174  		}
  1175  
  1176  	} else {
  1177  		// classic format
  1178  		errLine := regexLine.FindString(path)
  1179  		if errLine != "" {
  1180  			line = errLine[len(`.start=`):]
  1181  
  1182  		}
  1183  
  1184  	}
  1185  
  1186  	return line, nil
  1187  }
  1188  func getFileName(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, path string, objName string) (fileName string, error error) {
  1189  
  1190  	readableSource, err := checkReadableSource(config, client)
  1191  	if err != nil {
  1192  		return fileName, errors.Wrap(err, "get file name has failed")
  1193  
  1194  	}
  1195  
  1196  	path, err = url.PathUnescape(path)
  1197  
  1198  	var fileExtension string
  1199  	fileExtensionLength := 30 - len(objName)
  1200  	for i := 0; i < fileExtensionLength; i++ {
  1201  		fileExtension += "="
  1202  	}
  1203  
  1204  	if err != nil {
  1205  		return fileName, errors.Wrap(err, "get file name has failed")
  1206  
  1207  	}
  1208  
  1209  	//  INTERFACES
  1210  	regexInterface := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/interfaces\/\w*`)
  1211  	intf := regexInterface.FindString(path)
  1212  	if intf != "" && fileName == "" {
  1213  
  1214  		if readableSource {
  1215  
  1216  			fileName = strings.ToLower(objName) + ".intf.abap"
  1217  		} else {
  1218  			fileName = "REPS " + strings.ToUpper(objName) + fileExtension + "IU.abap"
  1219  		}
  1220  
  1221  	}
  1222  	// CLASSES DEFINITIONS
  1223  	regexClasDef := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/definitions\/`)
  1224  	clasDef := regexClasDef.FindString(path)
  1225  	if clasDef != "" && fileName == "" {
  1226  
  1227  		if readableSource {
  1228  
  1229  			fileName = strings.ToLower(objName) + ".clas.definitions.abap"
  1230  		} else {
  1231  			fileName = "CINC " + objName + fileExtension + "CCDEF.abap"
  1232  		}
  1233  
  1234  	}
  1235  
  1236  	// CLASSES IMPLEMENTATIONS
  1237  	regexClasImpl := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/implementations\/`)
  1238  	clasImpl := regexClasImpl.FindString(path)
  1239  	if clasImpl != "" && fileName == "" {
  1240  
  1241  		if readableSource {
  1242  
  1243  			fileName = strings.ToLower(objName) + ".clas.implementations.abap"
  1244  		} else {
  1245  			fileName = "CINC " + objName + fileExtension + "CCIMP.abap"
  1246  		}
  1247  
  1248  	}
  1249  
  1250  	// CLASSES MACROS
  1251  	regexClasMacro := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/includes\/macros\/`)
  1252  	clasMacro := regexClasMacro.FindString(path)
  1253  	if clasMacro != "" && fileName == "" {
  1254  
  1255  		if readableSource {
  1256  
  1257  			fileName = strings.ToLower(objName) + ".clas.macros.abap"
  1258  		} else {
  1259  			fileName = "CINC " + objName + fileExtension + "CCMAC.abap"
  1260  		}
  1261  
  1262  	}
  1263  
  1264  	// TEST CLASSES
  1265  	regexTestClass := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*#?\/?\w*\/?testclass`)
  1266  	testClass := regexTestClass.FindString(path)
  1267  	if testClass != "" && fileName == "" {
  1268  
  1269  		if readableSource {
  1270  
  1271  			fileName = strings.ToLower(objName) + ".clas.testclasses.abap"
  1272  		} else {
  1273  			fileName = "CINC " + objName + fileExtension + "CCAU.abap"
  1274  		}
  1275  
  1276  	}
  1277  
  1278  	// CLASS PROTECTED
  1279  	regexClasProtected := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OSO`)
  1280  	classProtected := regexClasProtected.FindString(path)
  1281  	if classProtected != "" && fileName == "" {
  1282  
  1283  		if readableSource {
  1284  
  1285  			fileName = strings.ToLower(objName) + ".clas.abap"
  1286  		} else {
  1287  			fileName = "CPRO " + objName + ".abap"
  1288  		}
  1289  
  1290  	}
  1291  
  1292  	// CLASS PRIVATE
  1293  	regexClasPrivate := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OSI`)
  1294  	classPrivate := regexClasPrivate.FindString(path)
  1295  	if classPrivate != "" && fileName == "" {
  1296  
  1297  		if readableSource {
  1298  
  1299  			fileName = strings.ToLower(objName) + ".clas.abap"
  1300  		} else {
  1301  			fileName = "CPRI " + objName + ".abap"
  1302  		}
  1303  
  1304  	}
  1305  
  1306  	// CLASS METHOD
  1307  	regexClasMethod := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#type=CLAS\/OM`)
  1308  	classMethod := regexClasMethod.FindString(path)
  1309  	if classMethod != "" && fileName == "" {
  1310  
  1311  		if readableSource {
  1312  
  1313  			fileName = strings.ToLower(objName) + ".clas.abap"
  1314  		} else {
  1315  
  1316  			regexmethodName := regexp.MustCompile(`name=\w*`)
  1317  			methodName := regexmethodName.FindString(path)
  1318  
  1319  			fileName = "METH " + methodName[len(`name=`):] + ".abap"
  1320  		}
  1321  
  1322  	}
  1323  
  1324  	// CLASS PUBLIC
  1325  	regexClasPublic := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/\w*\/source\/main#start`)
  1326  	classPublic := regexClasPublic.FindString(path)
  1327  	if classPublic != "" && fileName == "" {
  1328  
  1329  		if readableSource {
  1330  
  1331  			fileName = strings.ToLower(objName) + ".clas.abap"
  1332  		} else {
  1333  			fileName = "CPUB " + objName + ".abap"
  1334  		}
  1335  
  1336  	}
  1337  
  1338  	// FUNCTION INCLUDE
  1339  	regexFuncIncl := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/includes/\w*`)
  1340  
  1341  	funcIncl := regexFuncIncl.FindString(path)
  1342  	if funcIncl != "" && fileName == "" {
  1343  
  1344  		regexSubObj := regexp.MustCompile(`includes\/\w*`)
  1345  		subObject := regexSubObj.FindString(path)
  1346  		subObject = subObject[len(`includes/`):]
  1347  
  1348  		if readableSource {
  1349  
  1350  			fileName = strings.ToLower(objName) + ".fugr." + strings.ToLower(subObject) + ".reps.abap"
  1351  		} else {
  1352  			fileName = "REPS " + strings.ToUpper(subObject) + ".abap"
  1353  		}
  1354  
  1355  	}
  1356  
  1357  	// FUNCTION GROUP
  1358  	regexFuncGr := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/source\/main`)
  1359  
  1360  	funcGr := regexFuncGr.FindString(path)
  1361  	if funcGr != "" && fileName == "" {
  1362  
  1363  		if readableSource {
  1364  
  1365  			fileName = strings.ToLower(objName) + ".fugr.sapl" + strings.ToLower(objName) + ".reps.abap"
  1366  		} else {
  1367  			fileName = "REPS SAPL" + objName + ".abap"
  1368  		}
  1369  
  1370  	}
  1371  
  1372  	// FUNCTION MODULE
  1373  	regexFuncMod := regexp.MustCompile(`\/sap\/bc\/adt\/functions\/groups\/\w*\/fmodules/\w*`)
  1374  	funcMod := regexFuncMod.FindString(path)
  1375  	if funcMod != "" && fileName == "" {
  1376  
  1377  		regexSubObj := regexp.MustCompile(`includes\/\w*`)
  1378  		subObject := regexSubObj.FindString(path)
  1379  		subObject = subObject[len(`includes/`):]
  1380  
  1381  		if readableSource {
  1382  
  1383  			fileName = strings.ToLower(subObject) + ".func.abap"
  1384  		} else {
  1385  			fileName = "FUNC " + subObject + ".abap"
  1386  		}
  1387  
  1388  	}
  1389  	// CLAS
  1390  	regexClas := regexp.MustCompile(`\/sap\/bc\/adt\/oo\/classes\/` + strings.ToLower(objName))
  1391  	clas := regexClas.FindString(path)
  1392  	if clas != "" && fileName == "" {
  1393  		if readableSource {
  1394  
  1395  			fileName = strings.ToLower(objName) + ".clas.abap"
  1396  		} else {
  1397  
  1398  			fileName = "CPUB " + objName + ".abap"
  1399  		}
  1400  
  1401  	}
  1402  
  1403  	// PROGRAM
  1404  	regexProg := regexp.MustCompile(`\/sap\/bc\/adt\/programs\/programs\/` + strings.ToLower(objName))
  1405  	prog := regexProg.FindString(path)
  1406  	if prog != "" && fileName == "" {
  1407  
  1408  		fileName = "REPS " + objName + ".abap"
  1409  
  1410  	}
  1411  
  1412  	// TABLES
  1413  	regexTab := regexp.MustCompile(`\/sap\/bc\/adt\/ddic\/tables\/` + strings.ToLower(objName))
  1414  	tab := regexTab.FindString(path)
  1415  	if tab != "" && fileName == "" {
  1416  
  1417  		fileName = "TABL " + objName + ".asx.json"
  1418  
  1419  	}
  1420  
  1421  	return fileName, nil
  1422  
  1423  }
  1424  
  1425  func getTargetDir(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (string, error) {
  1426  
  1427  	var targetDir string
  1428  
  1429  	repository, err := getRepo(config, client)
  1430  
  1431  	if err != nil {
  1432  		return targetDir, err
  1433  	}
  1434  
  1435  	for _, config := range repository.Result.Config {
  1436  		if config.Key == "VCS_TARGET_DIR" {
  1437  			targetDir = config.Value
  1438  		}
  1439  	}
  1440  
  1441  	return targetDir, nil
  1442  
  1443  }
  1444  
  1445  func checkReadableSource(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (readableSource bool, error error) {
  1446  
  1447  	repoLayout, err := getRepositoryLayout(config, client)
  1448  	if err != nil {
  1449  		return readableSource, errors.Wrap(err, "could not check readable source format")
  1450  	}
  1451  
  1452  	if repoLayout.Layout.ReadableSource == "true" || repoLayout.Layout.ReadableSource == "only" || repoLayout.Layout.ReadableSource == "all" {
  1453  
  1454  		readableSource = true
  1455  
  1456  	} else {
  1457  
  1458  		readableSource = false
  1459  
  1460  	}
  1461  
  1462  	return readableSource, nil
  1463  }
  1464  
  1465  func getRepo(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (repositoryResponse, error) {
  1466  
  1467  	var repositoryResp repositoryResponse
  1468  	url := config.Host +
  1469  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
  1470  		"?sap-client=" + config.Client
  1471  
  1472  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1473  
  1474  	if urlErr != nil {
  1475  
  1476  		return repositoryResp, urlErr
  1477  	}
  1478  
  1479  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1480  	defer func() {
  1481  		if resp != nil && resp.Body != nil {
  1482  			resp.Body.Close()
  1483  		}
  1484  	}()
  1485  
  1486  	if httpErr != nil {
  1487  		return repositoryResponse{}, errors.Wrap(httpErr, "could not get repository")
  1488  	} else if resp == nil {
  1489  		return repositoryResponse{}, errors.New("could not get repository: did not retrieve a HTTP response")
  1490  	}
  1491  
  1492  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repositoryResp)
  1493  	if parsingErr != nil {
  1494  		return repositoryResponse{}, errors.Errorf("%v", parsingErr)
  1495  	}
  1496  
  1497  	return repositoryResp, nil
  1498  
  1499  }
  1500  
  1501  func getRepositoryLayout(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (layoutResponse, error) {
  1502  
  1503  	var repoLayoutResponse layoutResponse
  1504  	url := config.Host +
  1505  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
  1506  		"/layout?sap-client=" + config.Client
  1507  
  1508  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1509  
  1510  	if urlErr != nil {
  1511  
  1512  		return repoLayoutResponse, urlErr
  1513  	}
  1514  
  1515  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1516  
  1517  	defer func() {
  1518  		if resp != nil && resp.Body != nil {
  1519  			resp.Body.Close()
  1520  		}
  1521  	}()
  1522  
  1523  	if httpErr != nil {
  1524  		return layoutResponse{}, errors.Wrap(httpErr, "could not get repository layout")
  1525  	} else if resp == nil {
  1526  		return layoutResponse{}, errors.New("could not get repository layout: did not retrieve a HTTP response")
  1527  	}
  1528  
  1529  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &repoLayoutResponse)
  1530  	if parsingErr != nil {
  1531  		return layoutResponse{}, errors.Errorf("%v", parsingErr)
  1532  	}
  1533  
  1534  	return repoLayoutResponse, nil
  1535  }
  1536  
  1537  func getCommitList(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (commitResponse, error) {
  1538  
  1539  	var commitResp commitResponse
  1540  	url := config.Host +
  1541  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
  1542  		"/getCommit?sap-client=" + config.Client
  1543  
  1544  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1545  	if urlErr != nil {
  1546  
  1547  		return commitResp, urlErr
  1548  	}
  1549  
  1550  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1551  
  1552  	defer func() {
  1553  		if resp != nil && resp.Body != nil {
  1554  			resp.Body.Close()
  1555  		}
  1556  	}()
  1557  
  1558  	if httpErr != nil {
  1559  		return commitResponse{}, errors.Wrap(httpErr, "get repository history failed")
  1560  	} else if resp == nil {
  1561  		return commitResponse{}, errors.New("get repository history failed: did not retrieve a HTTP response")
  1562  	}
  1563  
  1564  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &commitResp)
  1565  	if parsingErr != nil {
  1566  		return commitResponse{}, errors.Errorf("%v", parsingErr)
  1567  	}
  1568  
  1569  	return commitResp, nil
  1570  }
  1571  
  1572  func getObjectDifference(config *gctsExecuteABAPQualityChecksOptions, fromCommit string, toCommit string, client piperhttp.Sender) (objectsResponse, error) {
  1573  	var objectResponse objectsResponse
  1574  
  1575  	url := config.Host +
  1576  		"/sap/bc/cts_abapvcs/repository/" + config.Repository +
  1577  		"/compareCommits?fromCommit=" + fromCommit + "&toCommit=" + toCommit + "&sap-client=" + config.Client
  1578  
  1579  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1580  
  1581  	if urlErr != nil {
  1582  
  1583  		return objectResponse, urlErr
  1584  	}
  1585  
  1586  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1587  
  1588  	defer func() {
  1589  		if resp != nil && resp.Body != nil {
  1590  			resp.Body.Close()
  1591  		}
  1592  	}()
  1593  
  1594  	if httpErr != nil {
  1595  		return objectsResponse{}, errors.Wrap(httpErr, "get object difference failed")
  1596  	} else if resp == nil {
  1597  		return objectsResponse{}, errors.New("get object difference failed: did not retrieve a HTTP response")
  1598  	}
  1599  
  1600  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &objectResponse)
  1601  	if parsingErr != nil {
  1602  		return objectsResponse{}, errors.Errorf("%v", parsingErr)
  1603  	}
  1604  	log.Entry().Info("get object differences: ", objectResponse.Objects)
  1605  	return objectResponse, nil
  1606  }
  1607  
  1608  func getObjectInfo(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender, objectName string, objectType string) (objectInfo, error) {
  1609  
  1610  	var objectMetInfoResponse objectInfo
  1611  	url := config.Host +
  1612  		"/sap/bc/cts_abapvcs/objects/" + objectType + "/" + objectName +
  1613  		"?sap-client=" + config.Client
  1614  
  1615  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1616  
  1617  	if urlErr != nil {
  1618  
  1619  		return objectMetInfoResponse, urlErr
  1620  	}
  1621  
  1622  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1623  
  1624  	defer func() {
  1625  		if resp != nil && resp.Body != nil {
  1626  			resp.Body.Close()
  1627  		}
  1628  	}()
  1629  
  1630  	if httpErr != nil {
  1631  		return objectInfo{}, errors.Wrap(httpErr, "resolve package failed")
  1632  	} else if resp == nil {
  1633  		return objectInfo{}, errors.New("resolve package failed: did not retrieve a HTTP response")
  1634  	}
  1635  
  1636  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &objectMetInfoResponse)
  1637  	if parsingErr != nil {
  1638  		return objectInfo{}, errors.Errorf("%v", parsingErr)
  1639  	}
  1640  	return objectMetInfoResponse, nil
  1641  
  1642  }
  1643  
  1644  func getHistory(config *gctsExecuteABAPQualityChecksOptions, client piperhttp.Sender) (historyResponse, error) {
  1645  
  1646  	var historyResp historyResponse
  1647  	url := config.Host +
  1648  		"/sap/bc/cts_abapvcs/repository/" + config.Repository + "/getHistory?sap-client=" + config.Client
  1649  
  1650  	url, urlErr := addQueryToURL(url, config.QueryParameters)
  1651  
  1652  	if urlErr != nil {
  1653  
  1654  		return historyResp, urlErr
  1655  	}
  1656  
  1657  	resp, httpErr := client.SendRequest("GET", url, nil, nil, nil)
  1658  
  1659  	defer func() {
  1660  		if resp != nil && resp.Body != nil {
  1661  			resp.Body.Close()
  1662  		}
  1663  	}()
  1664  	if httpErr != nil {
  1665  		return historyResponse{}, errors.Wrap(httpErr, "get history failed")
  1666  	} else if resp == nil {
  1667  		return historyResponse{}, errors.New("get history failed: did not retrieve a HTTP response")
  1668  	}
  1669  
  1670  	parsingErr := piperhttp.ParseHTTPResponseBodyJSON(resp, &historyResp)
  1671  	if parsingErr != nil {
  1672  		return historyResponse{}, errors.Errorf("%v", parsingErr)
  1673  	}
  1674  
  1675  	return historyResp, nil
  1676  }
  1677  
  1678  type worklist struct {
  1679  	XMLName             xml.Name `xml:"worklist"`
  1680  	Text                string   `xml:",chardata"`
  1681  	ID                  string   `xml:"id,attr"`
  1682  	Timestamp           string   `xml:"timestamp,attr"`
  1683  	UsedObjectSet       string   `xml:"usedObjectSet,attr"`
  1684  	ObjectSetIsComplete string   `xml:"objectSetIsComplete,attr"`
  1685  	Atcworklist         string   `xml:"atcworklist,attr"`
  1686  	ObjectSets          struct {
  1687  		Text      string `xml:",chardata"`
  1688  		ObjectSet []struct {
  1689  			Text  string `xml:",chardata"`
  1690  			Name  string `xml:"name,attr"`
  1691  			Title string `xml:"title,attr"`
  1692  			Kind  string `xml:"kind,attr"`
  1693  		} `xml:"objectSet"`
  1694  	} `xml:"objectSets"`
  1695  	Objects struct {
  1696  		Text   string `xml:",chardata"`
  1697  		Object []struct {
  1698  			Text        string `xml:",chardata"`
  1699  			URI         string `xml:"uri,attr"`
  1700  			Type        string `xml:"type,attr"`
  1701  			Name        string `xml:"name,attr"`
  1702  			PackageName string `xml:"packageName,attr"`
  1703  			Author      string `xml:"author,attr"`
  1704  			Atcobject   string `xml:"atcobject,attr"`
  1705  			Adtcore     string `xml:"adtcore,attr"`
  1706  			Findings    struct {
  1707  				Text    string `xml:",chardata"`
  1708  				Finding []struct {
  1709  					Text              string `xml:",chardata"`
  1710  					URI               string `xml:"uri,attr"`
  1711  					Location          string `xml:"location,attr"`
  1712  					Processor         string `xml:"processor,attr"`
  1713  					LastChangedBy     string `xml:"lastChangedBy,attr"`
  1714  					Priority          string `xml:"priority,attr"`
  1715  					CheckId           string `xml:"checkId,attr"`
  1716  					CheckTitle        string `xml:"checkTitle,attr"`
  1717  					MessageId         string `xml:"messageId,attr"`
  1718  					MessageTitle      string `xml:"messageTitle,attr"`
  1719  					ExemptionApproval string `xml:"exemptionApproval,attr"`
  1720  					ExemptionKind     string `xml:"exemptionKind,attr"`
  1721  					Checksum          string `xml:"checksum,attr"`
  1722  					QuickfixInfo      string `xml:"quickfixInfo,attr"`
  1723  					Atcfinding        string `xml:"atcfinding,attr"`
  1724  					Link              struct {
  1725  						Text string `xml:",chardata"`
  1726  						Href string `xml:"href,attr"`
  1727  						Rel  string `xml:"rel,attr"`
  1728  						Type string `xml:"type,attr"`
  1729  						Atom string `xml:"atom,attr"`
  1730  					} `xml:"link"`
  1731  					Quickfixes struct {
  1732  						Text      string `xml:",chardata"`
  1733  						Manual    string `xml:"manual,attr"`
  1734  						Automatic string `xml:"automatic,attr"`
  1735  						Pseudo    string `xml:"pseudo,attr"`
  1736  					} `xml:"quickfixes"`
  1737  				} `xml:"finding"`
  1738  			} `xml:"findings"`
  1739  		} `xml:"object"`
  1740  	} `xml:"objects"`
  1741  }
  1742  
  1743  type runResult struct {
  1744  	XMLName xml.Name `xml:"runResult"`
  1745  	Text    string   `xml:",chardata"`
  1746  	Aunit   string   `xml:"aunit,attr"`
  1747  	Program []struct {
  1748  		Text    string `xml:",chardata"`
  1749  		URI     string `xml:"uri,attr"`
  1750  		Type    string `xml:"type,attr"`
  1751  		Name    string `xml:"name,attr"`
  1752  		URIType string `xml:"uriType,attr"`
  1753  		Adtcore string `xml:"adtcore,attr"`
  1754  		Alerts  struct {
  1755  			Text  string `xml:",chardata"`
  1756  			Alert struct {
  1757  				Text            string `xml:",chardata"`
  1758  				HasSyntaxErrors string `xml:"hasSyntaxErrors,attr"`
  1759  				Kind            string `xml:"kind,attr"`
  1760  				Severity        string `xml:"severity,attr"`
  1761  				Title           string `xml:"title"`
  1762  				Details         struct {
  1763  					Text   string `xml:",chardata"`
  1764  					Detail struct {
  1765  						Text     string `xml:",chardata"`
  1766  						AttrText string `xml:"text,attr"`
  1767  					} `xml:"detail"`
  1768  				} `xml:"details"`
  1769  				Stack struct {
  1770  					Text       string `xml:",chardata"`
  1771  					StackEntry struct {
  1772  						Text        string `xml:",chardata"`
  1773  						URI         string `xml:"uri,attr"`
  1774  						Description string `xml:"description,attr"`
  1775  					} `xml:"stackEntry"`
  1776  				} `xml:"stack"`
  1777  			} `xml:"alert"`
  1778  		} `xml:"alerts"`
  1779  
  1780  		TestClasses struct {
  1781  			Text      string `xml:",chardata"`
  1782  			TestClass []struct {
  1783  				Text             string `xml:",chardata"`
  1784  				URI              string `xml:"uri,attr"`
  1785  				Type             string `xml:"type,attr"`
  1786  				Name             string `xml:"name,attr"`
  1787  				URIType          string `xml:"uriType,attr"`
  1788  				NavigationURI    string `xml:"navigationUri,attr"`
  1789  				DurationCategory string `xml:"durationCategory,attr"`
  1790  				RiskLevel        string `xml:"riskLevel,attr"`
  1791  				TestMethods      struct {
  1792  					Text       string `xml:",chardata"`
  1793  					TestMethod []struct {
  1794  						Text          string `xml:",chardata"`
  1795  						URI           string `xml:"uri,attr"`
  1796  						Type          string `xml:"type,attr"`
  1797  						Name          string `xml:"name,attr"`
  1798  						ExecutionTime string `xml:"executionTime,attr"`
  1799  						URIType       string `xml:"uriType,attr"`
  1800  						NavigationURI string `xml:"navigationUri,attr"`
  1801  						Unit          string `xml:"unit,attr"`
  1802  						Alerts        struct {
  1803  							Text  string `xml:",chardata"`
  1804  							Alert []struct {
  1805  								Text     string `xml:",chardata"`
  1806  								Kind     string `xml:"kind,attr"`
  1807  								Severity string `xml:"severity,attr"`
  1808  								Title    string `xml:"title"`
  1809  								Details  struct {
  1810  									Text   string `xml:",chardata"`
  1811  									Detail []struct {
  1812  										Text     string `xml:",chardata"`
  1813  										AttrText string `xml:"text,attr"`
  1814  										Details  struct {
  1815  											Text   string `xml:",chardata"`
  1816  											Detail []struct {
  1817  												Text     string `xml:",chardata"`
  1818  												AttrText string `xml:"text,attr"`
  1819  											} `xml:"detail"`
  1820  										} `xml:"details"`
  1821  									} `xml:"detail"`
  1822  								} `xml:"details"`
  1823  								Stack struct {
  1824  									Text       string `xml:",chardata"`
  1825  									StackEntry struct {
  1826  										Text        string `xml:",chardata"`
  1827  										URI         string `xml:"uri,attr"`
  1828  										Type        string `xml:"type,attr"`
  1829  										Name        string `xml:"name,attr"`
  1830  										Description string `xml:"description,attr"`
  1831  									} `xml:"stackEntry"`
  1832  								} `xml:"stack"`
  1833  							} `xml:"alert"`
  1834  						} `xml:"alerts"`
  1835  					} `xml:"testMethod"`
  1836  				} `xml:"testMethods"`
  1837  			} `xml:"testClass"`
  1838  		} `xml:"testClasses"`
  1839  	} `xml:"program"`
  1840  }
  1841  
  1842  type gctsException struct {
  1843  	Message     string `json:"message"`
  1844  	Description string `json:"description"`
  1845  	Code        int    `json:"code"`
  1846  }
  1847  
  1848  type gctsLogs struct {
  1849  	Time     int    `json:"time"`
  1850  	User     string `json:"user"`
  1851  	Section  string `json:"section"`
  1852  	Action   string `json:"action"`
  1853  	Severity string `json:"severity"`
  1854  	Message  string `json:"message"`
  1855  	Code     string `json:"code"`
  1856  }
  1857  
  1858  type commit struct {
  1859  	ID string `json:"id"`
  1860  }
  1861  
  1862  type commitResponse struct {
  1863  	Commits   []commit      `json:"commits"`
  1864  	ErrorLog  []gctsLogs    `json:"errorLog"`
  1865  	Log       []gctsLogs    `json:"log"`
  1866  	Exception gctsException `json:"exception"`
  1867  }
  1868  
  1869  type objectInfo struct {
  1870  	Pgmid     string `json:"pgmid"`
  1871  	Object    string `json:"object"`
  1872  	ObjName   string `json:"objName"`
  1873  	Srcsystem string `json:"srcsystem"`
  1874  	Author    string `json:"author"`
  1875  	Devclass  string `json:"devclass"`
  1876  }
  1877  
  1878  type repoConfig struct {
  1879  	Key        string  `json:"key"`
  1880  	Value      string  `json:"value"`
  1881  	Cprivate   string  `json:"cprivate"`
  1882  	Cprotected string  `json:"cprotected"`
  1883  	Cvisible   string  `json:"cvisible"`
  1884  	Category   string  `json:"category"`
  1885  	Scope      string  `json:"scope"`
  1886  	ChangedAt  float64 `json:"changeAt"`
  1887  	ChangedBy  string  `json:"changedBy"`
  1888  }
  1889  
  1890  type repository struct {
  1891  	Rid           string       `json:"rid"`
  1892  	Name          string       `json:"name"`
  1893  	Role          string       `json:"role"`
  1894  	Type          string       `json:"type"`
  1895  	Vsid          string       `json:"vsid"`
  1896  	PrivateFlag   string       `json:"privateFlag"`
  1897  	Status        string       `json:"status"`
  1898  	Branch        string       `json:"branch"`
  1899  	Url           string       `json:"url"`
  1900  	CreatedBy     string       `json:"createdBy"`
  1901  	CreatedDate   string       `json:"createdDate"`
  1902  	Config        []repoConfig `json:"config"`
  1903  	Objects       int          `json:"objects"`
  1904  	CurrentCommit string       `json:"currentCommit"`
  1905  }
  1906  
  1907  type repositoryResponse struct {
  1908  	Result    repository    `json:"result"`
  1909  	Exception gctsException `json:"exception"`
  1910  }
  1911  
  1912  type objects struct {
  1913  	Name   string `json:"name"`
  1914  	Type   string `json:"type"`
  1915  	Action string `json:"action"`
  1916  }
  1917  type objectsResponse struct {
  1918  	Objects   []objects     `json:"objects"`
  1919  	Log       []gctsLogs    `json:"log"`
  1920  	Exception gctsException `json:"exception"`
  1921  	ErrorLogs []gctsLogs    `json:"errorLog"`
  1922  }
  1923  
  1924  type repoObject struct {
  1925  	Pgmid       string `json:"pgmid"`
  1926  	Object      string `json:"object"`
  1927  	Type        string `json:"type"`
  1928  	Description string `json:"description"`
  1929  }
  1930  
  1931  type repoObjectResponse struct {
  1932  	Objects   []repoObject  `json:"objects"`
  1933  	Log       []gctsLogs    `json:"log"`
  1934  	Exception gctsException `json:"exception"`
  1935  	ErrorLogs []gctsLogs    `json:"errorLog"`
  1936  }
  1937  
  1938  type layout struct {
  1939  	FormatVersion   int    `json:"formatVersion"`
  1940  	Format          string `json:"format"`
  1941  	ObjectStorage   string `json:"objectStorage"`
  1942  	MetaInformation string `json:"metaInformation"`
  1943  	TableContent    string `json:"tableContent"`
  1944  	Subdirectory    string `json:"subdirectory"`
  1945  	ReadableSource  string `json:"readableSource"`
  1946  	KeepClient      string `json:"keepClient"`
  1947  }
  1948  
  1949  type layoutResponse struct {
  1950  	Layout    layout     `json:"layout"`
  1951  	Log       []gctsLogs `json:"log"`
  1952  	Exception string     `json:"exception"`
  1953  	ErrorLogs []gctsLogs `json:"errorLog"`
  1954  }
  1955  
  1956  type history struct {
  1957  	Rid          string `json:"rid"`
  1958  	CheckoutTime int    `json:"checkoutTime"`
  1959  	FromCommit   string `json:"fromCommit"`
  1960  	ToCommit     string `json:"toCommit"`
  1961  	Caller       string `json:"caller"`
  1962  	Type         string `json:"type"`
  1963  }
  1964  
  1965  type historyResponse struct {
  1966  	Result    []history `xml:"result"`
  1967  	Exception string    `json:"exception"`
  1968  }
  1969  
  1970  type checkstyleError struct {
  1971  	Text     string `xml:",chardata"`
  1972  	Message  string `xml:"message,attr"`
  1973  	Source   string `xml:"source,attr"`
  1974  	Line     string `xml:"line,attr"`
  1975  	Severity string `xml:"severity,attr"`
  1976  }
  1977  
  1978  type file struct {
  1979  	Text  string            `xml:",chardata"`
  1980  	Name  string            `xml:"name,attr"`
  1981  	Error []checkstyleError `xml:"error"`
  1982  }
  1983  
  1984  type checkstyle struct {
  1985  	XMLName xml.Name `xml:"checkstyle"`
  1986  	Text    string   `xml:",chardata"`
  1987  	Version string   `xml:"version,attr"`
  1988  	File    []file   `xml:"file"`
  1989  }