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

     1  package cmd
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/url"
     6  	"reflect"
     7  	"strings"
     8  	"time"
     9  
    10  	abapbuild "github.com/SAP/jenkins-library/pkg/abap/build"
    11  	"github.com/SAP/jenkins-library/pkg/abaputils"
    12  	"github.com/SAP/jenkins-library/pkg/command"
    13  	piperhttp "github.com/SAP/jenkins-library/pkg/http"
    14  	"github.com/SAP/jenkins-library/pkg/log"
    15  	"github.com/SAP/jenkins-library/pkg/piperutils"
    16  	"github.com/SAP/jenkins-library/pkg/telemetry"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  type abapEnvironmentBuildUtils interface {
    21  	command.ExecRunner
    22  	abaputils.Communication
    23  	abapbuild.HTTPSendLoader
    24  	piperutils.FileUtils
    25  	getMaxRuntime() time.Duration
    26  	getPollingInterval() time.Duration
    27  	publish()
    28  }
    29  
    30  type abapEnvironmentBuildUtilsBundle struct {
    31  	*command.Command
    32  	*piperhttp.Client
    33  	*abaputils.AbapUtils
    34  	*piperutils.Files
    35  	maxRuntime      time.Duration
    36  	pollingInterval time.Duration
    37  	storePublish    publish
    38  }
    39  
    40  type publish struct {
    41  	stepName  string
    42  	workspace string
    43  	reports   []piperutils.Path
    44  	links     []piperutils.Path
    45  }
    46  
    47  func (p *publish) publish(utils piperutils.FileUtils) {
    48  	if p.stepName != "" {
    49  		piperutils.PersistReportsAndLinks(p.stepName, p.workspace, utils, p.reports, p.links)
    50  	}
    51  }
    52  
    53  func (aEBUB *abapEnvironmentBuildUtilsBundle) publish() {
    54  	aEBUB.storePublish.publish(aEBUB)
    55  }
    56  
    57  func (aEBUB *abapEnvironmentBuildUtilsBundle) getMaxRuntime() time.Duration {
    58  	return aEBUB.maxRuntime
    59  }
    60  
    61  func (aEBUB *abapEnvironmentBuildUtilsBundle) getPollingInterval() time.Duration {
    62  	return aEBUB.pollingInterval
    63  }
    64  
    65  func (aEBUB *abapEnvironmentBuildUtilsBundle) PersistReportsAndLinks(stepName, workspace string, reports, links []piperutils.Path) {
    66  	// abapbuild.PersistReportsAndLinks(stepName, workspace, reports, links)
    67  	if aEBUB.storePublish.stepName == "" {
    68  		aEBUB.storePublish.stepName = stepName
    69  		aEBUB.storePublish.workspace = workspace
    70  		aEBUB.storePublish.reports = reports
    71  		aEBUB.storePublish.links = links
    72  	} else {
    73  		aEBUB.storePublish.reports = append(aEBUB.storePublish.reports, reports...)
    74  		aEBUB.storePublish.links = append(aEBUB.storePublish.reports, links...)
    75  	}
    76  }
    77  
    78  func newAbapEnvironmentBuildUtils(maxRuntime time.Duration, pollingInterval time.Duration) abapEnvironmentBuildUtils {
    79  	utils := abapEnvironmentBuildUtilsBundle{
    80  		Command: &command.Command{},
    81  		Client:  &piperhttp.Client{},
    82  		AbapUtils: &abaputils.AbapUtils{
    83  			Exec: &command.Command{},
    84  		},
    85  		maxRuntime:      maxRuntime * time.Minute,
    86  		pollingInterval: pollingInterval * time.Second,
    87  		storePublish:    publish{},
    88  	}
    89  	// Reroute command output to logging framework
    90  	utils.Stdout(log.Writer())
    91  	utils.Stderr(log.Writer())
    92  	return &utils
    93  }
    94  
    95  func abapEnvironmentBuild(config abapEnvironmentBuildOptions, telemetryData *telemetry.CustomData, cpe *abapEnvironmentBuildCommonPipelineEnvironment) {
    96  	utils := newAbapEnvironmentBuildUtils(time.Duration(config.MaxRuntimeInMinutes), time.Duration(config.PollingIntervalInSeconds))
    97  	if err := runAbapEnvironmentBuild(&config, telemetryData, utils, cpe); err != nil {
    98  		log.Entry().WithError(err).Fatal("step execution failed")
    99  	}
   100  }
   101  
   102  func runAbapEnvironmentBuild(config *abapEnvironmentBuildOptions, telemetryData *telemetry.CustomData, utils abapEnvironmentBuildUtils, cpe *abapEnvironmentBuildCommonPipelineEnvironment) error {
   103  
   104  	log.Entry().Info("╔════════════════════════════════╗")
   105  	log.Entry().Info("║ abapEnvironmentBuild           ║")
   106  	log.Entry().Info("╠════════════════════════════════╣")
   107  	log.Entry().Infof("║ %-30v ║", config.Phase)
   108  	log.Entry().Info("╙────────────────────────────────╜")
   109  
   110  	conn := new(abapbuild.Connector)
   111  	if err := initConnection(conn, config, utils); err != nil {
   112  		return errors.Wrap(err, "Connector initialization for communication with the ABAP system failed")
   113  	}
   114  
   115  	valuesList, err := evaluateAddonDescriptor(config)
   116  	if err != nil {
   117  		return errors.Wrap(err, "Error during the evaluation of the AddonDescriptor")
   118  	}
   119  
   120  	finalValues, err := runBuilds(conn, config, utils, valuesList)
   121  	// files should be published, even if an error occured
   122  	utils.publish()
   123  	if err != nil {
   124  		return err
   125  	}
   126  
   127  	cpe.abap.buildValues, err = convertValuesForCPE(finalValues)
   128  	if err != nil {
   129  		return errors.Wrap(err, "Error during the conversion of the values for the commonPipelineenvironment")
   130  	}
   131  	return nil
   132  }
   133  
   134  func runBuilds(conn *abapbuild.Connector, config *abapEnvironmentBuildOptions, utils abapEnvironmentBuildUtils, valuesList [][]abapbuild.Value) ([]abapbuild.Value, error) {
   135  	var finalValues []abapbuild.Value
   136  	// No addonDescriptor involved
   137  	if len(valuesList) == 0 {
   138  		values, err := generateValuesOnlyFromConfig(config)
   139  		if err != nil {
   140  			return finalValues, errors.Wrap(err, "Generating the values from config failed")
   141  		}
   142  		finalValues, err = runBuild(conn, config, utils, values)
   143  		if err != nil {
   144  			return finalValues, errors.Wrap(err, "Error during execution of build framework")
   145  		}
   146  	} else {
   147  		// Run several times for each repository in the addonDescriptor
   148  		var errstrings []string
   149  		vE := valuesEvaluator{}
   150  		vE.m = make(map[string]string)
   151  		for _, values := range valuesList {
   152  			cummulatedValues, err := generateValuesWithAddonDescriptor(config, values)
   153  			if err != nil {
   154  				return finalValues, errors.Wrap(err, "Error generating input values")
   155  			}
   156  			finalValuesForOneBuild, err := runBuild(conn, config, utils, cummulatedValues)
   157  			if err != nil {
   158  				err = errors.Wrapf(err, "Build with input values %s failed", values2string(values))
   159  				if config.StopOnFirstError {
   160  					return finalValues, err
   161  				}
   162  				errstrings = append(errstrings, err.Error())
   163  			}
   164  			finalValuesForOneBuild = removeAddonDescriptorValues(finalValuesForOneBuild, values)
   165  			// This means: probably values are duplicated, but the first one wins -> perhaps change this in the future if needed
   166  			if err := vE.appendValuesIfNotPresent(finalValuesForOneBuild, false); err != nil {
   167  				errstrings = append(errstrings, err.Error())
   168  			}
   169  		}
   170  		finalValues = vE.generateValueSlice()
   171  		if len(errstrings) > 0 {
   172  			finalError := errors.Errorf("%d out %d build runs failed:\n%s", len(errstrings), len(valuesList), (strings.Join(errstrings, "\n")))
   173  			return finalValues, finalError
   174  		}
   175  	}
   176  	return finalValues, nil
   177  }
   178  
   179  func initConnection(conn *abapbuild.Connector, config *abapEnvironmentBuildOptions, utils abapEnvironmentBuildUtils) error {
   180  	var connConfig abapbuild.ConnectorConfiguration
   181  	connConfig.CfAPIEndpoint = config.CfAPIEndpoint
   182  	connConfig.CfOrg = config.CfOrg
   183  	connConfig.CfSpace = config.CfSpace
   184  	connConfig.CfServiceInstance = config.CfServiceInstance
   185  	connConfig.CfServiceKeyName = config.CfServiceKeyName
   186  	connConfig.Host = config.Host
   187  	connConfig.Username = config.Username
   188  	connConfig.Password = config.Password
   189  	connConfig.MaxRuntimeInMinutes = config.MaxRuntimeInMinutes
   190  	connConfig.CertificateNames = config.CertificateNames
   191  	connConfig.Parameters = url.Values{}
   192  	if len(config.AbapSourceClient) != 0 {
   193  		connConfig.Parameters.Add("sap-client", config.AbapSourceClient)
   194  	}
   195  
   196  	if err := conn.InitBuildFramework(connConfig, utils, utils); err != nil {
   197  		return err
   198  	}
   199  
   200  	conn.MaxRuntime = utils.getMaxRuntime()
   201  	conn.PollingInterval = utils.getPollingInterval()
   202  	return nil
   203  }
   204  
   205  // ***********************************Run Build***************************************************************
   206  func runBuild(conn *abapbuild.Connector, config *abapEnvironmentBuildOptions, utils abapEnvironmentBuildUtils, values []abapbuild.Value) ([]abapbuild.Value, error) {
   207  	var finalValues []abapbuild.Value
   208  	var inputValues abapbuild.Values
   209  	inputValues.Values = values
   210  
   211  	build := myBuild{
   212  		Build: abapbuild.Build{
   213  			Connector: *conn,
   214  		},
   215  		abapEnvironmentBuildOptions: config,
   216  	}
   217  	if err := build.Start(inputValues); err != nil {
   218  		return finalValues, err
   219  	}
   220  
   221  	if err := build.Poll(); err != nil {
   222  		return finalValues, errors.Wrap(err, "Error during the polling for the final state of the build run")
   223  	}
   224  
   225  	if err := build.PrintLogs(); err != nil {
   226  		return finalValues, errors.Wrap(err, "Error printing the logs")
   227  	}
   228  
   229  	errBuildRun := build.EvaluteIfBuildSuccessful()
   230  
   231  	if err := build.Download(); err != nil {
   232  		if errBuildRun != nil {
   233  			errWraped := errors.Errorf("Download failed after execution of build failed: %v. Build error: %v", err, errBuildRun)
   234  			return finalValues, errWraped
   235  		}
   236  		return finalValues, err
   237  	}
   238  	if err := build.Publish(utils); err != nil {
   239  		return finalValues, err
   240  	}
   241  
   242  	finalValues, err := build.GetFinalValues()
   243  	if err != nil {
   244  		return finalValues, err
   245  	}
   246  	return finalValues, errBuildRun
   247  }
   248  
   249  type myBuild struct {
   250  	abapbuild.Build
   251  	*abapEnvironmentBuildOptions
   252  }
   253  
   254  func (b *myBuild) Start(values abapbuild.Values) error {
   255  	if err := b.Build.Start(b.abapEnvironmentBuildOptions.Phase, values); err != nil {
   256  		return errors.Wrap(err, "Error starting the build framework")
   257  	}
   258  	return nil
   259  }
   260  
   261  func (b *myBuild) EvaluteIfBuildSuccessful() error {
   262  	if err := b.Build.EvaluteIfBuildSuccessful(b.TreatWarningsAsError); err != nil {
   263  		return errors.Wrap(err, "Build ended without success")
   264  	}
   265  	return nil
   266  }
   267  
   268  func (b *myBuild) Download() error {
   269  	if b.DownloadAllResultFiles {
   270  		if err := b.DownloadAllResults(b.SubDirectoryForDownload, b.FilenamePrefixForDownload); err != nil {
   271  			return errors.Wrap(err, "Error during the download of the result files")
   272  		}
   273  	} else {
   274  		if err := b.DownloadResults(b.DownloadResultFilenames, b.SubDirectoryForDownload, b.FilenamePrefixForDownload); err != nil {
   275  			return errors.Wrapf(err, "Error during the download of the result files %s", b.DownloadResultFilenames)
   276  		}
   277  	}
   278  	return nil
   279  }
   280  
   281  func (b *myBuild) Publish(utils abapEnvironmentBuildUtils) error {
   282  	if b.PublishAllDownloadedResultFiles {
   283  		b.PublishAllDownloadedResults("abapEnvironmentBuild", utils)
   284  	} else {
   285  		if err := b.PublishDownloadedResults("abapEnvironmentBuild", b.PublishResultFilenames, utils); err != nil {
   286  			return errors.Wrapf(err, "Error during the publish of the result files %s", b.PublishResultFilenames)
   287  		}
   288  	}
   289  	return nil
   290  }
   291  
   292  func (b *myBuild) GetFinalValues() ([]abapbuild.Value, error) {
   293  	var values []abapbuild.Value
   294  	if err := b.GetValues(); err != nil {
   295  		return values, errors.Wrapf(err, "Error getting the values from build framework")
   296  	}
   297  	return b.Build.Values, nil
   298  }
   299  
   300  // **********************************Values Handling**************************************************************
   301  func convertValuesForCPE(values []abapbuild.Value) (string, error) {
   302  	type cpeValue struct {
   303  		ValueID string `json:"value_id"`
   304  		Value   string `json:"value"`
   305  	}
   306  	var cpeValues []cpeValue
   307  	byt, err := json.Marshal(&values)
   308  	if err != nil {
   309  		return "", errors.Wrapf(err, "Error converting the values from the build framework")
   310  	}
   311  	if err := json.Unmarshal(byt, &cpeValues); err != nil {
   312  		return "", errors.Wrapf(err, "Error converting the values from the build framework into the structure for the commonPipelineEnvironment")
   313  	}
   314  	jsonBytes, err := json.Marshal(cpeValues)
   315  	if err != nil {
   316  		return "", errors.Wrapf(err, "Error converting the converted values")
   317  	}
   318  	return string(jsonBytes), nil
   319  }
   320  
   321  func removeAddonDescriptorValues(finalValuesFromBuild []abapbuild.Value, valuesFromAddonDescriptor []abapbuild.Value) []abapbuild.Value {
   322  	var finalValues []abapbuild.Value
   323  	mapForAddonDescriptorValues := make(map[string]string)
   324  	for _, value := range valuesFromAddonDescriptor {
   325  		mapForAddonDescriptorValues[value.ValueID] = value.Value
   326  	}
   327  	for _, value := range finalValuesFromBuild {
   328  		_, present := mapForAddonDescriptorValues[value.ValueID]
   329  		if !present {
   330  			finalValues = append(finalValues, value)
   331  		}
   332  	}
   333  	return finalValues
   334  }
   335  
   336  func generateValuesWithAddonDescriptor(config *abapEnvironmentBuildOptions, repoValues []abapbuild.Value) ([]abapbuild.Value, error) {
   337  	var values []abapbuild.Value
   338  	vE := valuesEvaluator{}
   339  	// values from config
   340  	if err := vE.initialize(config.Values); err != nil {
   341  		return values, err
   342  	}
   343  	// values from addondescriptor
   344  	if err := vE.appendValuesIfNotPresent(repoValues, true); err != nil {
   345  		return values, err
   346  	}
   347  	// values from commonepipelineEnvironment
   348  	if err := vE.appendStringValuesIfNotPresent(config.CpeValues, false); err != nil {
   349  		return values, err
   350  	}
   351  	values = vE.generateValueSlice()
   352  	return values, nil
   353  }
   354  
   355  func generateValuesOnlyFromConfig(config *abapEnvironmentBuildOptions) ([]abapbuild.Value, error) {
   356  	return generateValuesWithAddonDescriptor(config, []abapbuild.Value{})
   357  }
   358  
   359  func generateValuesFromString(stringValues string) ([]abapbuild.Value, error) {
   360  	var values []abapbuild.Value
   361  	if len(stringValues) > 0 {
   362  		if err := json.Unmarshal([]byte(stringValues), &values); err != nil {
   363  			log.SetErrorCategory(log.ErrorConfiguration)
   364  			return values, errors.Wrapf(err, "Could not convert the values %s", stringValues)
   365  		}
   366  	}
   367  	return values, nil
   368  }
   369  
   370  type valuesEvaluator struct {
   371  	m map[string]string
   372  }
   373  
   374  func (vE *valuesEvaluator) initialize(stringValues string) error {
   375  	values, err := generateValuesFromString(stringValues)
   376  	if err != nil {
   377  		return errors.Wrapf(err, "Error converting the vales from the config")
   378  	}
   379  	vE.m = make(map[string]string)
   380  	for _, value := range values {
   381  		if (len(value.ValueID) == 0) || (len(value.Value) == 0) {
   382  			log.SetErrorCategory(log.ErrorConfiguration)
   383  			return errors.Errorf("Values %s from config have not the right format", stringValues)
   384  		}
   385  		_, present := vE.m[value.ValueID]
   386  		if present {
   387  			log.SetErrorCategory(log.ErrorConfiguration)
   388  			return errors.Errorf("Value_id %s is not unique in the config", value.ValueID)
   389  		}
   390  		vE.m[value.ValueID] = value.Value
   391  	}
   392  	return nil
   393  }
   394  
   395  func (vE *valuesEvaluator) appendStringValuesIfNotPresent(stringValues string, throwErrorIfPresent bool) error {
   396  	var values []abapbuild.Value
   397  	values, err := generateValuesFromString(stringValues)
   398  	if err != nil {
   399  		return errors.Wrapf(err, "Error converting the vales from the commonPipelineEnvironment")
   400  	}
   401  	if err := vE.appendValuesIfNotPresent(values, throwErrorIfPresent); err != nil {
   402  		return err
   403  	}
   404  	return nil
   405  }
   406  
   407  func (vE *valuesEvaluator) appendValuesIfNotPresent(values []abapbuild.Value, throwErrorIfPresent bool) error {
   408  	for _, value := range values {
   409  		if value.ValueID == "PHASE" || value.ValueID == "BUILD_FRAMEWORK_MODE" {
   410  			continue
   411  		}
   412  		_, present := vE.m[value.ValueID]
   413  		if present {
   414  			if throwErrorIfPresent {
   415  				return errors.Errorf("Value_id %s already existed in the config", value.ValueID)
   416  			}
   417  			log.Entry().Infof("Value '%s':'%s' already existed -> discard this value", value.ValueID, value.Value)
   418  		} else {
   419  			vE.m[value.ValueID] = value.Value
   420  		}
   421  	}
   422  	return nil
   423  }
   424  
   425  func (vE *valuesEvaluator) generateValueSlice() []abapbuild.Value {
   426  	var values []abapbuild.Value
   427  	var value abapbuild.Value
   428  	for k, v := range vE.m {
   429  		value.ValueID = k
   430  		value.Value = v
   431  		values = append(values, value)
   432  	}
   433  	return values
   434  }
   435  
   436  // **********************************Evaluate AddonDescriptor**************************************************************
   437  type myRepo struct {
   438  	abaputils.Repository
   439  }
   440  
   441  type condition struct {
   442  	Field    string `json:"field"`
   443  	Operator string `json:"operator"`
   444  	Value    string `json:"value"`
   445  }
   446  
   447  type useField struct {
   448  	Use    string `json:"use"`
   449  	Rename string `json:"renameTo"`
   450  }
   451  
   452  func evaluateAddonDescriptor(config *abapEnvironmentBuildOptions) ([][]abapbuild.Value, error) {
   453  	var listOfValuesList [][]abapbuild.Value
   454  	if len(config.AddonDescriptor) == 0 && len(config.UseFieldsOfAddonDescriptor) > 0 {
   455  		return listOfValuesList, errors.New("Config contains UseFieldsOfAddonDescriptor but no addonDescriptor is provided in the commonPipelineEnvironment")
   456  	}
   457  	if len(config.AddonDescriptor) > 0 {
   458  		addonDescriptor := new(abaputils.AddonDescriptor)
   459  		if err := addonDescriptor.InitFromJSONstring(config.AddonDescriptor); err != nil {
   460  			log.SetErrorCategory(log.ErrorConfiguration)
   461  			return listOfValuesList, errors.Wrap(err, "Error during the conversion of the AddonDescriptor")
   462  		}
   463  		for _, repo := range addonDescriptor.Repositories {
   464  			myRepo := myRepo{
   465  				Repository: repo,
   466  			}
   467  			use, err := myRepo.checkCondition(config)
   468  			if err != nil {
   469  				return listOfValuesList, errors.Wrapf(err, "Checking of ConditionOnAddonDescriptor failed")
   470  			}
   471  			if use {
   472  				values, err := myRepo.generateValues(config)
   473  				if err != nil {
   474  					return listOfValuesList, errors.Wrap(err, "Error generating values from AddonDescriptor")
   475  				}
   476  				if len(values) > 0 {
   477  					listOfValuesList = append(listOfValuesList, values)
   478  				}
   479  			}
   480  		}
   481  	}
   482  	return listOfValuesList, nil
   483  }
   484  
   485  func (mR *myRepo) checkCondition(config *abapEnvironmentBuildOptions) (bool, error) {
   486  	var conditions []condition
   487  	if len(config.ConditionOnAddonDescriptor) > 0 {
   488  		if err := json.Unmarshal([]byte(config.ConditionOnAddonDescriptor), &conditions); err != nil {
   489  			log.SetErrorCategory(log.ErrorConfiguration)
   490  			return false, errors.Wrapf(err, "Conversion of ConditionOnAddonDescriptor in the config failed")
   491  		}
   492  		for _, cond := range conditions {
   493  			if cond.Field == "" || cond.Operator == "" || cond.Value == "" {
   494  				log.SetErrorCategory(log.ErrorConfiguration)
   495  				return false, errors.Errorf("Invalid condition for field %s with operator %s and value %s", cond.Field, cond.Operator, cond.Value)
   496  			}
   497  			use, err := mR.amI(cond.Field, cond.Operator, cond.Value)
   498  			if err != nil {
   499  				return false, errors.Wrapf(err, "Checking the field %s failed", cond.Field)
   500  			}
   501  			if !use {
   502  				log.Entry().Infof("addonDescriptor with the name %s does not fulfil the requierement %s%s%s from the ConditionOnAddonDescriptor, therefore it is not used", mR.Name, cond.Field, cond.Operator, cond.Value)
   503  				return false, nil
   504  			}
   505  			log.Entry().Infof("addonDescriptor with the name %s does fulfil the requierement %s%s%s in the ConditionOnAddonDescriptor", mR.Name, cond.Field, cond.Operator, cond.Value)
   506  		}
   507  	}
   508  	return true, nil
   509  }
   510  
   511  func (mR *myRepo) generateValues(config *abapEnvironmentBuildOptions) ([]abapbuild.Value, error) {
   512  	var values []abapbuild.Value
   513  	var useFields []useField
   514  	if len(config.UseFieldsOfAddonDescriptor) == 0 {
   515  		log.Entry().Infof("UseFieldsOfAddonDescriptor is empty, nothing is used from the addonDescriptor")
   516  	} else {
   517  		if err := json.Unmarshal([]byte(config.UseFieldsOfAddonDescriptor), &useFields); err != nil {
   518  			log.SetErrorCategory(log.ErrorConfiguration)
   519  			return values, errors.Wrapf(err, "Conversion of UseFieldsOfAddonDescriptor in the config failed")
   520  		}
   521  		m := make(map[string]string)
   522  		for _, uF := range useFields {
   523  			if uF.Use == "" || uF.Rename == "" {
   524  				log.SetErrorCategory(log.ErrorConfiguration)
   525  				return values, errors.Errorf("Invalid UseFieldsOfAddonDescriptor for use %s and renameTo %s", uF.Use, uF.Rename)
   526  			}
   527  			m[uF.Use] = uF.Rename
   528  		}
   529  
   530  		fields := reflect.ValueOf(mR.Repository)
   531  		typeOfS := fields.Type()
   532  		for i := 0; i < fields.NumField(); i++ {
   533  			var value abapbuild.Value
   534  			ValueID := typeOfS.Field(i).Name
   535  			rename, present := m[ValueID]
   536  			if present {
   537  				log.Entry().Infof("Use field %s from addonDescriptor and rename it to %s, the value is %s", ValueID, rename, fields.Field(i).String())
   538  				value.ValueID = rename
   539  				value.Value = fields.Field(i).String()
   540  				values = append(values, value)
   541  			}
   542  		}
   543  		if len(values) != len(useFields) {
   544  			log.SetErrorCategory(log.ErrorConfiguration)
   545  			return values, errors.Errorf("Not all fields in UseFieldsOfAddonDescriptor have been found. Probably a 'use' was used which does not exist")
   546  		}
   547  	}
   548  	return values, nil
   549  }
   550  
   551  func (mR *myRepo) getField(field string) string {
   552  	r := reflect.ValueOf(mR)
   553  	f := reflect.Indirect(r).FieldByName(field)
   554  	return string(f.String())
   555  }
   556  
   557  func (mR *myRepo) amI(field string, operator string, comp string) (bool, error) {
   558  	operators := OperatorCallback{
   559  		"==": Equal,
   560  		"!=": Unequal,
   561  	}
   562  	name := mR.getField(field)
   563  	if fn, ok := operators[operator]; ok {
   564  		return fn(name, comp), nil
   565  	}
   566  	log.SetErrorCategory(log.ErrorConfiguration)
   567  	return false, errors.Errorf("Invalid operator %s", operator)
   568  }
   569  
   570  type OperatorCallback map[string]func(string, string) bool
   571  
   572  func Equal(a, b string) bool {
   573  	return a == b
   574  }
   575  
   576  func Unequal(a, b string) bool {
   577  	return a != b
   578  }
   579  
   580  func values2string(values []abapbuild.Value) string {
   581  	var result string
   582  	for index, value := range values {
   583  		if index > 0 {
   584  			result = result + "; "
   585  		}
   586  		result = result + value.ValueID + " = " + value.Value
   587  	}
   588  	return result
   589  }