github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/src/com/sap/piper/ConfigurationHelper.groovy (about) 1 package com.sap.piper 2 3 @API 4 class ConfigurationHelper implements Serializable { 5 6 def static SEPARATOR = '/' 7 8 static ConfigurationHelper newInstance(Script step, Map config = [:]) { 9 new ConfigurationHelper(step, config) 10 } 11 12 ConfigurationHelper loadStepDefaults(Map compatibleParameters = [:], String stageName = step.env.STAGE_NAME) { 13 DefaultValueCache.prepare(step) 14 this.config = ConfigurationLoader.defaultGeneralConfiguration() 15 mixin(ConfigurationLoader.defaultGeneralConfiguration(), null, compatibleParameters) 16 mixin(ConfigurationLoader.defaultStepConfiguration(null, name), null, compatibleParameters) 17 mixin(ConfigurationLoader.defaultStageConfiguration(null, stageName), null, compatibleParameters) 18 return this 19 } 20 21 private Map config 22 private Script step 23 private String name 24 private Map validationResults = null 25 private String dependingOn 26 27 private ConfigurationHelper(Script step, Map config){ 28 this.config = config ?: [:] 29 this.step = step 30 this.name = step.STEP_NAME 31 if(!this.name) throw new IllegalArgumentException('Step has no public name property!') 32 } 33 34 ConfigurationHelper collectValidationFailures() { 35 validationResults = validationResults ?: [:] 36 return this 37 } 38 39 ConfigurationHelper mixinGeneralConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){ 40 Map generalConfiguration = ConfigurationLoader.generalConfiguration([commonPipelineEnvironment: commonPipelineEnvironment]) 41 return mixin(generalConfiguration, filter, compatibleParameters) 42 } 43 44 ConfigurationHelper mixinStageConfig(commonPipelineEnvironment, stageName, Set filter = null, Map compatibleParameters = [:]){ 45 Map stageConfiguration = ConfigurationLoader.stageConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], stageName) 46 return mixin(stageConfiguration, filter, compatibleParameters) 47 } 48 49 ConfigurationHelper mixinStepConfig(commonPipelineEnvironment, Set filter = null, Map compatibleParameters = [:]){ 50 Map stepConfiguration = ConfigurationLoader.stepConfiguration([commonPipelineEnvironment: commonPipelineEnvironment], name) 51 return mixin(stepConfiguration, filter, compatibleParameters) 52 } 53 54 ConfigurationHelper mixin(Map parameters, Set filter = null, Map compatibleParameters = [:]){ 55 if (parameters.size() > 0 && compatibleParameters.size() > 0) { 56 parameters = ConfigurationMerger.merge(handleCompatibility(compatibleParameters, parameters), null, parameters) 57 } 58 if (filter) { 59 filter.add('collectTelemetryData') 60 } 61 config = ConfigurationMerger.merge(parameters, filter, config) 62 return this 63 } 64 65 private Map handleCompatibility(Map compatibleParameters, String paramStructure = '', Map configMap, Map newConfigMap = [:] ) { 66 Map newConfig = [:] 67 compatibleParameters.each {entry -> 68 if (entry.getValue() instanceof Map) { 69 def internalParamStructure = (paramStructure ? paramStructure + '.' : '') + entry.getKey() 70 newConfig[entry.getKey()] = handleCompatibility(entry.getValue(), internalParamStructure, configMap, newConfig) 71 } else { 72 def configSubMap = configMap 73 for(String key in paramStructure.tokenize('.')){ 74 configSubMap = configSubMap?.get(key) 75 } 76 if (configSubMap == null || (configSubMap != null && configSubMap[entry.getKey()] == null)) { 77 def value = getConfigPropertyNested(configMap, entry.getValue()) 78 if(null == value) 79 value = getConfigPropertyNested(newConfigMap, entry.getValue()) 80 if (value != null) { 81 newConfig[entry.getKey()] = value 82 def paramName = (paramStructure ? paramStructure + '.' : '') + entry.getKey() 83 this.step.echo ("[INFO] The parameter '${entry.getValue()}' is COMPATIBLE to the parameter '${paramName}'") 84 } 85 } 86 } 87 } 88 return newConfig 89 } 90 91 ConfigurationHelper mixin(String key){ 92 def parts = tokenizeKey(key) 93 def targetMap = config 94 if(parts.size() > 1) { 95 key = parts.last() 96 parts.remove(key) 97 targetMap = getConfigPropertyNested(config, parts.join(SEPARATOR)) 98 } 99 def dependentValue = config[dependingOn] 100 if(targetMap[key] == null && dependentValue && config[dependentValue]) 101 targetMap[key] = config[dependentValue][key] 102 103 dependingOn = null 104 return this 105 } 106 107 ConfigurationHelper dependingOn(dependentKey){ 108 dependingOn = dependentKey 109 return this 110 } 111 112 ConfigurationHelper addIfEmpty(key, value){ 113 if (config[key] instanceof Boolean) { 114 return this 115 } else if (!config[key]){ 116 config[key] = value 117 } 118 return this 119 } 120 121 ConfigurationHelper addIfNull(key, value){ 122 if (config[key] == null){ 123 config[key] = value 124 } 125 return this 126 } 127 128 Map use(){ 129 handleValidationFailures() 130 MapUtils.traverse(config, { v -> (v instanceof GString) ? v.toString() : v }) 131 if(config.verbose) step.echo "[${name}] Configuration: ${config}" 132 return MapUtils.deepCopy(config) 133 } 134 135 /* private */ def getConfigPropertyNested(key) { 136 return getConfigPropertyNested(config, key) 137 } 138 139 /* private */ static getConfigPropertyNested(Map config, key) { 140 141 List parts = tokenizeKey(key) 142 143 if (config[parts.head()] != null) { 144 145 if (config[parts.head()] in Map && !parts.tail().isEmpty()) { 146 return getConfigPropertyNested(config[parts.head()], parts.tail().join(SEPARATOR)) 147 } 148 149 if (config[parts.head()].class == String) { 150 return (config[parts.head()] as String).trim() 151 } 152 } 153 return config[parts.head()] 154 } 155 156 /* private */ static tokenizeKey(String key) { 157 // reason for cast to CharSequence: String#tokenize(./.) causes a deprecation warning. 158 List parts = (key in String) ? (key as CharSequence).tokenize(SEPARATOR) : ([key] as List) 159 return parts 160 } 161 162 private void existsMandatoryProperty(key, errorMessage) { 163 164 def paramValue = getConfigPropertyNested(config, key) 165 166 if (paramValue == null) { 167 if(! errorMessage) errorMessage = "ERROR - NO VALUE AVAILABLE FOR ${key}" 168 169 def iae = new IllegalArgumentException(errorMessage) 170 if(validationResults == null) { 171 throw iae 172 } 173 validationResults.put(key, iae) 174 } 175 } 176 177 ConfigurationHelper withMandatoryProperty(key, errorMessage = null, condition = null){ 178 if(condition){ 179 if(condition(this.config)) 180 existsMandatoryProperty(key, errorMessage) 181 }else{ 182 existsMandatoryProperty(key, errorMessage) 183 } 184 return this 185 } 186 187 ConfigurationHelper withPropertyInValues(String key, Set values){ 188 withMandatoryProperty(key) 189 def value = config[key] instanceof GString ? config[key].toString() : config[key] 190 if(! (value in values) ) { 191 throw new IllegalArgumentException("Invalid ${key} = '${value}'. Valid '${key}' values are: ${values}.") 192 } 193 return this 194 } 195 196 private handleValidationFailures() { 197 if(! validationResults) return 198 if(validationResults.size() == 1) throw validationResults.values().first() 199 String msg = 'ERROR - NO VALUE AVAILABLE FOR: ' + validationResults.keySet().join(', ') 200 IllegalArgumentException iae = new IllegalArgumentException(msg) 201 validationResults.each { e -> iae.addSuppressed(e.value) } 202 throw iae 203 } 204 }