/*
* Copyright 2015 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.config.materials.dependency;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.thoughtworks.go.config.*;
import com.thoughtworks.go.config.materials.AbstractMaterialConfig;
import com.thoughtworks.go.config.materials.Filter;
import com.thoughtworks.go.domain.ConfigErrors;
import com.thoughtworks.go.domain.DependencyFilter;
import static com.thoughtworks.go.util.ExceptionUtils.bombIfNull;
@ConfigTag(value = "pipeline", label = "Pipeline")
public class DependencyMaterialConfig extends AbstractMaterialConfig implements ParamsAttributeAware {
public static final String PIPELINE_NAME = "pipelineName";
public static final String STAGE_NAME = "stageName";
public static final String PIPELINE_STAGE_NAME = "pipelineStageName";
public static final String TYPE = "DependencyMaterial";
private static final Pattern PIPELINE_STAGE_COMBINATION_PATTERN = Pattern.compile("^(.+) (\\[.+\\])$");
public static final String ORIGIN = "origin";
@ConfigAttribute(value = "pipelineName")
private com.thoughtworks.go.config.CaseInsensitiveString pipelineName = new CaseInsensitiveString("Unknown");
@ConfigAttribute(value = "stageName")
private CaseInsensitiveString stageName = new CaseInsensitiveString("Unknown");
private String pipelineStageName;
public DependencyMaterialConfig() {
super(TYPE);
}
public DependencyMaterialConfig(final CaseInsensitiveString pipelineName, final CaseInsensitiveString stageName) {
this(null, pipelineName, stageName);
}
public DependencyMaterialConfig(final CaseInsensitiveString pipelineName, final CaseInsensitiveString stageName, final String serverAlias) {
this(null, pipelineName, stageName);
}
public DependencyMaterialConfig(final CaseInsensitiveString name, final CaseInsensitiveString pipelineName, final CaseInsensitiveString stageName) {
super(TYPE, name, new ConfigErrors());
bombIfNull(pipelineName, "null pipelineName");
bombIfNull(stageName, "null stageName");
this.pipelineName = pipelineName;
this.stageName = stageName;
}
@Override
public CaseInsensitiveString getName() {
return super.getName() == null ? pipelineName : super.getName();
}
public String getUserName() {
return "cruise";
}
@Override
public String getLongDescription() {
return getDescription();
}
@Override
public Filter filter() {
return new DependencyFilter();
}
@Override
public boolean isInvertFilter() {
return false;
}
@Override
public boolean matches(String name, String regex) {
return false;
}
@Override
public String getDescription() {
return CaseInsensitiveString.str(pipelineName);
}
@Override
public String getTypeForDisplay() {
return "Pipeline";
}
@Override
public boolean isAutoUpdate() {
return true;
}
@Override
public void setAutoUpdate(boolean autoUpdate) {
}
@Override
protected void appendCriteria(Map<String, Object> parameters) {
parameters.put("pipelineName", CaseInsensitiveString.str(pipelineName));
parameters.put("stageName", CaseInsensitiveString.str(stageName));
}
@Override
protected void appendAttributes(Map<String, Object> parameters) {
appendCriteria(parameters);
}
public CaseInsensitiveString getPipelineName() {
return pipelineName;
}
public void setPipelineName(CaseInsensitiveString pipelineName) {
this.pipelineName = pipelineName;
}
public CaseInsensitiveString getStageName() {
return stageName;
}
public void setStageName(CaseInsensitiveString stageName) {
this.stageName = stageName;
}
@Override
public String getFolder() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DependencyMaterialConfig that = (DependencyMaterialConfig) o;
if (type != null ? !type.equals(that.type) : that.type != null) {
return false;
}
if (pipelineName != null ? !pipelineName.equals(that.pipelineName) : that.pipelineName != null) {
return false;
}
if (stageName != null ? !stageName.equals(that.stageName) : that.stageName != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = (type != null ? type.hashCode() : 0);
result = 31 * result + (pipelineName != null ? pipelineName.hashCode() : 0);
result = 31 * result + (stageName != null ? stageName.hashCode() : 0);
return result;
}
@Override public String toString() {
return "DependencyMaterialConfig{" +
"pipelineName='" + pipelineName + '\'' +
", stageName='" + stageName + '\'' +
'}';
}
@Override
public String getDisplayName() {
return CaseInsensitiveString.str(getName());
}
@Override
protected void validateConcreteMaterial(ValidationContext validationContext) {
CaseInsensitiveString upstreamPipelineName = this.getPipelineName();
CaseInsensitiveString upstreamStageName = this.getStageName();
PipelineConfig upstreamPipeline = validationContext.getPipelineConfigByName(upstreamPipelineName);
PipelineConfig pipeline = validationContext.getPipeline();
if (upstreamPipeline==null) {
errors.add(DependencyMaterialConfig.PIPELINE_STAGE_NAME, String.format("Pipeline with name '%s' does not exist, it is defined as a dependency for pipeline '%s' (%s)", upstreamPipelineName, pipeline.name(), pipeline.getOriginDisplayName()));
}
else if (upstreamPipeline.findBy(upstreamStageName) == null) {
errors.add(DependencyMaterialConfig.PIPELINE_STAGE_NAME, String.format("Stage with name '%s' does not exist on pipeline '%s', it is being referred to from pipeline '%s' (%s)", upstreamStageName, upstreamPipelineName, pipeline.name(), pipeline.getOriginDisplayName()));
}
}
@Override
public String getUriForDisplay() {
return String.format("%s / %s", pipelineName, stageName);
}
public void validateUniqueness(Set<CaseInsensitiveString> dependencies) {
CaseInsensitiveString upstreamPipelineName = pipelineName;
if (dependencies.contains(upstreamPipelineName)) {
String message = (String.format("A pipeline can depend on each upstream pipeline only once. Remove one of the occurrences of '%s' from the current pipeline dependencies.", upstreamPipelineName));
errors.add(PIPELINE_STAGE_NAME, message);
}
dependencies.add(pipelineName);
}
@Override
public void setConfigAttributes(Object attributes) {
resetCachedIdentityAttributes();
if (attributes == null) {
return;
}
Map attributesMap = (Map) attributes;
if (attributesMap.containsKey(MATERIAL_NAME)) {
name = new CaseInsensitiveString((String) attributesMap.get(MATERIAL_NAME));
if (CaseInsensitiveString.isBlank(name)) {
name = null;
}
}
if (attributesMap.containsKey(PIPELINE_STAGE_NAME)) {
pipelineStageName = (String) attributesMap.get(PIPELINE_STAGE_NAME);
Matcher matcher = PIPELINE_STAGE_COMBINATION_PATTERN.matcher(pipelineStageName);
if(matcher.matches()){
pipelineName = new CaseInsensitiveString(matcher.group(1));
String stageNameWithBrackets = matcher.group(2);
stageName = new CaseInsensitiveString(stageNameWithBrackets.replace("[","").replace("]",""));
}
else {
errors.add(PIPELINE_STAGE_NAME, String.format("'%s' should conform to the pattern 'pipeline [stage]'",pipelineStageName));
}
}
}
public String getPipelineStageName() {
if (pipelineStageName != null) {
return pipelineStageName;
}
if (CaseInsensitiveString.isBlank(pipelineName) || CaseInsensitiveString.isBlank(stageName)) {
return null;
}
return String.format("%s [%s]", pipelineName, stageName);
}
@Override
public Boolean isUsedInFetchArtifact(PipelineConfig pipelineConfig){
List<FetchTask> fetchTasks = pipelineConfig.getFetchTasks();
for (FetchTask fetchTask : fetchTasks) {
if(pipelineName.equals(fetchTask.getDirectParentInAncestorPath()))
return true;
}
return false;
}
@Override
protected void appendPipelineUniqueCriteria(Map<String, Object> basicCriteria) {
// Dependency materials are already unique within a pipeline
}
}