Package com.denimgroup.threadfix.service.defects.utils.tfs

Source Code of com.denimgroup.threadfix.service.defects.utils.tfs.TFSClientImpl

////////////////////////////////////////////////////////////////////////
//
//     Copyright (c) 2009-2014 Denim Group, Ltd.
//
//     The contents of this file are subject to the Mozilla Public 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.mozilla.org/MPL/
//
//     Software distributed under the License is distributed on an "AS IS"
//     basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//     License for the specific language governing rights and limitations
//     under the License.
//
//     The Original Code is ThreadFix.
//
//     The Initial Developer of the Original Code is Denim Group, Ltd.
//     Portions created by Denim Group, Ltd. are Copyright (C)
//     Denim Group, Ltd. All Rights Reserved.
//
//     Contributor(s): Denim Group, Ltd.
//
////////////////////////////////////////////////////////////////////////
package com.denimgroup.threadfix.service.defects.utils.tfs;

import com.denimgroup.threadfix.data.entities.DefaultConfiguration;
import com.denimgroup.threadfix.exception.DefectTrackerUnavailableException;
import com.denimgroup.threadfix.importer.util.ResourceUtils;
import com.denimgroup.threadfix.logging.SanitizedLogger;
import com.denimgroup.threadfix.service.ProxyService;
import com.denimgroup.threadfix.service.defects.DefectMetadata;
import com.denimgroup.threadfix.service.defects.TFSDefectTracker;
import com.microsoft.tfs.core.TFSTeamProjectCollection;
import com.microsoft.tfs.core.clients.workitem.WorkItem;
import com.microsoft.tfs.core.clients.workitem.WorkItemClient;
import com.microsoft.tfs.core.clients.workitem.fields.*;
import com.microsoft.tfs.core.clients.workitem.project.Project;
import com.microsoft.tfs.core.clients.workitem.project.ProjectCollection;
import com.microsoft.tfs.core.clients.workitem.query.WorkItemCollection;
import com.microsoft.tfs.core.config.ConnectionAdvisor;
import com.microsoft.tfs.core.config.DefaultConnectionAdvisor;
import com.microsoft.tfs.core.exceptions.TECoreException;
import com.microsoft.tfs.core.exceptions.TFSUnauthorizedException;
import com.microsoft.tfs.core.httpclient.Credentials;
import com.microsoft.tfs.core.httpclient.UsernamePasswordCredentials;
import com.microsoft.tfs.core.httpclient.auth.AuthScope;
import com.microsoft.tfs.core.ws.runtime.exceptions.UnauthorizedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

import static com.denimgroup.threadfix.CollectionUtils.list;

public class TFSClientImpl extends SpringBeanAutowiringSupport implements TFSClient {

    @Autowired(required = false)
    private ProxyService proxyService;

    protected static final SanitizedLogger LOG = new SanitizedLogger("TFSClientImpl");

    // We need to load the native libraries and this seems to be the best spot.
    // The idea is to use the same code for loading all the libraries but use
    // string values to specify which folder they are in and which names to look up.
    static {
        String osName = System.getProperty("os.name"), osArch = System.getProperty("os.arch");
        LOG.info("Attempting to load libraries for " + osName + ".");

        String folderName = null, prefix = null, suffix = null;
        String[] names = null;

        if (osName == null) {
            LOG.error("Received null from System.getProperty(\"os.name\"), " +
                    "something is wrong here.");
        } else if (osName.startsWith("Windows")) {
            folderName = "/tfs-native/win32/x86";
            if (osArch != null && osArch.contains("64")) {
                folderName += "_64";
            }
            prefix = "native_";
            suffix = ".dll";
            names = new String[] { "synchronization", "auth", "console",
                    "filesystem", "messagewindow", "misc", "registry" };

        } else if (osName.startsWith("Mac OS")) {
            folderName = "/tfs-native/macosx";
            prefix = "libnative_";
            suffix = ".jnilib";
            names = new String[] { "auth", "console", "filesystem", "keychain",
                    "misc", "synchronization" };
        } else if (osName.startsWith("Linux")) {
            String archExtension = osArch;
            if (osArch.equals("amd64")) {
                archExtension = "x86_64";
            } else if (osArch.equals("i386")) {
                archExtension = "x86";
            }

            folderName = "/tfs-native/linux/" + archExtension;
            prefix = "libnative_";
            suffix = ".so";
            names = new String[] { "auth", "console", "filesystem", "misc",
                    "synchronization" };

        } else if (osName.equals("hpux") || osName.equals("aix")
                || osName.equals("solaris")) {
            folderName = "/tfs-native/" + osName + "/";
            prefix = "libnative_";
            suffix = ".so";
            if (osArch != null && osArch.equals("PA_RISC")) {
                suffix = ".sl";
            } else if (osArch != null && osArch.equals("ppc")) {
                suffix = ".a";
            }
        } else {
            LOG.error("OS name not supported by TFS. " +
                    "The TFS integration will fail.");
        }

        if (folderName != null && names != null) {
            try {

                URL url = ResourceUtils.getResourceAsUrl(folderName);

                if (url != null) {
                    String base = url.toURI().getPath()
                            .replaceFirst("file:", "");
                    try {
                        for (String library : names) {
                            System.load(base + prefix + library + suffix);
                        }

                        LOG.info("Successfully loaded native libraries for "
                                + osName + ".");
                    } catch (UnsatisfiedLinkError e) {
                        LOG.error("Unable to locate one of the libraries.", e);
                    }
                }
            } catch (URISyntaxException e) {
                LOG.error("Unable to convert the path String to a URI.", e);
            }

        } else {
            LOG.error("Attempt to load TFS native libraries failed..");
        }
    }

    ConnectionStatus lastStatus = ConnectionStatus.INVALID;
    WorkItemClient client = null;

    @Override
    public void updateDefectIdMaps(String ids, Map<String, String> stringStatusMap, Map<String, Boolean> openStatusMap) {
        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to submit a defect.");
            return;
        }

        String wiqlQuery = "Select ID, State from WorkItems where (id in ("
                + ids + "))";

        // Run the query and get the results.
        WorkItemCollection workItems = client.query(wiqlQuery);

        for (int i = 0; i < workItems.size(); i++) {
            WorkItem workItem = workItems.getWorkItem(i);

            stringStatusMap.put(String.valueOf(workItem.getID()),
                    (String) workItem.getFields().getField("State")
                            .getOriginalValue());
            openStatusMap.put(String.valueOf(workItem.getID()),
                    workItem.isOpen());
        }

        client.close();
    }

    @Override
    public List<String> getPriorities() {
        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to submit a defect.");
            return null;
        }

        List<String> returnPriorities = list();

        FieldDefinitionCollection collection = client
                .getFieldDefinitions();

        Collections.addAll(returnPriorities, collection.get("Priority")
                .getAllowedValues().getValues());

        client.close();

        return returnPriorities;
    }

    @Override
    public List<String> getDefectIds(String projectName) {
        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to get defect IDs.");
            return null;
        }

        String wiqlQuery = "Select [System.Id] from WorkItems Where [System.TeamProject] = '" + projectName + "'";

        // Run the query and get the results.
        WorkItemCollection workItems = client.query(wiqlQuery);

        List<String> ids = list();

        for (int i = 0; i < workItems.size(); i++) {
            ids.add(String.valueOf(workItems.getWorkItem(i).getID()));
        }

        client.close();

        return ids;
    }

    @Override
    public List<String> getProjectNames() {
        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to submit a defect.");
            return null;
        }

        try {
            ProjectCollection collection = client.getProjects();

            List<String> strings = list();

            for (Project project : collection) {
                strings.add(project.getName());
            }

            return strings;
        } catch (UnauthorizedException | TFSUnauthorizedException e) {
            LOG.warn("Ran into TFSUnauthorizedException while trying to retrieve products.");
            return null;
        } finally {
            client.close();
        }
    }

    @Override
    public String getProjectId(String projectName) {
        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to submit a defect.");
            return null;
        }

        try {
            Project project = client.getProjects().get(projectName);

            return project == null ? null : String.valueOf(project.getID());

        } catch (UnauthorizedException | TFSUnauthorizedException e) {
            LOG.warn("Ran into TFSUnauthorizedException while trying to retrieve products.");
            return null;
        } finally {
            client.close();
        }
    }

    @Override
    public ConnectionStatus configure(String url, String username, String password) {

        try {
            Credentials credentials = new UsernamePasswordCredentials(username, password);

            URI uri = null;
            try {
                uri = new URI(url);
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }

            ConnectionAdvisor advisor = new DefaultConnectionAdvisor(Locale.getDefault(), TimeZone.getDefault());

            TFSTeamProjectCollection projects = new TFSTeamProjectCollection(uri, credentials, advisor);
            addProxy(projects.getHTTPClient());

            try {
                client = projects.getWorkItemClient();
                lastStatus = client == null ? ConnectionStatus.INVALID : ConnectionStatus.VALID;
            } catch (UnauthorizedException | TFSUnauthorizedException e) {
                LOG.warn("TFSUnauthorizedException encountered, unable to connect to TFS. " +
                        "Check credentials and endpoint.");
            }
        } catch (TECoreException e) {
            if (e.getMessage().contains("TF30059")) {
                throw new DefectTrackerUnavailableException(e,
                        "TFS is unavailable (TF30059 error). More details are available in the error logs.");
            } else {
                throw new DefectTrackerUnavailableException(e,
                        "An exception occurred while attempting to connect to TFS. " +
                                "Check the error logs for more details.");
            }
        }

        return lastStatus;
    }

    private void addProxy(com.microsoft.tfs.core.httpclient.HttpClient client) {

        if (proxyService != null && proxyService.shouldUseProxy(TFSDefectTracker.class)) {

            DefaultConfiguration config = proxyService.getDefaultConfigurationWithProxyCredentials();

            if (config.hasConfiguredHostAndPort()) {
                client.getHostConfiguration().setProxy(config.getProxyHost(), config.getProxyPort());

                if (config.hasConfiguredCredentials()) {
                    client.getState().setProxyCredentials(AuthScope.ANY,
                            new UsernamePasswordCredentials(config.getProxyUsername(), config.getProxyPassword()));
                }
            }
        }
    }

    @Override
    public String createDefect(String projectName, DefectMetadata metadata, String description) {

        if (lastStatus != ConnectionStatus.VALID || client == null) {
            LOG.error("Please configure the tracker properly before trying to submit a defect.");
            return null;
        }

        try {
            Project project = client.getProjects().get(projectName);

            if (project == null) {
                LOG.warn("Product was not found. Unable to create defect.");
                return null;
            }

            WorkItem item = client.newWorkItem(project
                    .getVisibleWorkItemTypes()[0]);

            if (item == null) {
                LOG.warn("Unable to create item in TFS.");
                return null;
            }

            item.setTitle(metadata.getDescription());
            item.getFields().getField("Description").setValue(description);
            item.getFields().getField("Priority").setValue(metadata.getPriority());

            String itemId = null;

            if (checkItemValues(item)) {
                item.save();
                itemId = String.valueOf(item.getID());
            } else {
                LOG.error("Failed to create issue because one or more fields were invalid. " +
                        "Check the above logs for more details.");
            }

            return itemId;

        } catch (UnauthorizedException | TFSUnauthorizedException e) {
            LOG.warn("Ran into TFSUnauthorizedException while trying to retrieve products.", e);
            return null;
        } finally {
            client.close();
        }
    }

    // This method checks all the item values and tries to patch them when necessary.
    private boolean checkItemValues(WorkItem item) {

        boolean valid = true;

        // we want to exit early if we find a field that we can't patch
        OUTER: for (Field field : item.getFields()) {
            if (field.getStatus() != FieldStatus.VALID) {

                if (field.getStatus() == FieldStatus.INVALID_EMPTY) {
                    LOG.info("Found INVALID_EMPTY error on field " + field.getName() +
                            ". Attempting to assign the string \"<None>\" to the field.");
                    field.setValue("<None>");
                }

                if (field.getStatus() == FieldStatus.INVALID_NOT_EMPTY) {
                    LOG.info("Found INVALID_NOT_EMPTY on field " + field.getName() + ". Setting field value to null. ");
                    field.setValue(null);
                }

                if (field.getStatus() != FieldStatus.VALID) {
                    valid = false;
                    LOG.error("Received error message for field " + field.getName() +
                            ": " + field.getStatus().getInvalidMessage(field));

                    LOG.info("Attempting to patch fields. " +
                            "This could result in different values for fields that you have set.");

                    // Read all field definitions to find the correct possible values for the field.
                    for (FieldDefinition definition : client.getFieldDefinitions()) {
                        if (definition.getName().equals(field.getName())) {
                            AllowedValuesCollection allowedValues = definition.getAllowedValues();

                            if (allowedValues.size() > 0) {

                                Object newValue = allowedValues.get(0);

                                LOG.info("List of allowed values for field " + field.getName() +
                                        " was not empty. Setting field value to the first available (" +
                                        newValue + ").");
                                field.setValue(newValue);

                                valid = field.getStatus() == FieldStatus.VALID;
                                if (field.getStatus() != FieldStatus.VALID) {
                                    LOG.error("Setting " + field.getName() + " to a known allowed value (" +
                                            newValue + ") failed. Giving up.");
                                    break OUTER;
                                } else {
                                    LOG.info("Setting " + field.getName() + " to " + newValue + " worked. Moving on.");
                                }
                            } else {
                                LOG.error("Set of possible values was empty. Giving up.");
                                valid = false;
                                break OUTER;
                            }
                        }
                    }
                }
            }
        }

        return valid;
    }

    @Override
    public ConnectionStatus checkUrl(String url) {
        Credentials credentials = new UsernamePasswordCredentials("", "");

        URI uri;
        try {
            uri = new URI(url);
        } catch (URISyntaxException e) {
            LOG.warn("Invalid syntax for the URL.",e);
            return ConnectionStatus.INVALID;
        }

        TFSTeamProjectCollection projects = new TFSTeamProjectCollection(uri,
                credentials);

        addProxy(projects.getHTTPClient());

        try {
            projects.getWorkItemClient().getProjects();
            //projects.getWorkItemClient();
            LOG.info("No UnauthorizedException was thrown when attempting to connect with blank credentials.");
            return ConnectionStatus.VALID;
        } catch (UnauthorizedException | TFSUnauthorizedException e) {
            LOG.info("Got an UnauthorizedException, which means that the TFS url was good.");
            return ConnectionStatus.VALID;
        } catch (TECoreException e) {
            if (e.getMessage().contains("unable to find valid certification path to requested target")) {
                LOG.warn("An invalid or self-signed certificate was found.");
                return ConnectionStatus.INVALID_CERTIFICATE;
            } else {
                return ConnectionStatus.INVALID;
            }
        }
    }

}
TOP

Related Classes of com.denimgroup.threadfix.service.defects.utils.tfs.TFSClientImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.