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

com.marvelution.jira.plugins.sonar.streams.SonarStreamsActivityProvider Maven / Gradle / Ivy

/*
 * Licensed to Marvelution under one or more contributor license
 * agreements.  See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Marvelution licenses this file to you 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.marvelution.jira.plugins.sonar.streams;

import static com.atlassian.streams.spi.StandardStreamsFilterOption.ACTIVITY_KEY;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.marvelution.jira.plugins.sonar.streams.SonarStreamsFilterOptionProvider.SONAR_BASE_ACTIVITY_OBJECT_TYPE;
import static com.marvelution.jira.plugins.sonar.streams.SonarStreamsFilterOptionProvider.SONAR_ACTIVITY_OBJECT_TYPE;
import static com.marvelution.jira.plugins.sonar.streams.SonarStreamsFilterOptionProvider.SONAR_ASSOCIATION_KEY;
import static com.marvelution.jira.plugins.sonar.streams.SonarStreamsFilterOptionProvider.SONAR_SERVER_KEY;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.sonar.wsclient.Sonar;
import org.sonar.wsclient.services.Event;
import org.sonar.wsclient.services.EventQuery;

import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.plugin.webresource.UrlMode;
import com.atlassian.plugin.webresource.WebResourceUrlProvider;
import com.atlassian.sal.api.message.I18nResolver;
import com.atlassian.streams.api.ActivityRequest;
import com.atlassian.streams.api.ActivityVerb;
import com.atlassian.streams.api.StreamsEntry;
import com.atlassian.streams.api.StreamsException;
import com.atlassian.streams.api.StreamsFeed;
import com.atlassian.streams.api.UserProfile;
import com.atlassian.streams.api.common.ImmutableNonEmptyList;
import com.atlassian.streams.api.common.Option;
import com.atlassian.streams.spi.CancellableTask;
import com.atlassian.streams.spi.CancelledException;
import com.atlassian.streams.spi.Filters;
import com.atlassian.streams.spi.StandardStreamsFilterOption;
import com.atlassian.streams.spi.StreamsActivityProvider;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.marvelution.jira.plugins.sonar.services.associations.SonarAssociation;
import com.marvelution.jira.plugins.sonar.services.associations.SonarAssociationManager;
import com.marvelution.jira.plugins.sonar.services.servers.SonarClientFactory;
import com.marvelution.jira.plugins.sonar.services.servers.SonarServer;
import com.marvelution.jira.plugins.sonar.services.servers.SonarServerManager;
import com.marvelution.jira.plugins.sonar.utils.PluginHelper;

/**
 * Hudson specific implementation of the {@link StreamsActivityProvider}
 * 
 * @author Mark Rekveld
 * @since 4.2.0
 */
public class SonarStreamsActivityProvider implements StreamsActivityProvider {

	private final Logger logger = Logger.getLogger(SonarStreamsActivityProvider.class);
	private final I18nResolver i18nResolver;
	private final ProjectManager projectManager;
	private final SonarAssociationManager associationManager;
	private final SonarServerManager serverManager;
	private final SonarClientFactory clientFactory;
	private final WebResourceUrlProvider webResourceUrlProvider;

	/**
	 * Constructor
	 *
	 * @param i18nResolver the {@link I18nResolver} implementation
	 * @param projectManager the {@link ProjectManager} implementation
	 * @param associationManager the {@link SonarAssociationManager} implementation
	 * @param serverManager the {@link SonarServerManager} implementation
	 * @param clientFactory the {@link SonarClientFactory} implementation
	 * @param webResourceUrlProvider the {@link WebResourceUrlProvider} implementation
	 */
	public SonarStreamsActivityProvider(I18nResolver i18nResolver, ProjectManager projectManager, SonarAssociationManager associationManager,
					SonarServerManager serverManager, SonarClientFactory clientFactory,
					WebResourceUrlProvider webResourceUrlProvider) {
			this.i18nResolver = checkNotNull(i18nResolver, "i18nResolver");
			this.projectManager = checkNotNull(projectManager, "projectManager");
			this.associationManager = checkNotNull(associationManager, "associationManager");
			this.serverManager = checkNotNull(serverManager, "serverManager");
			this.clientFactory = checkNotNull(clientFactory, "clientFactory");
			this.webResourceUrlProvider = checkNotNull(webResourceUrlProvider, "webResourceUrlProvider");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public CancellableTask getActivityFeed(final ActivityRequest activityRequest) throws StreamsException {
		return new CancellableTask() {

			final AtomicBoolean cancelled = new AtomicBoolean(false);

			/**
			 * {@inheritDoc}
			 */
			@Override
			public StreamsFeed call() throws Exception {
				Set eventTypes = getActivityFilters(activityRequest);
				Set associations = Sets.newHashSet();
				Project project = null;
				if ((project = getProjectFromFilter(activityRequest)) != null) {
					logger.debug("Only getting the associations related to project with key: " + project.getKey());
					associations.addAll(associationManager.getAssociations(project));
				} else {
					if (activityRequest.getProviderFilters().containsKey(SONAR_ASSOCIATION_KEY)) {
						logger.debug("Only getting events from associations in the provider filter");
						for (Integer associationId : getAssociationFilters(activityRequest)) {
							associations.add(associationManager.getAssociation(associationId));
						}
					}
					if (activityRequest.getProviderFilters().containsKey(SONAR_SERVER_KEY)) {
						logger.debug("Only getting events from servers in the provider filter");
						for (Integer serverId : getServerFilters(activityRequest)) {
							SonarServer server = serverManager.getServer(serverId);
							associations.addAll(Arrays.asList(server.getAssociations()));
						}
					}
					if (!activityRequest.getProviderFilters().containsKey(SONAR_ASSOCIATION_KEY)
						&& !activityRequest.getProviderFilters().containsKey(SONAR_SERVER_KEY)) {
						logger.debug("Getting events from all associations");
						associations.addAll(associationManager.getAssociations());
					}
				}
				EventQuery query = new EventQuery();
				if (!eventTypes.contains(EventType.ALL)) {
					Set categories = Sets.newHashSet();
					for (EventType type : eventTypes) {
						categories.add(type.category);
					}
					query.setCategories(categories.toArray(new String[categories.size()]));
				}
				List streamsEntries = Lists.newArrayList();
				for (SonarAssociation association : associations) {
					// Make sure we only have the servers that the administrator allows in the activity streams
					if (association.getSonarServer().isIncludeInStreams()) {
						Sonar client = clientFactory.create(association.getSonarServer());
						query.setResourceKey(association.getSonarProject());
						if (cancelled.get()) {
							throw new CancelledException();
						}
						try {
							logger.debug("Connecting to server " + association.getSonarServer().getHost()
								+ " to get all the activities matching the query");
							for (Event event : client.findAll(query)) {
								streamsEntries.add(toStreamsEntry(association, event));
							}
						} catch (Exception e) {
							logger.error("Failed to execute query: " + association.getSonarServer().getHost()
								+ query.getUrl());
							throw new StreamsException("Failed to get activities from assocaition "
								+ association.getSonarServer().getName() + " / " + association.getSonarProject(), e);
						}
					} else {
						logger.debug("Skipping server '" + association.getSonarServer().getName()
							+ "' in activity streams");
					}
				}
				return new StreamsFeed(i18nResolver.getText("streams.sonar.feed.title"), streamsEntries,
					Option. none());
			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public Result cancel() {
				cancelled.set(true);
				return Result.CANCELLED;
			}

		};
	}

	/**
	 * Transform a given {@link Event} to a {@link StreamsEntry}
	 * 
	 * @param from the {@link Event} to transform
	 * @return the {@link StreamsEntry}
	 * @throws URISyntaxException 
	 */
	private StreamsEntry toStreamsEntry(SonarAssociation association, final Event event) throws URISyntaxException {
		URI eventUri = new URI(association.getSonarServer().getHost() + "/dashboard/index/"
			+ association.getSonarProject());
		StreamsEntry.ActivityObject activityObject =
			new StreamsEntry.ActivityObject(StreamsEntry.ActivityObject.params().id("")
				.alternateLinkUri(eventUri).activityObjectType(SONAR_ACTIVITY_OBJECT_TYPE));
		ActivityVerb verb =
			SonarStreamsFilterOptionProvider.getActivityVerbForEvent(EventType.getEventTypeForEvent(event));
		final StreamsEntry.Renderer renderer = new SonarStreamsEntryRenderer(i18nResolver, event);
		UserProfile.Builder builder = new UserProfile.Builder("sonar").fullName("Sonar")
			.profilePictureUri(Option.option(
				URI.create(webResourceUrlProvider.getStaticPluginResourceUrl(PluginHelper.getPluginKey()
				+ ":sonar-stream-resources", "sonarsource-wave.png", UrlMode.ABSOLUTE))));
		return new StreamsEntry(StreamsEntry
			.params()
			.id(eventUri)
			.postedDate(new DateTime(event.getDate()))
			.authors(ImmutableNonEmptyList.of(builder.build()))
			.addActivityObject(activityObject)
			.verb(verb)
			.addLink(
				URI.create(webResourceUrlProvider.getStaticPluginResourceUrl(PluginHelper.getPluginKey()
					+ ":sonar-stream-resources", "puzzle-piece.gif", UrlMode.ABSOLUTE)),
				StreamsActivityProvider.ICON_LINK_REL,
				Option.none(String.class))
			.alternateLinkUri(eventUri)
			.renderer(renderer)
			.applicationType("Sonar"), i18nResolver);
	}

	/**
	 * Get a single {@link Project} from the Standard Streams Filter
	 * 
	 * @param activityRequest the {@link ActivityRequest}
	 * @return the {@link Project}, may be null
	 */
	private Project getProjectFromFilter(ActivityRequest activityRequest) {
		Set keys =
			Filters.getIsValues(activityRequest.getStandardFilters().get(StandardStreamsFilterOption.PROJECT_KEY));
		if (keys != null && keys.size() == 1) {
			try {
				return projectManager.getProjectObjByKey(keys.toArray(new String[keys.size()])[0]);
			} catch (Exception e) {
				// Ignore this. Just invalid configuration
			}
		}
		return null;
	}

	/**
	 * Get all the active Association Ids from the {@link ActivityRequest}
	 * 
	 * @param activityRequest the {@link ActivityRequest} to get all the association Ids from
	 * @return the {@link Set} of association ids
	 */
	private Set getAssociationFilters(ActivityRequest activityRequest) {
		Set ids = Sets.newHashSet();
		for (SonarAssociation association : associationManager.getAssociations()) {
			ids.add(association.getID());
		}
		return getFiltersFromActivityRequest(activityRequest, SONAR_ASSOCIATION_KEY, ids, toInteger);
	}

	/**
	 * Get all the active Server Ids from the {@link ActivityRequest}
	 * 
	 * @param activityRequest the {@link ActivityRequest} to get all the server Ids from
	 * @return the {@link Set} of server ids
	 */
	private Set getServerFilters(ActivityRequest activityRequest) {
		Set ids = Sets.newHashSet();
		for (SonarServer server : serverManager.getServers()) {
			ids.add(server.getID());
		}
		return getFiltersFromActivityRequest(activityRequest, SONAR_SERVER_KEY, ids, toInteger);
	}

	/**
	 * Get all the active {@link EventType} objects from the {@link ActivityRequest}
	 * 
	 * @param activityRequest the {@link ActivityRequest} to get the options from
	 * @return the {@link Set} of {@link EventType} objects
	 */
	private Set getActivityFilters(ActivityRequest activityRequest) {
		return getFiltersFromActivityRequest(activityRequest, ACTIVITY_KEY, Sets.newHashSet(EventType.values()),
			toSonarActivity);
	}

	/**
	 * Get the Provider Filter values out of the {@link ActivityRequest}
	 * 
	 * @param activityRequest the {@link ActivityRequest}
	 * @param key the key of the Provider Filter
	 * @param allItems the default all items for the provider
	 * @param transformer the {@link Function} to use to transform the {@link String} value to the needed value
	 * @return {@link Set} of transformed values
	 */
	private  Set getFiltersFromActivityRequest(ActivityRequest activityRequest, String key, Set allItems,
					Function transformer) {
		Set isStringValues = Filters.getIsValues(activityRequest.getProviderFilters().get(key));
		Set notStringValues = Filters.getNotValues(activityRequest.getProviderFilters().get(key));
		Set isValues;
		if (isStringValues.isEmpty()) {
			isValues = ImmutableSet.copyOf(allItems);
		} else {
			isValues = ImmutableSet.copyOf(Iterables.transform(isStringValues, transformer));
		}
		Set notValues = ImmutableSet.copyOf(Iterables.transform(notStringValues, transformer));
		return Sets.difference(isValues, notValues);
	}

	/**
	 * Converts an activity option key (in the form of a String) to an {@link EventType}.
	 */
	private static final Function toSonarActivity = new Function() {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public EventType apply(String option) {
			String hudsonActivity = option.substring((SONAR_BASE_ACTIVITY_OBJECT_TYPE + ":").length());
			return EventType.valueOf(hudsonActivity.replace(" ", "_").toUpperCase());
		}

	};

	/**
	 * Converts an activity option key (in the form of a String) to an {@link Integer}.
	 */
	private static final Function toInteger = new Function() {

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Integer apply(String option) {
			return Integer.parseInt(option);
		}

	};

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy