Skip to content

Commit

Permalink
Merge pull request #76 from blackducksoftware/assigning-users-to-proj…
Browse files Browse the repository at this point in the history
…ects

Assigning users to the mapped projects
  • Loading branch information
James Richard authored May 2, 2019
2 parents 97be530 + 5a6ac30 commit a418834
Show file tree
Hide file tree
Showing 6 changed files with 298 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
/**
* Black Duck JIRA Plugin
*
* Copyright (C) 2019 Black Duck Software, Inc.
* http://www.blackducksoftware.com/
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.blackducksoftware.integration.jira.common;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.blackducksoftware.integration.jira.common.blackduck.BlackDuckConnectionHelper;
import com.blackducksoftware.integration.jira.common.model.BlackDuckProjectMapping;
import com.blackducksoftware.integration.jira.common.settings.GlobalConfigurationAccessor;
import com.blackducksoftware.integration.jira.common.settings.PluginErrorAccessor;
import com.blackducksoftware.integration.jira.common.settings.model.PluginBlackDuckServerConfigModel;
import com.blackducksoftware.integration.jira.config.model.BlackDuckJiraConfigSerializable;
import com.synopsys.integration.blackduck.api.generated.component.AssignedUserRequest;
import com.synopsys.integration.blackduck.api.generated.discovery.ApiDiscovery;
import com.synopsys.integration.blackduck.api.generated.response.AssignedProjectView;
import com.synopsys.integration.blackduck.api.generated.view.ProjectView;
import com.synopsys.integration.blackduck.api.generated.view.UserView;
import com.synopsys.integration.blackduck.service.BlackDuckService;
import com.synopsys.integration.blackduck.service.BlackDuckServicesFactory;
import com.synopsys.integration.exception.IntegrationException;

//TODO remove this when Super users in Black Duck no longer need to be assigned to a project in order to receive notifications for that project. Remove when all customers update to the fixed version of BD
public class BlackDuckAssignUtil {
private final BlackDuckJiraLogger logger = new BlackDuckJiraLogger(Logger.getLogger(this.getClass().getName()));

public void assignUserToBlackDuckProject(final PluginErrorAccessor pluginErrorAccessor, final GlobalConfigurationAccessor globalConfigurationAccessor) {
try {
final Set<BlackDuckProjectMapping> blackDuckProjectMappings = getBlackDuckProjectMappings(globalConfigurationAccessor);
if (blackDuckProjectMappings.isEmpty()) {
return;
}
final BlackDuckService blackDuckService = getBlackDuckService(globalConfigurationAccessor);
final List<ProjectView> allProjects = getAllBDProjects(blackDuckService);
final Set<ProjectView> matchingProjects = getMatchingBDProjects(blackDuckProjectMappings, allProjects);
if (matchingProjects.isEmpty()) {
return;
}
final UserView currentUser = getCurrentUser(blackDuckService);
final Set<ProjectView> nonAssignedProjects = getProjectsThatNeedAssigning(blackDuckService, currentUser, matchingProjects);
if (nonAssignedProjects.isEmpty()) {
return;
}
assignUserToProjects(pluginErrorAccessor, blackDuckService, currentUser, nonAssignedProjects);
} catch (final IntegrationException e) {
logger.error("Could not assign the Black Duck user to the configured Black Duck projects. " + e.getMessage(), e);
pluginErrorAccessor.addBlackDuckError(e, "assignUserToBlackDuckProject");
}
}

public Set<BlackDuckProjectMapping> getBlackDuckProjectMappings(final GlobalConfigurationAccessor globalConfigurationAccessor) {
if (null == globalConfigurationAccessor.getIssueCreationConfig() && null == globalConfigurationAccessor.getIssueCreationConfig().getProjectMapping()) {
logger.debug("There is no issue creation configuration or project mappings. Skipping assigning the user to the BD Project.");
return new HashSet<>();
}
final String projectMappingJson = globalConfigurationAccessor.getIssueCreationConfig().getProjectMapping().getMappingsJson();
if (StringUtils.isBlank(projectMappingJson)) {
logger.debug("There are no project mappings. Skipping assigning the user to the BD Project.");
return new HashSet<>();
}
final BlackDuckJiraConfigSerializable config = new BlackDuckJiraConfigSerializable();
config.setHubProjectMappingsJson(projectMappingJson);
if (config.getHubProjectMappings().isEmpty()) {
logger.debug("There are no project mappings in the mapping json. Skipping assigning the user to the BD Project.");
}
return config.getHubProjectMappings();
}

public BlackDuckService getBlackDuckService(final GlobalConfigurationAccessor globalConfigurationAccessor) throws IntegrationException {
final BlackDuckConnectionHelper blackDuckConnectionHelper = new BlackDuckConnectionHelper();

final PluginBlackDuckServerConfigModel blackDuckServerConfig = globalConfigurationAccessor.getBlackDuckServerConfig();
final BlackDuckServicesFactory blackDuckServicesFactory = blackDuckConnectionHelper.createBlackDuckServicesFactory(logger, blackDuckServerConfig.createBlackDuckServerConfigBuilder());

return blackDuckServicesFactory.createBlackDuckService();
}

public List<ProjectView> getAllBDProjects(final BlackDuckService blackDuckService) throws IntegrationException {
return blackDuckService.getAllResponses(ApiDiscovery.PROJECTS_LINK_RESPONSE);
}

public Set<ProjectView> getMatchingBDProjects(final Set<BlackDuckProjectMapping> projectMappings, final List<ProjectView> allProjects) throws IntegrationException {
final Map<String, ProjectView> projectMap = allProjects.stream().collect(Collectors.toMap(ProjectView::getName, Function.identity()));

final Set<ProjectView> matchingProjects = new HashSet<>();
for (final BlackDuckProjectMapping blackDuckProjectMapping : projectMappings) {
final String blackDuckProjectName = blackDuckProjectMapping.getBlackDuckProjectName();
if (blackDuckProjectMapping.isProjectPattern()) {
matchingProjects.addAll(projectMap.entrySet().stream()
.filter(entry -> entry.getKey().matches(blackDuckProjectName))
.map(Map.Entry::getValue)
.collect(Collectors.toSet()));
} else {
final ProjectView projectView = projectMap.get(blackDuckProjectName);
matchingProjects.add(projectView);
}
}
if (matchingProjects.isEmpty()) {
logger.debug("There are no BD projects that map the projects configured in the project mappings. Skipping assigning the user to the BD Project.");
}
return matchingProjects;
}

public UserView getCurrentUser(final BlackDuckService blackDuckService) throws IntegrationException {
return blackDuckService.getResponse(ApiDiscovery.CURRENT_USER_LINK_RESPONSE);
}

public Set<ProjectView> getProjectsThatNeedAssigning(final BlackDuckService blackDuckService, final UserView currentUser, final Set<ProjectView> matchingProjects) throws IntegrationException {
final Set<String> assignedProjects = blackDuckService.getAllResponses(currentUser, UserView.PROJECTS_LINK_RESPONSE)
.stream()
.map(AssignedProjectView::getName)
.collect(Collectors.toSet());
final Set<ProjectView> nonAssignedProjects = matchingProjects.stream()
.filter(project -> !assignedProjects.contains(project.getName()))
.collect(Collectors.toSet());

if (nonAssignedProjects.isEmpty()) {
logger.debug("There are no BD projects that need to have this User assigned to them. Skipping assigning the user to the BD Project.");
}
return nonAssignedProjects;
}

public void assignUserToProjects(final PluginErrorAccessor pluginErrorAccessor, final BlackDuckService blackDuckService, final UserView currentUser, final Set<ProjectView> projectsToAssign) throws IntegrationException {
final AssignedUserRequest assignedUserRequest = new AssignedUserRequest();
assignedUserRequest.setUser(currentUser.getHref().orElseThrow(() -> new IntegrationException(String.format("The current user, %s, does not have an href.", currentUser.getUserName()))));
for (final ProjectView projectView : projectsToAssign) {
final Optional<String> projectUsersLinkOptional = projectView.getFirstLink(ProjectView.USERS_LINK);
if (projectUsersLinkOptional.isPresent()) {
blackDuckService.post(projectUsersLinkOptional.get(), assignedUserRequest);
} else {
final String errorMessage = String.format("Could not assign the user, %s, to the project %s because there is no users link.", currentUser.getUserName(), projectView.getName());
logger.error(errorMessage);
pluginErrorAccessor.addBlackDuckError(errorMessage, "assignUserToBlackDuckProject");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import com.blackducksoftware.integration.jira.common.settings.GlobalConfigurationAccessor;
import com.blackducksoftware.integration.jira.common.settings.JiraSettingsAccessor;
import com.blackducksoftware.integration.jira.common.settings.PluginConfigurationAccessor;
import com.blackducksoftware.integration.jira.common.settings.PluginErrorAccessor;
import com.blackducksoftware.integration.jira.common.settings.model.GeneralIssueCreationConfigModel;
import com.blackducksoftware.integration.jira.common.settings.model.PluginGroupsConfigModel;
import com.blackducksoftware.integration.jira.common.settings.model.PluginIssueCreationConfigModel;
Expand All @@ -66,16 +67,20 @@ public class IssueCreationConfigActions {
private final BlackDuckJiraLogger logger = new BlackDuckJiraLogger(Logger.getLogger(this.getClass().getName()));
private final PluginConfigurationAccessor pluginConfigurationAccessor;
private final GlobalConfigurationAccessor globalConfigurationAccessor;
private final PluginErrorAccessor pluginErrorAccessor;
private final AuthorizationChecker authorizationChecker;
private final ProjectManager projectManager;
private final WorkflowHelper workflowHelper;
private final BlackDuckMonitor blackDuckMonitor;
private final ProjectMappingConfigActions projectMappingConfigActions;

private transient Thread assignmentThread;

public IssueCreationConfigActions(final JiraSettingsAccessor jiraSettingsAccessor, final AuthorizationChecker authorizationChecker, final ProjectManager projectManager,
final WorkflowHelper workflowHelper, final BlackDuckMonitor blackDuckMonitor) {
this.pluginConfigurationAccessor = jiraSettingsAccessor.createPluginConfigurationAccessor();
this.globalConfigurationAccessor = jiraSettingsAccessor.createGlobalConfigurationAccessor();
this.pluginErrorAccessor = jiraSettingsAccessor.createPluginErrorAccessor();
this.authorizationChecker = authorizationChecker;
this.projectManager = projectManager;
this.workflowHelper = workflowHelper;
Expand Down Expand Up @@ -208,9 +213,19 @@ public BlackDuckJiraConfigSerializable updateConfig(final BlackDuckJiraConfigSer
globalConfigurationAccessor.setIssueCreationConfig(issueCreationConfig);
updatePluginTaskInterval(previousInterval.orElse(0), intervalBetweenChecks);

startUserAssignment();

return config;
}

private void startUserAssignment() {
if (null != assignmentThread && assignmentThread.isAlive()) {
assignmentThread.interrupt();
}
assignmentThread = new UserAssignThread("userAssignmentThread", logger, globalConfigurationAccessor, pluginErrorAccessor);
assignmentThread.start();
}

private void validateCreator(final BlackDuckJiraConfigSerializable config) {
if (StringUtils.isBlank(config.getCreator())) {
config.setGeneralSettingsError(JiraConfigErrorStrings.NO_CREATOR_SPECIFIED_ERROR);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Black Duck JIRA Plugin
*
* Copyright (C) 2019 Black Duck Software, Inc.
* http://www.blackducksoftware.com/
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.blackducksoftware.integration.jira.config.controller.action;

import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeoutException;

import com.blackducksoftware.integration.jira.common.BlackDuckAssignUtil;
import com.blackducksoftware.integration.jira.common.BlackDuckJiraLogger;
import com.blackducksoftware.integration.jira.common.model.BlackDuckProjectMapping;
import com.blackducksoftware.integration.jira.common.settings.GlobalConfigurationAccessor;
import com.blackducksoftware.integration.jira.common.settings.PluginErrorAccessor;
import com.synopsys.integration.blackduck.api.generated.view.ProjectView;
import com.synopsys.integration.blackduck.api.generated.view.UserView;
import com.synopsys.integration.blackduck.service.BlackDuckService;
import com.synopsys.integration.exception.IntegrationException;

public class UserAssignThread extends Thread {
private final BlackDuckJiraLogger logger;
private final GlobalConfigurationAccessor globalConfigurationAccessor;
private final PluginErrorAccessor pluginErrorAccessor;

private transient boolean shouldTimeout;

public UserAssignThread(final String threadName, final BlackDuckJiraLogger logger, final GlobalConfigurationAccessor globalConfigurationAccessor, final PluginErrorAccessor pluginErrorAccessor) {
super(threadName);
this.logger = logger;
this.globalConfigurationAccessor = globalConfigurationAccessor;
this.pluginErrorAccessor = pluginErrorAccessor;
}

@Override
public void run() {
logger.debug("Starting User Assignment thread");
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
shouldTimeout = true;
}
}, 600000);

final BlackDuckAssignUtil blackDuckAssignUtil = new BlackDuckAssignUtil();
try {
final Set<BlackDuckProjectMapping> blackDuckProjectMappings = blackDuckAssignUtil.getBlackDuckProjectMappings(globalConfigurationAccessor);
if (blackDuckProjectMappings.isEmpty()) {
return;
}
checkShouldInterruptOrTimeout();
final BlackDuckService blackDuckService = blackDuckAssignUtil.getBlackDuckService(globalConfigurationAccessor);
final List<ProjectView> allProjects = blackDuckAssignUtil.getAllBDProjects(blackDuckService);
checkShouldInterruptOrTimeout();
final Set<ProjectView> matchingProjects = blackDuckAssignUtil.getMatchingBDProjects(blackDuckProjectMappings, allProjects);
if (matchingProjects.isEmpty()) {
return;
}
checkShouldInterruptOrTimeout();
final UserView currentUser = blackDuckAssignUtil.getCurrentUser(blackDuckService);
checkShouldInterruptOrTimeout();
final Set<ProjectView> nonAssignedProjects = blackDuckAssignUtil.getProjectsThatNeedAssigning(blackDuckService, currentUser, matchingProjects);
if (nonAssignedProjects.isEmpty()) {
return;
}
checkShouldInterruptOrTimeout();
blackDuckAssignUtil.assignUserToProjects(pluginErrorAccessor, blackDuckService, currentUser, nonAssignedProjects);
logger.debug("Completed User Assignment");
} catch (final IntegrationException e) {
logger.error("Could not assign the Black Duck user to the configured Black Duck projects. " + e.getMessage(), e);
pluginErrorAccessor.addBlackDuckError(e, "assignUserToBlackDuckProject");
} catch (final InterruptedException e) {
logger.warn("The user assignment thread was interrupted.");
} catch (final TimeoutException e) {
logger.error("The user assignment thread timed out after 10 minutes.");
}
}

private void checkShouldInterruptOrTimeout() throws InterruptedException, TimeoutException {
if (!Thread.currentThread().isAlive() || Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
if (shouldTimeout) {
throw new TimeoutException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public Optional<String> execute(final String previousStartDate) {
final Optional<UserView> optionalUserView = getBlackDuckUser(blackDuckServicesFactory.createBlackDuckService());
if (!optionalUserView.isPresent()) {
logger.warn("Will not request notifications from Black Duck because of an invalid user configuration");
return Optional.of(previousStartDate);
return Optional.ofNullable(previousStartDate);
}
final UserView blackDuckUser = optionalUserView.get();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ private void runUpgrade(final Date installDate) {
upgradeSteps.updateInstallDate(installDate);
upgradeSteps.updateOldMappingsIfNeeded();
upgradeSteps.upgradeToV6FromAny();
upgradeSteps.assignUserToBlackDuckProject();
}

private Number getIntervalInMinutes() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.commons.lang3.StringUtils;

import com.atlassian.sal.api.pluginsettings.PluginSettings;
import com.blackducksoftware.integration.jira.common.BlackDuckAssignUtil;
import com.blackducksoftware.integration.jira.common.BlackDuckJiraLogger;
import com.blackducksoftware.integration.jira.common.BlackDuckPluginDateFormatter;
import com.blackducksoftware.integration.jira.common.model.BlackDuckProjectMapping;
Expand Down Expand Up @@ -133,4 +134,9 @@ public void updateOldMappingsIfNeeded() {
}
}

public void assignUserToBlackDuckProject() {
final BlackDuckAssignUtil blackDuckAssignUtil = new BlackDuckAssignUtil();
blackDuckAssignUtil.assignUserToBlackDuckProject(jiraSettingsAccessor.createPluginErrorAccessor(), jiraSettingsAccessor.createGlobalConfigurationAccessor());
}

}

0 comments on commit a418834

Please sign in to comment.