org.jboss.set.aphrodite.issue.trackers.bugzilla.IssueWrapper.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.set.aphrodite.issue.trackers.bugzilla.IssueWrapper.java

Source

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.set.aphrodite.issue.trackers.bugzilla;

import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaClient.ID_PARAM_PATTERN;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ASSIGNEE;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.BLOCKS;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.COMPONENT;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.CREATION_TIME;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.DEPENDS_ON;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.DESCRIPTION;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ESTIMATED_TIME;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.EXTERNAL_URL;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.FLAGS;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.FLAG_NAME;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.FLAG_STATUS;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.HOURS_WORKED;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ID;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ID_QUERY;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ISSUE_IDS;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.ISSUE_TYPE;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.LAST_UPDATED;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.METHOD_SET_COLLECTION;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.PRODUCT;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.REPORTER;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.STATUS;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.PRIORITY;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.SUMMARY;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.TARGET_MILESTONE;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.TARGET_RELEASE;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.VERSION;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.getAphroditeFlag;
import static org.jboss.set.aphrodite.issue.trackers.bugzilla.BugzillaFields.getBugzillaFlag;

import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.set.aphrodite.common.Utils;
import org.jboss.set.aphrodite.config.TrackerType;
import org.jboss.set.aphrodite.domain.Flag;
import org.jboss.set.aphrodite.domain.FlagStatus;
import org.jboss.set.aphrodite.domain.Issue;
import org.jboss.set.aphrodite.domain.IssueEstimation;
import org.jboss.set.aphrodite.domain.IssueStatus;
import org.jboss.set.aphrodite.domain.IssueType;
import org.jboss.set.aphrodite.domain.Release;
import org.jboss.set.aphrodite.domain.Stage;
import org.jboss.set.aphrodite.domain.User;
import org.jboss.set.aphrodite.spi.AphroditeException;

/**
 * @author Ryan Emerson
 */
class IssueWrapper {

    private static final Log LOG = LogFactory.getLog(BugzillaIssueTracker.class);

    Issue bugzillaBugToIssue(Map<String, Object> bug, URL baseURL) {
        Integer id = (Integer) bug.get(ID);
        URL url = Utils.createURL(baseURL + ID_QUERY + id);
        Issue issue = new Issue(url, TrackerType.BUGZILLA);
        issue.setTrackerId(id.toString());
        issue.setAssignee(User.createWithEmail((String) bug.get(ASSIGNEE)));
        issue.setReporter(User.createWithEmail((String) bug.get(REPORTER)));
        issue.setCreationTime((Date) bug.get(CREATION_TIME));
        issue.setLastUpdated((Date) bug.get(LAST_UPDATED));
        issue.setSummary((String) bug.get(SUMMARY));
        issue.setDescription((String) bug.get(DESCRIPTION));
        issue.setStatus(IssueStatus.valueOf((String) bug.get(STATUS)));
        issue.setPriority(IssuePriorityTranslatorUtil.translateFromBugzilla((String) bug.get(PRIORITY)));

        Object[] components = (Object[]) bug.get(COMPONENT);
        List<String> tmp = new ArrayList<>();
        for (Object component : components) {
            tmp.add((String) component);
        }
        issue.setComponents(tmp);
        issue.setProduct((String) bug.get(PRODUCT));
        issue.setStatus(IssueStatus.valueOf(((String) bug.get(STATUS)).toUpperCase()));

        String type = (String) bug.get(ISSUE_TYPE);
        issue.setType(IssueType.getMatchingIssueType(type));

        setAffectedVersions(issue, (Object[]) (bug.get(VERSION)));
        setReleases(issue, bug);

        List<URL> dependsOn = getListOfURlsFromIds(bug, baseURL, DEPENDS_ON);
        dependsOn.addAll(getListOfExternalURLsFromIds(bug, EXTERNAL_URL));
        issue.setDependsOn(dependsOn);
        issue.setBlocks(getListOfURlsFromIds(bug, baseURL, BLOCKS));

        checkIsNullEstimation(bug, issue);
        extractStageAndStreams(bug, issue);
        return issue;
    }

    private static void setReleases(Issue issue, Map<String, Object> bug) {
        List<Release> releases = new ArrayList<>();
        if (bug.containsKey(TARGET_RELEASE) && bug.get(TARGET_RELEASE) != null) {
            Object[] targetReleases = (Object[]) bug.get(TARGET_RELEASE);
            for (Object targetRelease : targetReleases) {
                if (bug.containsKey(TARGET_MILESTONE) && bug.get(TARGET_MILESTONE) != null) {
                    releases.add(new Release((String) targetRelease, (String) bug.get(TARGET_MILESTONE)));
                } else {
                    releases.add(new Release((String) targetRelease));
                }
            }
        }
        issue.setReleases(releases);
    }

    private void setAffectedVersions(Issue issue, Object[] affectedVersions) {
        if (affectedVersions != null && affectedVersions.length > 0) {
            List<String> affectedVersionsList = new ArrayList<String>(affectedVersions.length);
            for (Object affectedVersion : affectedVersions)
                affectedVersionsList.add(affectedVersion.toString());
            issue.setAffectedVersions(affectedVersionsList);
        }
    }

    private void checkIsNullEstimation(Map<String, Object> bug, Issue issue) {
        Double estimatedTime = (Double) bug.get(ESTIMATED_TIME);
        Double hoursWorked = (Double) bug.get(HOURS_WORKED);
        if (estimatedTime != null && hoursWorked != null) {
            issue.setEstimation(new IssueEstimation(estimatedTime, hoursWorked));
        } else if (estimatedTime != null) {
            issue.setEstimation(new IssueEstimation(estimatedTime));
        }

    }

    Map<String, Object> issueToBugzillaBug(Issue issue, Map<String, Object> loginDetails)
            throws AphroditeException {
        checkUnsupportedUpdateFields(issue);
        checkUnsupportedIssueStatus(issue);

        Map<String, Object> params = new HashMap<>(loginDetails);
        issue.getTrackerId().ifPresent(trackerId -> params.put(ISSUE_IDS, trackerId));
        issue.getSummary().ifPresent(summary -> params.put(SUMMARY, summary));
        issue.getProduct().ifPresent(product -> params.put(PRODUCT, product));
        params.put(COMPONENT, issue.getComponents().toArray(new String[issue.getComponents().size()]));
        issue.getAssignee()
                .ifPresent(assignee -> params.put(ASSIGNEE, assignee.getEmail().orElseThrow(this::nullUserEmail)));
        issue.getReporter()
                .ifPresent(reporter -> params.put(REPORTER, reporter.getEmail().orElseThrow(this::nullUserEmail)));

        issue.getEstimation().ifPresent(tracking -> {
            params.put(HOURS_WORKED, tracking.getHoursWorked());
            params.put(ESTIMATED_TIME, tracking.getInitialEstimate());
        });

        params.put(STATUS, issue.getStatus().toString());
        params.put(FLAGS, getStageAndStreamsMap(issue.getStreamStatus(), issue.getStage().getStateMap()));

        if (issue.getType() != IssueType.UNDEFINED)
            params.put(ISSUE_TYPE, issue.getType().get());

        addReleaseToUpdate(issue, params);
        addURLCollectionToParameters(issue.getDependsOn(), DEPENDS_ON, params);
        addURLCollectionToParameters(issue.getBlocks(), BLOCKS, params);
        return params;
    }

    private IllegalArgumentException nullUserEmail() {
        throw new IllegalArgumentException(
                "Bugzilla requires a non-null User.email field to update assignee/reporters");
    }

    private void checkUnsupportedUpdateFields(Issue issue) {
        if (issue.getReporter().isPresent() && LOG.isWarnEnabled())
            LOG.warn("Bugzilla does not support updating the reporter field, field ignored.");

        if (issue.getStatus() == IssueStatus.UNDEFINED && LOG.isWarnEnabled())
            LOG.warn("IssueStatus.UNDEFINED is ignored when updating a Bugzilla operation.");
    }

    private void checkUnsupportedIssueStatus(Issue issue) throws AphroditeException {
        if (issue.getStatus() == IssueStatus.CREATED) {
            throw new AphroditeException("Bugzilla issues do not support the IssueStatus CREATED");
        }
    }

    private void addReleaseToUpdate(Issue issue, Map<String, Object> params) {
        List<Release> releases = issue.getReleases();
        if (!releases.isEmpty()) {
            Release release = releases.get(0);
            release.getVersion().ifPresent(version -> params.put(VERSION, version));
            release.getMilestone().ifPresent(milestone -> params.put(TARGET_MILESTONE, milestone));

            if (releases.size() > 1) {
                Utils.logWarnMessage(LOG, "Bugzilla only supports one Release object. The first Release object in "
                        + "Issue::getReleases is used and subsequent Releases are ignored.");
            }
        }
    }

    private List<Map<String, Object>> getStageAndStreamsMap(Map<String, FlagStatus> streams,
            Map<Flag, FlagStatus> stateMap) {
        List<Map<String, Object>> flags = new ArrayList<>();
        for (Map.Entry<String, FlagStatus> stream : streams.entrySet()) {
            Map<String, Object> flagMap = new HashMap<>();
            flagMap.put(FLAG_NAME, stream.getKey());
            flagMap.put(FLAG_STATUS, stream.getValue().getSymbol());
            flags.add(flagMap);
        }

        for (Map.Entry<Flag, FlagStatus> entry : stateMap.entrySet()) {
            Map<String, Object> flagMap = new HashMap<>();
            Optional<String> bzFlag = getBugzillaFlag(entry.getKey());
            bzFlag.ifPresent(flagName -> {
                flagMap.put(FLAG_NAME, flagName);
                flagMap.put(FLAG_STATUS, entry.getValue().getSymbol());
                flags.add(flagMap);
            });
        }
        return flags;
    }

    private void addURLCollectionToParameters(List<URL> urls, String flag, Map<String, Object> params) {
        Map<String, Object> map = new HashMap<>();
        List<String> ids = Utils.getParametersFromUrls(ID_PARAM_PATTERN, urls);
        map.put(METHOD_SET_COLLECTION, ids);
        params.put(flag, map);
    }

    private List<URL> getListOfURlsFromIds(Map<String, Object> bug, URL baseURL, String field) {
        List<URL> list = new ArrayList<>();
        Object[] ids = (Object[]) bug.get(field);
        for (Object id : ids)
            list.add(Utils.createURL(baseURL + BugzillaFields.ID_QUERY + id));
        return list;
    }

    private List<URL> getListOfExternalURLsFromIds(Map<String, Object> bug, String externalBugField) {
        if (!bug.containsKey(externalBugField)) {
            return Collections.emptyList();
        }
        List<URL> externalURL = new ArrayList<>();
        Object[] eBugs = (Object[]) bug.get(externalBugField);

        for (Object tmp : eBugs) {
            try {
                Map<String, Object> bz = (Map<String, Object>) tmp;
                Map<String, Object> bzType = (Map<String, Object>) bz.get("type");
                if (!("JIRA".equals(bzType.get("type")))) {
                    continue;
                }
                String bugId = (String) bz.get("ext_bz_bug_id");
                String urlBase = (String) bzType.get("full_url");
                String url = urlBase.replace("%id%", bugId);
                externalURL.add(URI.create(url).toURL());
            } catch (Exception e) {
                Utils.logException(LOG, "cannot convert the url", e);
            }
        }
        return externalURL;
    }

    private void extractStageAndStreams(Map<String, Object> bug, Issue issue) {
        Stage issueStage = new Stage();
        Map<String, FlagStatus> streams = new HashMap<>();
        if (bug.get(FLAGS) != null) {
            for (Object object : (Object[]) bug.get(FLAGS)) {
                @SuppressWarnings("unchecked")
                Map<String, Object> flagMap = (Map<String, Object>) object;
                String name = (String) flagMap.get(FLAG_NAME);

                if (name.contains("_ack")) { // If Flag
                    Optional<Flag> flag = getAphroditeFlag(name);
                    if (!flag.isPresent())
                        continue;

                    FlagStatus status = FlagStatus.getMatchingFlag((String) flagMap.get(FLAG_STATUS));
                    issueStage.setStatus(flag.get(), status);
                } else if (name.equals("needinfo") || name.equals("requires_doc_text")
                        || name.equals("qe_test_coverage")) {
                    continue;
                } else { // Else Stream
                    FlagStatus status = FlagStatus.getMatchingFlag((String) flagMap.get(FLAG_STATUS));
                    streams.put(name, status);
                }
            }
        }
        issue.setStage(ensureStageMapIsComplete(issueStage));
        issue.setStreamStatus(streams);
    }

    // Ensure all missing flag, if any are set to NO_SET
    private static Stage ensureStageMapIsComplete(Stage issue) {
        for (Flag flag : Flag.values())
            issue.getStateMap().putIfAbsent(flag, FlagStatus.NO_SET);

        return issue;
    }
}