All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.serenitybdd.plugins.jirarequirements.JIRARequirementsProvider Maven / Gradle / Ivy

package net.serenitybdd.plugins.jirarequirements;

import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.serenitybdd.plugins.jira.client.JerseyJiraClient;
import net.serenitybdd.plugins.jira.client.LoadingStrategy;
import net.serenitybdd.plugins.jira.domain.IssueSummary;
import net.serenitybdd.plugins.jira.model.JQLException;
import net.serenitybdd.plugins.jira.service.JIRAConfiguration;
import net.serenitybdd.plugins.jira.service.SystemPropertiesJIRAConfiguration;
import net.serenitybdd.core.di.SerenityInfrastructure;
import net.thucydides.model.domain.TestOutcome;
import net.thucydides.model.domain.TestTag;
import net.thucydides.model.requirements.RequirementsList;
import net.thucydides.model.requirements.RequirementsTagProvider;
import net.thucydides.model.requirements.model.Requirement;
import net.thucydides.model.util.EnvironmentVariables;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;


/**
 * Integrate Thucydides reports with requirements, epics and stories in a JIRA server.
 */
public class JIRARequirementsProvider implements RequirementsTagProvider {

    private List requirements = null;
    private final JerseyJiraClient jiraClient;
    private final String projectKey;
    private final EnvironmentVariables environmentVariables;

    private final String EPIC_LINK = "Epic Link";

    private final Logger logger = LoggerFactory.getLogger(JIRARequirementsProvider.class);

    public JIRARequirementsProvider() {
        this(new SystemPropertiesJIRAConfiguration(SerenityInfrastructure.getEnvironmentVariables()),
                SerenityInfrastructure.getEnvironmentVariables() );
    }

    public JIRARequirementsProvider(JIRAConfiguration jiraConfiguration) {
        this(jiraConfiguration, SerenityInfrastructure.getEnvironmentVariables());
    }

    public JIRARequirementsProvider(JIRAConfiguration jiraConfiguration, EnvironmentVariables environmentVariables) {
        logConnectionDetailsFor(jiraConfiguration);
        projectKey = jiraConfiguration.getProject();
        this.environmentVariables = environmentVariables;
        jiraClient = new ConfigurableJiraClient(jiraConfiguration.getJiraUrl(),
                jiraConfiguration.getJiraUser(),
                jiraConfiguration.getJiraPassword(),
                projectKey).usingCustomFields(customFieldsDefinedIn(environmentVariables));
    }

    private List definedCustomFields() {
        List customFields = Lists.newArrayList();
        int customFieldIndex = 1;
        while (addCustomFieldIfDefined(environmentVariables, customFields, customFieldNumber(customFieldIndex++))) ;
        return customFields;
    }

    private List customFieldsDefinedIn(EnvironmentVariables environmentVariables) {
        List customFields = Lists.newArrayList();
        addCustomFieldIfDefined(environmentVariables, customFields,
                JIRARequirementsConfiguration.JIRA_CUSTOM_NARRATIVE_FIELD.getName());
        customFields.addAll(definedCustomFields());
        return customFields;
    }

    private String customFieldNumber(int customFieldIndex) {
        return JIRARequirementsConfiguration.JIRA_CUSTOM_FIELD.getName() + "." + customFieldIndex;
    }

    private boolean addCustomFieldIfDefined(EnvironmentVariables environmentVariables,
                                            List customFields,
                                            String customField) {
        String customFieldName = environmentVariables.getProperty(customField);
        if (StringUtils.isNotEmpty(customFieldName)) {
            customFields.add(customFieldName);
            return true;
        }
        return false;
    }

    private void logConnectionDetailsFor(JIRAConfiguration jiraConfiguration) {
        logger.debug("JIRA URL: {}", jiraConfiguration.getJiraUrl());
        logger.debug("JIRA project: {}", jiraConfiguration.getProject());
        logger.debug("JIRA user: {}", jiraConfiguration.getJiraUser());
    }

    private String getProjectKey() {
        return projectKey;
    }

    @Override
    public List getRequirements() {

        requirements = persisted(requirements);
        if ((requirements == null) && providerActivated()) {

            List rootRequirementIssues;
            logger.debug("Loading root requirements: " + rootRequirementsJQL());
            try {
                rootRequirementIssues = jiraClient.findByJQL(rootRequirementsJQL(), LoadingStrategy.LOAD_IN_BATCHES);
            } catch (JQLException e) {
                logger.debug("No root requirements found (JQL = " + rootRequirementsJQL(), e);
                rootRequirementIssues = Lists.newArrayList();
            }
            logger.debug("Loading root requirements done: " + rootRequirementIssues.size());

            RequirementsLoader requirementsLoader = new ConcurrentRequirementsLoader(environmentVariables, this);
            requirements = requirementsLoader.loadFrom(rootRequirementIssues);
            requirements = addParentsTo(requirements);
            persist(requirements);

        }
        return requirements;
    }

    private List persisted(List requirements) {
        return requirements;
    }

    private void persist(List requirements) {
        // TODO: Implement me
    }

    private boolean providerActivated() {
        return environmentVariables.getPropertyAsBoolean("serenity.providers.jira-requirements-provider", true);
    }

    private List addParentsTo(List requirements) {
        return addParentsTo(requirements, null);
    }

    private final List NO_REQUIREMENTS = ImmutableList.of();

    private List addParentsTo(List requirements, String parent) {
        List augmentedRequirements = Lists.newArrayList();
        for(Requirement requirement : requirements) {
            List children = requirement.hasChildren()
                    ? addParentsTo(requirement.getChildren(),requirement.getName()) : NO_REQUIREMENTS;
            augmentedRequirements.add(requirement.withParent(parent).withChildren(children));
        }
        return augmentedRequirements;
    }

    private Requirement requirementFrom(IssueSummary issue) {

        Requirement baseRequirement = Requirement.named(issue.getSummary())
                .withOptionalCardNumber(issue.getKey())
                .withType(issue.getType())
                .withNarrative(narativeTextFrom(issue))
                .withReleaseVersions(issue.getFixVersions());

        for (String fieldName : definedCustomFields()) {
            if (issue.customField(fieldName).isPresent()) {
                String value = issue.customField(fieldName).get().asString();
                String renderedValue = issue.getRendered().customField(fieldName).get();
                baseRequirement = baseRequirement.withCustomField(fieldName).setTo(value, renderedValue);
            }
        }
        return baseRequirement;
    }

    private String narativeTextFrom(IssueSummary issue) {
        Optional customFieldName = Optional.ofNullable(environmentVariables.getProperty(JIRARequirementsConfiguration.JIRA_CUSTOM_NARRATIVE_FIELD.getName()));
        if (customFieldName.isPresent()) {
            return customFieldNameFor(issue, customFieldName.get()).orElse(ObjectUtils.firstNonNull(issue.getRendered().getDescription(), ""));
        } else {
            return issue.getRendered().getDescription();
        }

    }

    private Optional customFieldNameFor(IssueSummary issue, String customFieldName) {
        if (issue.customField(customFieldName).isPresent()) {
            return Optional.of(issue.customField(customFieldName).get().asString());
        } else {
            return Optional.empty();
        }
    }


    protected List findChildrenFor(Requirement parent, final int level) {
        List children;

        long t0 = System.currentTimeMillis();
        try {
            logger.debug("Loading child requirements for: " + parent.getName());
            children = jiraClient.findByJQL(childIssuesJQL(parent, level), LoadingStrategy.LOAD_IN_SINGLE_QUERY);

            logger.debug("Loading child requirements for " + parent.getName() + " done: " + children.size());
        } catch (JQLException e) {
            logger.warn("No children found for requirement " + parent, e);
            return NO_REQUIREMENTS;
        }

        final List childRequirements = Collections.synchronizedList(new ArrayList());
        for(IssueSummary childIssue : children) {
            Requirement childRequirement = requirementFrom(childIssue);
            if (moreRequirements(level)) {
                List grandChildren = findChildrenFor(childRequirement, level + 1);
                childRequirement = childRequirement.withChildren(grandChildren);
            }
            childRequirements.add(childRequirement);
        }
        logger.debug("{} child requirements loaded in: {} ms", childRequirements.size(), System.currentTimeMillis() - t0);
        logger.debug("Child requirements: {}", childRequirements);
        return childRequirements;
    }

    private String childIssuesJQL(Requirement parent, int level) {
        String linkType = getRequirementsLinks().get(level);
        if (linkType.equals(EPIC_LINK)) {
            return "'" + getRequirementsLinks().get(level) + "' = " + parent.getCardNumber();
        } else {
            return "issue in linkedIssues(" + parent.getCardNumber() + ",\"" + linkType + "\")";
        }
    }

    private boolean moreRequirements(int level) {
        return level < getRequirementsLinks().size() - 1;
    }



    private String rootRequirementsJQL() {
        return "issuetype = " + getRootIssueType() + " and project=" + getProjectKey();
    }

    private String getRootIssueType() {
        return environmentVariables.getProperty(JIRARequirementsConfiguration.JIRA_ROOT_ISSUE_TYPE.getName(), "epic");
    }

    @Override
    public Optional getParentRequirementOf(TestOutcome testOutcome) {
        logger.debug("Find parent requirement in JIRA for " + testOutcome.getTitle());
        List issueKeys = testOutcome.getIssueKeys();
        if (!issueKeys.isEmpty() && providerActivated()) {
            try {
                java.util.Optional parentIssue = jiraClient.findByKey(issueKeys.get(0));
                if (parentIssue.isPresent()) {
                    logger.debug("Parent found: " + parentIssue.get());
                    return Optional.of(requirementFrom(parentIssue.get()));
                } else {
                    return Optional.empty();
                }
            } catch (JQLException e) {
                if (noSuchIssue(e)) {
                    return Optional.empty();
                } else {
                    throw new IllegalArgumentException(e);
                }
            }
        } else {
            return Optional.empty();
        }
    }

    @Override
    public Optional getParentRequirementOf(Requirement requirement) {
        for (Requirement candidateParent : RequirementsList.of(getRequirements()).asFlattenedList()) {
            if (candidateParent.getChildren().contains(requirement)) {
                return Optional.of(candidateParent);
            }
        }
        return Optional.empty();
    }

    private boolean noSuchIssue(JQLException e) {
        return e.getMessage().contains("error 400");
    }

    @Override
    public Optional getRequirementFor(TestTag testTag) {
        for (Requirement requirement : getFlattenedRequirements()) {
            if (requirement.getType().equals(testTag.getType()) && requirement.getName().equals(testTag.getName())) {
                return Optional.of(requirement);
            }
        }
        return Optional.empty();
    }

    @Override
    public Set getTagsFor(TestOutcome testOutcome) {
        List issues = testOutcome.getIssueKeys();
        Set tags = Sets.newHashSet();
        for (String issue : issues) {
            tags.addAll(tagsFromIssue(issue));
        }
        return ImmutableSet.copyOf(tags);
    }

    private Collection tagsFromIssue(String issueKey) {
        if (providerActivated()) {
            IssueTagReader tagReader = new IssueTagReader(jiraClient, getFlattenedRequirements(), projectKey);
            return tagReader.addIssueTags(issueKey)
                    .addRequirementTags(issueKey)
                    .addVersionTags(issueKey).getTags();
        } else {
            return ImmutableList.of();
        }
    }

    private List getFlattenedRequirements() {
        return getFlattenedRequirements(getRequirements());
    }

    private List getFlattenedRequirements(List someRequirements) {
        List flattenedRequirements = Lists.newArrayList();
        for (Requirement requirement : someRequirements) {
            flattenedRequirements.add(requirement);
            flattenedRequirements.addAll(getFlattenedRequirements(requirement.getChildren()));
        }
        return flattenedRequirements;
    }

    public List getRequirementsLinks() {
        String requirementLinks = environmentVariables.getProperty(JIRARequirementsConfiguration.JIRA_REQUIREMENT_LINKS.getName(),
                                                                   "Epic Link");
        return Splitter.on(",").trimResults().omitEmptyStrings().splitToList(requirementLinks);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy