/** * * Copyright (C) norad.fr * * 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 fr.norad.visuwall.plugin.teamcity; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.apache.commons.lang.StringUtils.isBlank; 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.Set; import java.util.TreeSet; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import fr.norad.visuwall.api.domain.BuildState; import fr.norad.visuwall.api.domain.BuildTime; import fr.norad.visuwall.api.domain.Commiter; import fr.norad.visuwall.api.domain.ProjectKey; import fr.norad.visuwall.api.domain.SoftwareProjectId; import fr.norad.visuwall.api.domain.TestResult; import fr.norad.visuwall.api.exception.BuildIdNotFoundException; import fr.norad.visuwall.api.exception.BuildNotFoundException; import fr.norad.visuwall.api.exception.MavenIdNotFoundException; import fr.norad.visuwall.api.exception.ProjectNotFoundException; import fr.norad.visuwall.api.exception.ViewNotFoundException; import fr.norad.visuwall.api.plugin.capability.BuildCapability; import fr.norad.visuwall.api.plugin.capability.TestCapability; import fr.norad.visuwall.api.plugin.capability.ViewCapability; import fr.norad.visuwall.providers.teamcity.TeamCity; import fr.norad.visuwall.providers.teamcity.exception.TeamCityBuildListNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityBuildNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityBuildTypeNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityChangesNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityProjectNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityProjectsNotFoundException; import fr.norad.visuwall.providers.teamcity.exception.TeamCityUserNotFoundException; import fr.norad.visuwall.providers.teamcity.resource.TeamCityAbstractBuild; import fr.norad.visuwall.providers.teamcity.resource.TeamCityBuild; import fr.norad.visuwall.providers.teamcity.resource.TeamCityBuildItem; import fr.norad.visuwall.providers.teamcity.resource.TeamCityBuildType; import fr.norad.visuwall.providers.teamcity.resource.TeamCityBuilds; import fr.norad.visuwall.providers.teamcity.resource.TeamCityChange; import fr.norad.visuwall.providers.teamcity.resource.TeamCityProject; import fr.norad.visuwall.providers.teamcity.resource.TeamCityUser; public class TeamCityConnection implements BuildCapability, TestCapability, ViewCapability { private static final Logger LOG = LoggerFactory.getLogger(TeamCityConnection.class); private boolean connected; @VisibleForTesting TeamCity teamCity; @Override public void connect(String url, String login, String password) { checkNotNull(url, "url is mandatory"); if (isBlank(url)) { throw new IllegalArgumentException("url can't be null."); } if (isBlank(login)) { LOG.info("Login is blank, new value is 'guest'"); login = "guest"; password = ""; } teamCity = new TeamCity(url, login, password); connected = true; } @Override public void close() { connected = false; } @Override public String getDescription(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String buildTypeId = softwareProjectId.getProjectId(); TeamCityBuildType buildType = teamCity.findBuildType(buildTypeId); return buildType.getDescription(); } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Cannot find description of project with software project id:" + softwareProjectId, e); } } @Override public SoftwareProjectId identify(ProjectKey projectKey) throws ProjectNotFoundException { checkConnected(); Preconditions.checkNotNull(projectKey, "projectKey is mandatory"); try { String name = projectKey.getName(); List<TeamCityProject> projects = teamCity.findAllProjects(); for (TeamCityProject project : projects) { String projectName = project.getName(); if (projectName.equals(name)) { String projectId = project.getId(); return new SoftwareProjectId(projectId); } } } catch (TeamCityProjectsNotFoundException e) { throw new ProjectNotFoundException("Can't identify software project id with project key: " + projectKey, e); } throw new ProjectNotFoundException("Can't identify software project id with project key: " + projectKey); } @Override public List<String> getBuildIds(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { Set<String> ids = new TreeSet<String>(); TeamCityBuildType buildType = teamCity.findBuildType(softwareProjectId.getProjectId()); addBuildIds(ids, buildType); List<String> arrayList = new ArrayList<String>(ids); Collections.sort(arrayList, new BuildIdComparator()); return arrayList; } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Cannot find build numbers of software project id:" + softwareProjectId, e); } } @Override public Map<SoftwareProjectId, String> listSoftwareProjectIds() { checkConnected(); Map<SoftwareProjectId, String> projectIds = new HashMap<SoftwareProjectId, String>(); try { List<TeamCityProject> projects = teamCity.findAllProjects(); for (TeamCityProject project : projects) { try { project = teamCity.findProject(project.getId()); List<TeamCityBuildType> buildTypes = project.getBuildTypes(); for (TeamCityBuildType teamCityBuildType : buildTypes) { String id = teamCityBuildType.getId(); SoftwareProjectId softwareProjectId = new SoftwareProjectId(id); projectIds.put(softwareProjectId, teamCityBuildType.getName()); } } catch (TeamCityProjectNotFoundException e) { LOG.warn("Cannot find project with id " + project.getId(), e); } } } catch (TeamCityProjectsNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Cannot build list of software project ids.", e); } } return projectIds; } @Override public BuildState getBuildState(SoftwareProjectId softwareProjectId, String buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); try { String projectId = softwareProjectId.getProjectId(); TeamCityBuild build = teamCity.findBuild(projectId, buildId); String status = build.getStatus(); return States.asVisuwallState(status); } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Cannot find build type for software project id:" + softwareProjectId, e); } catch (TeamCityBuildNotFoundException e) { try { TeamCityBuild runningBuild = teamCity.findRunningBuild(); if (buildId.equals(runningBuild.getId())) { return BuildState.UNKNOWN; } } catch (TeamCityBuildNotFoundException e1) { } throw new BuildNotFoundException("Cannot find build #" + buildId + " for software project id:" + softwareProjectId, e); } } @Override public Date getEstimatedFinishTime(SoftwareProjectId softwareProjectId, String buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); try { TeamCityBuild runningBuild = teamCity.findRunningBuild(); if (buildId.equals(runningBuild.getId())) { int seconds = runningBuild.getRunningInfo().getEstimatedTotalSeconds(); return new DateTime().plusSeconds(seconds).toDate(); } throw new BuildNotFoundException("Cannot find build #" + buildId + " for software project id:" + softwareProjectId); } catch (TeamCityBuildNotFoundException e) { throw new BuildNotFoundException("Cannot find a running build", e); } } @Override public boolean isBuilding(SoftwareProjectId softwareProjectId, String buildId) throws ProjectNotFoundException, BuildNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); try { TeamCityBuild build = teamCity.findRunningBuild(); return softwareProjectId.getProjectId().equals(build.getBuildType().getId()) && buildId.equals(build.getId()); } catch (TeamCityBuildNotFoundException e) { return false; } } @Override public String getLastBuildId(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException, BuildIdNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); TeamCityAbstractBuild lastBuild; try { lastBuild = teamCity.findLastBuild(softwareProjectId.getProjectId()); return lastBuild.getId(); } catch (TeamCityBuildNotFoundException e) { throw new BuildIdNotFoundException("Cannot find project with software project id " + softwareProjectId, e); } } @Override public String getMavenId(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException, MavenIdNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); String projectId = softwareProjectId.getProjectId(); try { return teamCity.findMavenId(projectId); } catch (fr.norad.visuwall.providers.common.MavenIdNotFoundException e) { throw new MavenIdNotFoundException("Cannot find maven id for " + softwareProjectId, e); } } @Override public String getName(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String buildTypeId = softwareProjectId.getProjectId(); TeamCityBuildType buildType = teamCity.findBuildType(buildTypeId); return buildType.getName(); } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Can't find name of project with software project id:" + softwareProjectId, e); } } @Override public boolean isClosed() { return !connected; } @Override public BuildTime getBuildTime(SoftwareProjectId softwareProjectId, String buildId) throws BuildNotFoundException, ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); try { String projectId = softwareProjectId.getProjectId(); TeamCityBuild teamcityBuild = teamCity.findBuild(projectId, buildId); return BuildTimes.createFrom(teamcityBuild); } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Cannot find build type with software project id:" + softwareProjectId, e); } catch (TeamCityBuildNotFoundException e) { throw new BuildNotFoundException("Cannot find build #" + buildId + " for software project id:" + softwareProjectId, e); } } @Override public boolean isProjectDisabled(SoftwareProjectId softwareProjectId) throws ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); try { String projectId = softwareProjectId.getProjectId(); TeamCityBuildType buildType = teamCity.findBuildType(projectId); return buildType.isPaused(); } catch (TeamCityBuildTypeNotFoundException e) { throw new ProjectNotFoundException("Can't find project with software project id:" + softwareProjectId, e); } } @Override public List<Commiter> getBuildCommiters(SoftwareProjectId softwareProjectId, String buildId) throws BuildNotFoundException, ProjectNotFoundException { checkConnected(); checkSoftwareProjectId(softwareProjectId); checkBuildId(buildId); List<Commiter> commiters = new ArrayList<Commiter>(); try { List<TeamCityChange> changes = teamCity.findChanges(Integer.valueOf(buildId)); for (TeamCityChange change : changes) { String username = change.getUsername(); Commiter commiter = new Commiter(username); try { TeamCityUser user = teamCity.findUserByUsername(username); LOG.debug("retrieved user: " + user.toString()); commiter.setName(user.getName()); commiter.setEmail(user.getEmail()); } catch (TeamCityUserNotFoundException e) { LOG.debug("user not found: " + username); if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage()); } } if (!commiters.contains(commiter)) { commiters.add(commiter); } } } catch (TeamCityChangesNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage()); } } return commiters; } @Override public TestResult analyzeUnitTests(SoftwareProjectId softwareProjectId) { checkConnected(); TestResult result = new TestResult(); try { String lastBuildId = getLastBuildId(softwareProjectId); if (isBuilding(softwareProjectId, lastBuildId)) { return result; } TeamCityBuild build = teamCity.findBuild(softwareProjectId.getProjectId(), lastBuildId.toString()); String statusText = build.getStatusText(); int failed = TestResultExtractor.extractFailed(statusText); int passed = TestResultExtractor.extractPassed(statusText); int ignored = TestResultExtractor.extractIgnored(statusText); result.setFailCount(failed); result.setPassCount(passed); result.setSkipCount(ignored); } catch (ProjectNotFoundException e) { LOG.warn("Can't analyze unit tests for softwareProjectId:" + softwareProjectId, e); } catch (BuildIdNotFoundException e) { LOG.warn("Can't analyze unit tests for softwareProjectId:" + softwareProjectId, e); } catch (TeamCityBuildTypeNotFoundException e) { LOG.warn("Can't analyze unit tests for softwareProjectId:" + softwareProjectId, e); } catch (TeamCityBuildNotFoundException e) { LOG.warn("Can't analyze unit tests for softwareProjectId:" + softwareProjectId, e); } catch (BuildNotFoundException e) { LOG.warn("Can't analyze unit tests for softwareProjectId:" + softwareProjectId, e); } return result; } @Override public TestResult analyzeIntegrationTests(SoftwareProjectId softwareProjectId) { checkConnected(); return new TestResult(); } private void addBuildIds(Set<String> numbers, TeamCityBuildType buildType) { try { String buildTypeId = buildType.getId(); TeamCityBuilds buildList = teamCity.findBuildList(buildTypeId); List<TeamCityBuildItem> builds = buildList.getBuilds(); for (TeamCityBuildItem item : builds) { numbers.add(item.getId()); } } catch (TeamCityBuildListNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage()); } } } private void checkBuildId(String buildId) { checkNotNull("buildId is mandatory"); } private void checkConnected() { checkState(connected, "You must connect your plugin"); } private void checkSoftwareProjectId(SoftwareProjectId softwareProjectId) { checkNotNull(softwareProjectId, "softwareProjectId is mandatory"); } @Override public List<SoftwareProjectId> findSoftwareProjectIdsByViews(List<String> views) { checkConnected(); List<SoftwareProjectId> softwareProjectIds = new ArrayList<SoftwareProjectId>(); for (String viewName : views) { try { TeamCityProject project = teamCity.findProjectByName(viewName); project = teamCity.findProject(project.getId()); List<TeamCityBuildType> buildTypes = project.getBuildTypes(); for (TeamCityBuildType teamCityBuildType : buildTypes) { SoftwareProjectId softwareProjectId = new SoftwareProjectId(teamCityBuildType.getId()); softwareProjectIds.add(softwareProjectId); } } catch (TeamCityProjectNotFoundException e) { LOG.warn("Cannot add build types of project " + viewName, e); } } return softwareProjectIds; } @Override public List<String> findViews() { checkConnected(); List<String> views = new ArrayList<String>(); try { List<TeamCityProject> projects = teamCity.findAllProjects(); for (TeamCityProject project : projects) { String view = project.getName(); views.add(view); } } catch (TeamCityProjectsNotFoundException e) { if (LOG.isDebugEnabled()) { LOG.debug("Can't build list of software project ids.", e); } } return views; } @Override public List<String> findProjectNamesByView(String viewName) throws ViewNotFoundException { checkConnected(); checkViewName(viewName); List<String> projects = new ArrayList<String>(); try { TeamCityProject project = teamCity.findProjectByName(viewName); project = teamCity.findProject(project.getId()); List<TeamCityBuildType> buildTypes = project.getBuildTypes(); for (TeamCityBuildType buildType : buildTypes) { String projectName = buildType.getName(); projects.add(projectName); } } catch (TeamCityProjectNotFoundException e) { throw new ViewNotFoundException("Cannot finnd project names for view: " + viewName, e); } return projects; } private void checkViewName(String viewName) { checkNotNull(viewName, "viewName is mandatory"); } }