PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/components/server-activity-streams/src/main/java/org/marvelution/jji/streams/JenkinsStreamsActivityProvider.java

https://bitbucket.org/marvelution/jira-jenkins-integration
Java | 295 lines | 221 code | 23 blank | 51 comment | 9 complexity | e41ecc4464dfbe43fc7affdcfd368e02 MD5 | raw file
  1. /*
  2. * Copyright (c) 2012-present Marvelution Holding B.V.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.marvelution.jji.streams;
  17. import java.io.*;
  18. import java.net.*;
  19. import java.util.*;
  20. import java.util.concurrent.atomic.*;
  21. import java.util.stream.*;
  22. import javax.inject.*;
  23. import org.marvelution.jji.api.*;
  24. import org.marvelution.jji.data.services.api.*;
  25. import org.marvelution.jji.model.*;
  26. import com.atlassian.jira.project.*;
  27. import com.atlassian.jira.security.*;
  28. import com.atlassian.plugin.spring.scanner.annotation.imports.*;
  29. import com.atlassian.plugin.webresource.*;
  30. import com.atlassian.sal.api.message.*;
  31. import com.atlassian.streams.api.*;
  32. import com.atlassian.streams.api.common.*;
  33. import com.atlassian.streams.spi.*;
  34. import com.atlassian.templaterenderer.*;
  35. import com.google.common.collect.*;
  36. import org.apache.commons.lang3.*;
  37. import org.joda.time.*;
  38. import org.slf4j.*;
  39. import static com.atlassian.jira.permission.ProjectPermissions.*;
  40. import static com.atlassian.plugin.webresource.UrlMode.*;
  41. import static com.atlassian.streams.api.StreamsEntry.ActivityObject.*;
  42. import static com.atlassian.streams.api.common.ImmutableNonEmptyList.*;
  43. import static com.atlassian.streams.api.common.Option.*;
  44. import static com.atlassian.streams.spi.StandardStreamsFilterOption.*;
  45. import static java.util.stream.Collectors.*;
  46. import static javax.ws.rs.core.UriBuilder.*;
  47. /**
  48. * {@link StreamsActivityProvider} for Jenkins Builds
  49. *
  50. * @author Mark Rekveld
  51. * @since 1.0.0
  52. */
  53. public class JenkinsStreamsActivityProvider
  54. implements StreamsActivityProvider
  55. {
  56. private static final Logger LOGGER = LoggerFactory.getLogger(JenkinsStreamsActivityProvider.class);
  57. private final AddonHelper addonHelper;
  58. private final IssueLinkService issueLinkService;
  59. private final ProjectManager projectManager;
  60. private final PermissionManager permissionManager;
  61. private final JiraAuthenticationContext authenticationContext;
  62. private final I18nResolver i18nResolver;
  63. private final TemplateRenderer templateRenderer;
  64. private final WebResourceUrlProvider urlProvider;
  65. @Inject
  66. public JenkinsStreamsActivityProvider(
  67. AddonHelper addonHelper,
  68. IssueLinkService issueLinkService,
  69. @ComponentImport ProjectManager projectManager,
  70. @ComponentImport PermissionManager permissionManager,
  71. @ComponentImport JiraAuthenticationContext authenticationContext,
  72. @ComponentImport I18nResolver i18nResolver,
  73. @ComponentImport @Named("templateRenderer") TemplateRenderer templateRenderer,
  74. @ComponentImport WebResourceUrlProvider urlProvider)
  75. {
  76. this.addonHelper = addonHelper;
  77. this.issueLinkService = issueLinkService;
  78. this.projectManager = projectManager;
  79. this.permissionManager = permissionManager;
  80. this.authenticationContext = authenticationContext;
  81. this.i18nResolver = i18nResolver;
  82. this.templateRenderer = templateRenderer;
  83. this.urlProvider = urlProvider;
  84. }
  85. @Override
  86. public CancellableTask<StreamsFeed> getActivityFeed(final ActivityRequest activityRequest)
  87. {
  88. final IssueLinkService.LinkRequest filter = getLinkRequest(activityRequest);
  89. LOGGER.debug("Streams filter: {}", filter);
  90. return new CancellableTask<StreamsFeed>()
  91. {
  92. private final AtomicBoolean cancelled = new AtomicBoolean(false);
  93. @Override
  94. public StreamsFeed call()
  95. {
  96. Iterable<StreamsEntry> entries = new ArrayList<>();
  97. if (!filter.getInProjectKeys().isEmpty())
  98. {
  99. Iterable<Build> builds = issueLinkService.getBuildsForLinkRequest(filter, activityRequest.getMaxResults());
  100. if (cancelled.get())
  101. {
  102. throw new CancelledException();
  103. }
  104. LOGGER.debug("Found build entries for stream: {}", builds);
  105. entries = transformEntries(builds, cancelled);
  106. }
  107. return new StreamsFeed(i18nResolver.getText("jenkins.streams.feed.title"), entries, none());
  108. }
  109. @Override
  110. public CancellableTask.Result cancel()
  111. {
  112. cancelled.set(true);
  113. return CancellableTask.Result.CANCELLED;
  114. }
  115. };
  116. }
  117. /**
  118. * @since 3.3.0
  119. */
  120. IssueLinkService.LinkRequest getLinkRequest(ActivityRequest activityRequest)
  121. {
  122. IssueLinkService.LinkRequest request = new IssueLinkService.LinkRequest();
  123. Multimap<String, Pair<StreamsFilterType.Operator, Iterable<String>>> standardFilters = activityRequest.getStandardFilters();
  124. // We cannot take user filters into account, so if filtering for specific users, then don't load any builds.
  125. if (Filters.getIsValues(standardFilters.get(USER.getKey())).isEmpty())
  126. {
  127. request.setInProjectKeys(getInProjectsByPermission(Filters.getIsValues(standardFilters.get(PROJECT_KEY))));
  128. request.setNotInProjectKeys(Filters.getNotValues(standardFilters.get(PROJECT_KEY)));
  129. request.setInIssueKeys(Filters.getIsValues(standardFilters.get(ISSUE_KEY.getKey())));
  130. request.setNotInIssueKeys(Filters.getNotValues(standardFilters.get(ISSUE_KEY.getKey())));
  131. }
  132. return request;
  133. }
  134. /**
  135. * Transform the given Builds to StreamEntries
  136. *
  137. * @param builds the Builds to transform
  138. * @param cancelled cancelled flag
  139. * @return the stream entries
  140. */
  141. private Iterable<StreamsEntry> transformEntries(
  142. Iterable<Build> builds,
  143. AtomicBoolean cancelled)
  144. {
  145. Set<Build> processed = new HashSet<>();
  146. List<StreamsEntry> entries = new ArrayList<>();
  147. for (Build build : builds)
  148. {
  149. if (cancelled.get())
  150. {
  151. throw new CancelledException();
  152. }
  153. if (!processed.contains(build))
  154. {
  155. try
  156. {
  157. entries.add(toStreamEntry(build));
  158. }
  159. catch (Exception e)
  160. {
  161. LOGGER.warn("Unable to get Streams Entry for build {}: {}", build, e.getMessage());
  162. LOGGER.debug("toStreamEntry error", e);
  163. }
  164. processed.add(build);
  165. }
  166. }
  167. return entries;
  168. }
  169. /**
  170. * Transform a {@link Build} to a {@link StreamsEntry}
  171. *
  172. * @param build the {@link Build} to transform
  173. * @return the {@link StreamsEntry}
  174. */
  175. private StreamsEntry toStreamEntry(final Build build)
  176. {
  177. final Job job = build.getJob();
  178. final Site site = job.getSite();
  179. StreamsEntry.ActivityObject activityObject = new StreamsEntry.ActivityObject(
  180. params().id(String.valueOf(build.getId())).alternateLinkUri(build.getDisplayUrl()));
  181. StreamsEntry.Renderer renderer = new StreamsEntry.Renderer()
  182. {
  183. @Override
  184. public Html renderTitleAsHtml(StreamsEntry streamsEntry)
  185. {
  186. Map<String, Object> context = new HashMap<>();
  187. String displayName = build.getDisplayName();
  188. if (StringUtils.isBlank(displayName))
  189. {
  190. displayName = job.getDisplayName() + " > #" + build.getNumber();
  191. }
  192. context.put("build_display_name", displayName);
  193. context.put("build_url", build.getDisplayUrl());
  194. context.put("build_deleted", build.isDeleted() || job.isDeleted());
  195. context.put("build_result", build.getResult().key());
  196. return new Html(renderTemplate("/templates/streams/stream-title.vm", context));
  197. }
  198. @Override
  199. public Option<Html> renderSummaryAsHtml(StreamsEntry streamsEntry)
  200. {
  201. return none();
  202. }
  203. @Override
  204. public Option<Html> renderContentAsHtml(StreamsEntry streamsEntry)
  205. {
  206. Map<String, Object> context = new HashMap<>();
  207. context.put("build_duration", build.getFormattedDuration());
  208. context.put("build_cause", build.getCause());
  209. context.put("node", build.getBuiltOn());
  210. return Option.some(new Html(renderTemplate("/templates/streams/stream-content.vm", context)));
  211. }
  212. };
  213. URI icon = fromUri(urlProvider.getStaticPluginResourceUrl(addonHelper.getCompleteModuleKey("images"), "images", ABSOLUTE)).path(
  214. "/icon96_{siteType}.png").build(site.getType().value());
  215. NonEmptyIterable<UserProfile> authors = of(new UserProfile.Builder(site.getName()).profilePictureUri(option(icon))
  216. .profilePageUri(option(site.getDisplayUrl()))
  217. .build());
  218. return new StreamsEntry(StreamsEntry.params()
  219. .id(build.getDisplayUrl())
  220. .postedDate(new DateTime(build.getTimestamp()))
  221. .authors(authors)
  222. .addActivityObject(activityObject)
  223. .verb(ActivityVerbs.post())
  224. .alternateLinkUri(build.getDisplayUrl())
  225. .renderer(renderer)
  226. .applicationType("org.marvelution.jenkins"), i18nResolver);
  227. }
  228. /**
  229. * Get all the Project keys where the user has view source permissions for
  230. *
  231. * @param inProjectKeys {@link Set} of project keys set in the gadget
  232. * @return the {@link Set} of projects to include in the filter
  233. */
  234. private Set<String> getInProjectsByPermission(Set<String> inProjectKeys)
  235. {
  236. Stream<Project> projects;
  237. if (inProjectKeys.isEmpty())
  238. {
  239. projects = projectManager.getProjectObjects().stream();
  240. }
  241. else
  242. {
  243. projects = inProjectKeys.stream().map(projectManager::getProjectObjByKey);
  244. }
  245. return projects.filter(project -> permissionManager.hasPermission(VIEW_DEV_TOOLS, project, authenticationContext.getLoggedInUser()))
  246. .map(Project::getKey)
  247. .collect(toSet());
  248. }
  249. /**
  250. * Render the given template using the given context
  251. *
  252. * @param template the template name
  253. * @param context the context {@link Map}
  254. * @return the rendered template
  255. */
  256. private String renderTemplate(
  257. String template,
  258. Map<String, Object> context)
  259. {
  260. StringWriter writer = new StringWriter();
  261. try
  262. {
  263. templateRenderer.render(template, context, writer);
  264. }
  265. catch (IOException e)
  266. {
  267. LOGGER.warn(e.getMessage());
  268. LOGGER.debug("Failed to render template: {}", template, e);
  269. }
  270. return writer.toString();
  271. }
  272. }