/* * Copyright 2016 ThoughtWorks, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.thoughtworks.go.domain.scm; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import com.thoughtworks.go.config.*; import com.thoughtworks.go.config.builder.ConfigurationPropertyBuilder; import com.thoughtworks.go.config.materials.AbstractMaterialConfig; import com.thoughtworks.go.config.validation.NameTypeValidator; import com.thoughtworks.go.domain.ConfigErrors; import com.thoughtworks.go.domain.config.Configuration; import com.thoughtworks.go.domain.config.ConfigurationProperty; import com.thoughtworks.go.domain.config.ConfigurationValue; import com.thoughtworks.go.domain.config.PluginConfiguration; import com.thoughtworks.go.domain.ConfigurationDisplayUtil; import com.thoughtworks.go.plugin.access.scm.SCMConfiguration; import com.thoughtworks.go.plugin.access.scm.SCMConfigurations; import com.thoughtworks.go.plugin.access.scm.SCMMetadataStore; import com.thoughtworks.go.plugin.api.config.Property; import com.thoughtworks.go.util.CachedDigestUtils; import com.thoughtworks.go.util.ListUtil; import com.thoughtworks.go.util.StringUtil; import javax.annotation.PostConstruct; import java.io.Serializable; import java.util.*; import static com.thoughtworks.go.util.ListUtil.join; import static java.lang.String.format; import static org.apache.commons.lang.StringUtils.isBlank; import static org.apache.commons.lang.StringUtils.isEmpty; @ConfigTag("scm") @ConfigReferenceCollection(collectionName = "scms", idFieldName = "id") public class SCM implements Serializable, Validatable { public static final String SCM_ID = "scmId"; public static final String NAME = "name"; public static final String AUTO_UPDATE = "autoUpdate"; public static final String PLUGIN_CONFIGURATION = "pluginConfiguration"; public static final String VALUE_KEY = "value"; public static final String ERRORS_KEY = "errors"; private ConfigErrors errors = new ConfigErrors(); @ConfigAttribute(value = "id", allowNull = true) private String id; @ConfigAttribute(value = "name", allowNull = false) private String name; @ConfigAttribute(value = "autoUpdate", optional = true) private boolean autoUpdate = true; @Expose @SerializedName("plugin") @ConfigSubtag private PluginConfiguration pluginConfiguration = new PluginConfiguration(); @Expose @SerializedName("config") @ConfigSubtag private Configuration configuration = new Configuration(); public SCM() { } public SCM(String id, PluginConfiguration pluginConfiguration, Configuration configuration) { this.id = id; this.pluginConfiguration = pluginConfiguration; this.configuration = configuration; } public String getId() { return id; } //used in erb as it cannot access id attribute as it treats 'id' as keyword public String getSCMId() { return getId(); } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isAutoUpdate() { return autoUpdate; } public void setAutoUpdate(boolean autoUpdate) { this.autoUpdate = autoUpdate; } public PluginConfiguration getPluginConfiguration() { return pluginConfiguration; } public void setPluginConfiguration(PluginConfiguration pluginConfiguration) { this.pluginConfiguration = pluginConfiguration; } public Configuration getConfiguration() { return configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } public void addConfigurations(List<ConfigurationProperty> configurations) { ConfigurationPropertyBuilder builder = new ConfigurationPropertyBuilder(); for (ConfigurationProperty property : configurations) { SCMConfigurations scmConfigurations = SCMMetadataStore.getInstance().getConfigurationMetadata(getPluginId()); if (isValidPluginConfiguration(property.getConfigKeyName(), scmConfigurations)) { configuration.add(builder.create(property.getConfigKeyName(), property.getConfigValue(), property.getEncryptedValue(), scmConfigurationFor(property.getConfigKeyName(), scmConfigurations).getOption(SCMConfiguration.SECURE))); } else { configuration.add(property); } } } private boolean isValidPluginConfiguration(String configKey, SCMConfigurations scmConfigurations) { return doesPluginExist() && scmConfigurationFor(configKey, scmConfigurations) != null; } private SCMConfiguration scmConfigurationFor(String configKey, SCMConfigurations scmConfigurations) { return scmConfigurations.get(configKey); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SCM that = (SCM) o; if (id != null ? !id.equals(that.id) : that.id != null) { return false; } if (name != null ? !name.equals(that.name) : that.name != null) { return false; } if (pluginConfiguration != null ? !pluginConfiguration.equals(that.pluginConfiguration) : that.pluginConfiguration != null) { return false; } if (configuration != null ? !configuration.equals(that.configuration) : that.configuration != null) { return false; } return true; } @Override public int hashCode() { int result = id != null ? id.hashCode() : 0; result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (pluginConfiguration != null ? pluginConfiguration.hashCode() : 0); result = 31 * result + (configuration != null ? configuration.hashCode() : 0); return result; } @Override public void validate(ValidationContext validationContext) { if (isBlank(name)) { errors().add(NAME, "Please provide name"); } else if (new NameTypeValidator().isNameInvalid(name)) { errors().add(NAME, NameTypeValidator.errorMessage("SCM", name)); } configuration.validateTree(); configuration.validateUniqueness(String.format("SCM '%s'", name)); } @Override public ConfigErrors errors() { return errors; } @Override public void addError(String fieldName, String message) { errors.add(fieldName, message); } public Map<String, Map<String, String>> getConfigAsMap() { Map<String, Map<String, String>> configMap = new HashMap<>(); for (ConfigurationProperty property : configuration) { Map<String, String> mapValue = new HashMap<>(); mapValue.put(VALUE_KEY, property.getValue()); if (!property.errors().isEmpty()) { mapValue.put(ERRORS_KEY, ListUtil.join(property.errors().getAll())); } configMap.put(property.getConfigKeyName(), mapValue); } return configMap; } public String getConfigForDisplay() { String pluginId = getPluginId(); SCMMetadataStore metadataStore = SCMMetadataStore.getInstance(); List<ConfigurationProperty> propertiesToBeUsedForDisplay = ConfigurationDisplayUtil.getConfigurationPropertiesToBeUsedForDisplay(metadataStore, pluginId, configuration); String prefix = metadataStore.hasPlugin(pluginId) ? "" : "WARNING! Plugin missing. "; return prefix + configuration.forDisplay(propertiesToBeUsedForDisplay); } private String getPluginId() { return pluginConfiguration.getId(); } public Boolean doesPluginExist(){ return SCMMetadataStore.getInstance().hasPlugin(getPluginId()); } @PostConstruct public void applyPluginMetadata() { String pluginId = getPluginId(); for (ConfigurationProperty configurationProperty : configuration) { SCMMetadataStore scmMetadataStore = SCMMetadataStore.getInstance(); if (scmMetadataStore.getConfigurationMetadata(pluginId) != null) { boolean isSecureProperty = scmMetadataStore.hasOption(pluginId, configurationProperty.getConfigurationKey().getName(), SCMConfiguration.SECURE); configurationProperty.handleSecureValueConfiguration(isSecureProperty); } } } public void setConfigAttributes(Object attributes) { Map attributesMap = (Map) attributes; if (attributesMap.containsKey(SCM_ID)) { id = ((String) attributesMap.get(SCM_ID)); } if (attributesMap.containsKey(NAME)) { name = ((String) attributesMap.get(NAME)); } this.setAutoUpdate("true".equals(attributesMap.get(AUTO_UPDATE))); if (attributesMap.containsKey(PLUGIN_CONFIGURATION)) { pluginConfiguration.setConfigAttributes(attributesMap.get(PLUGIN_CONFIGURATION)); } setPluginConfigurationAttributes(attributesMap); } protected void setPluginConfigurationAttributes(Map attributes) { SCMConfigurations scmConfigurations = SCMMetadataStore.getInstance().getConfigurationMetadata(pluginConfiguration.getId()); if (scmConfigurations == null) { throw new RuntimeException("metadata unavailable for plugin: " + pluginConfiguration.getId()); } for (SCMConfiguration scmConfiguration : scmConfigurations.list()) { String key = scmConfiguration.getKey(); if (attributes.containsKey(key)) { if (configuration.getProperty(key) == null) { configuration.addNewConfiguration(scmConfiguration.getKey(), scmConfiguration.getOption(Property.SECURE)); } configuration.getProperty(key).setConfigurationValue(new ConfigurationValue((String) attributes.get(key))); configuration.getProperty(key).handleSecureValueConfiguration(scmConfiguration.getOption(Property.SECURE)); } } } public String getFingerprint() { List<String> list = new ArrayList<>(); list.add(format("%s=%s", "plugin-id", getPluginId())); handleSCMProperties(list); String fingerprint = join(list, AbstractMaterialConfig.FINGERPRINT_DELIMITER); // CAREFUL! the hash algorithm has to be same as the one used in 47_create_new_materials.sql return CachedDigestUtils.sha256Hex(fingerprint); } private void handleSCMProperties(List<String> list) { SCMConfigurations metadata = SCMMetadataStore.getInstance().getConfigurationMetadata(getPluginId()); for (ConfigurationProperty configurationProperty : configuration) { handleProperty(list, metadata, configurationProperty); } } private void handleProperty(List<String> list, SCMConfigurations metadata, ConfigurationProperty configurationProperty) { SCMConfiguration scmConfiguration = null; if (metadata != null) { scmConfiguration = metadata.get(configurationProperty.getConfigurationKey().getName()); } if (scmConfiguration == null || scmConfiguration.getOption(SCMConfiguration.PART_OF_IDENTITY)) { list.add(configurationProperty.forFingerprint()); } } public boolean isNew() { return isEmpty(id); } public void clearEmptyConfigurations() { configuration.clearEmptyConfigurations(); } public List<ConfigErrors> getAllErrors() { return ErrorCollector.getAllErrors(this); } @PostConstruct public void ensureIdExists() { if (StringUtil.isBlank(getId())) { setId(UUID.randomUUID().toString()); } } public String getSCMType() { return "pluggable_material_" + getPluginConfiguration().getId().replaceAll("[^a-zA-Z0-9_]", "_"); } }