com.almworks.jira.structure.api.StructurePluginHelper Maven / Gradle / Ivy
Show all versions of structure-api Show documentation
package com.almworks.jira.structure.api;
import com.almworks.integers.*;
import com.almworks.jira.structure.api.auth.StructureAuth;
import com.almworks.jira.structure.api.cache.StructureCacheHelper;
import com.almworks.jira.structure.api.error.StructureError;
import com.almworks.jira.structure.api.permissions.CoreAppPermissions;
import com.almworks.jira.structure.api.permissions.StructureAppPermission;
import com.almworks.jira.structure.api.settings.StructureConfiguration;
import com.almworks.jira.structure.api.structure.Structure;
import com.almworks.jira.structure.api.util.CallableE;
import com.atlassian.annotations.Internal;
import com.atlassian.crowd.embedded.api.Group;
import com.atlassian.jira.bc.issue.search.SearchService;
import com.atlassian.jira.bc.issue.worklog.TimeTrackingConfiguration;
import com.atlassian.jira.issue.*;
import com.atlassian.jira.issue.search.SearchException;
import com.atlassian.jira.issue.search.SearchRequestManager;
import com.atlassian.jira.jql.operand.JqlOperandResolver;
import com.atlassian.jira.jql.parser.JqlParseException;
import com.atlassian.jira.jql.parser.JqlQueryParser;
import com.atlassian.jira.jql.util.JqlStringSupport;
import com.atlassian.jira.permission.GlobalPermissionKey;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.jira.security.*;
import com.atlassian.jira.security.plugin.ProjectPermissionKey;
import com.atlassian.jira.security.roles.ProjectRole;
import com.atlassian.jira.security.roles.ProjectRoleManager;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.user.preferences.UserPreferencesManager;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.util.I18nHelper;
import com.atlassian.jira.util.MessageSet;
import com.atlassian.plugin.PluginAccessor;
import com.atlassian.plugin.event.PluginEventManager;
import com.atlassian.query.Query;
import org.jetbrains.annotations.*;
import java.text.Collator;
import java.util.*;
/**
* {@code StructurePluginHelper} is a helper component that provides a lot of helpful methods and which is extensively
* used by Structure plugin itself and by Structure extensions.
*
* As a consumer of Structure API, you can also benefit from using these methods, however, since this class is
* marked {@code @Internal}, you may need to retest or recompile your code with every new minor or micro release of
* the Structure API.
*
*
* The methods in this class form several groups:
*
*
*
* - Quick access to components,
* - Permission and access checks,
* - Web resource utilities for building pages that include Structure widget,
* - Searching and filtering utilities,
* - I18n tools,
* - Caching and lifecycle tools,
* - Miscellaneous helper methods.
*
*/
@Internal
public interface StructurePluginHelper {
// === Permission and access checks ===
/**
* Checks user access to the issue for viewing or editing. Edit check includes verifying that issue editing is not
* prohibited by workflow.
*
* @param issue issue ID
* @param checkEdit if true, edit permissions are required.
* @param user user to check for
* @return specific error or {@code null} if everything is ok
*/
@Nullable
StructureError getIssueError(@Nullable Long issue, boolean checkEdit, @Nullable ApplicationUser user);
/**
* Convenience method to check issue access for the current user.
*
* @param issue issue ID
* @param checkEdit if true, edit permissions are required.
* @return specific error or {@code null} if everything is ok
* @see #getIssueError(Long, boolean, ApplicationUser)
*/
@Nullable
default StructureError getIssueError(@Nullable Long issue, boolean checkEdit) {
return getIssueError(issue, checkEdit, StructureAuth.getUser());
}
/**
* Checks user access to the issue for viewing or editing. Edit check includes verifying that issue editing is not
* prohibited by workflow. Also can check if the project that the issue belongs to is enabled for Structure.
*
* @param issue issue
* @param checkEdit if true, edit permissions are required
* @param checkProjectEnabledForStructure if true, issue's project must be enabled for Structure
* @param user user to check for
* @return specific error or {@code null} if everything is ok
*/
@Contract("null, _, _, _ -> !null")
@Nullable
StructureError getIssueError(@Nullable Issue issue, boolean checkEdit, boolean checkProjectEnabledForStructure,
@Nullable ApplicationUser user);
/**
* Convenience method to check issue access for the current user.
*
* @param issue issue
* @param checkEdit if true, edit permissions are required
* @return specific error or {@code null} if everything is ok
* @see #getIssueError(Issue, boolean, boolean, ApplicationUser)
*/
@Contract("null, _ -> !null")
@Nullable
default StructureError getIssueError(@Nullable Issue issue, boolean checkEdit) {
return getIssueError(issue, checkEdit, false, StructureAuth.getUser());
}
/**
* Convenience method to check issue access for the current user.
*
* @param issue issue
* @param checkEdit if true, edit permissions are required
* @param checkProjectEnabledForStructure if true, issue's project must be enabled for Structure
* @return specific error or {@code null} if everything is ok
* @see #getIssueError(Issue, boolean, boolean, ApplicationUser)
*/
@Contract("null, _, _ -> !null")
@Nullable
default StructureError getIssueError(@Nullable Issue issue, boolean checkEdit, boolean checkProjectEnabledForStructure) {
return getIssueError(issue, checkEdit, checkProjectEnabledForStructure, StructureAuth.getUser());
}
/**
* Checks if the project is enabled for Structure and the current user can see it.
*
* @param project the project
* @return true if the project is "structured" and visible to the current user
*/
boolean isProjectStructuredForCurrentUser(@Nullable Project project);
/**
* Checks if the given user is allowed to work with Structure add-on.
*
* @return true if the given user can work with Structure
*/
default boolean isStructureAvailableToUser(@Nullable ApplicationUser user) {
return isAllowed(CoreAppPermissions.USE, user);
}
/**
* Checks if the current user is allowed to work with Structure add-on.
*
* @return true if the current user can work with Structure
*/
default boolean isStructureAvailableToCurrentUser() {
return isStructureAvailableToUser(StructureAuth.getUser());
}
/**
* Checks if the given user is allowed to create new structures.
*/
default boolean isCreateStructureAllowed(@Nullable ApplicationUser user) {
return isAllowed(CoreAppPermissions.CREATE_STRUCTURE, user);
}
/**
* Checks if the given user is allowed to create and run synchronizers.
*/
default boolean isSynchronizationAllowed(@Nullable ApplicationUser user) {
return isAllowed(CoreAppPermissions.SYNCHRONIZATION, user);
}
/**
* Checks if the given user is allowed to create and run generators.
*/
default boolean isAutomationAccessAllowed(@Nullable ApplicationUser user) {
return isAllowed(CoreAppPermissions.AUTOMATION, user);
}
/**
* Checks if the given user is allowed to manage global saved columns.
*/
default boolean isManageGlobalSavedColumnsAllowed(@Nullable ApplicationUser user) {
return isAllowed(CoreAppPermissions.MANAGE_GLOBAL_SAVED_COLUMNS, user);
}
/**
* Checks if the given user allowed to perform the action guarded by the given permission.
* @see com.almworks.jira.structure.api.permissions.CoreAppPermissions
*/
boolean isAllowed(@NotNull StructureAppPermission permission, @Nullable ApplicationUser user);
/**
* Checks if the current user has authenticated in the system.
*
* @return true if the current user is not anonymous
*/
default boolean isAuthenticated() {
return getUser() != null;
}
/**
* Checks if the given user is a Jira administrator (but not necessarily "System Administrator"!).
*
* @return true if the given user is an admin.
*/
boolean isAdmin(@Nullable ApplicationUser user);
/**
* Checks if the current user is a Jira administrator (but not necessarily "System Administrator"!).
*
* @return true if the current user is an admin.
*/
default boolean isAdmin() {
return isAdmin(getUser());
}
/**
* Checks if the given user is a Jira system administrator.
*
* @param user the user to check
* @return {@code true} if the given user is not {@code null} and a Jira system administrator.
*/
boolean isSystemAdmin(@Nullable ApplicationUser user);
/**
* Checks if the current user is a Jira system administrator.
*
* @return {@code true} if the current user is a Jira system administrator.
*/
default boolean isSystemAdmin() {
return isSystemAdmin(getUser());
}
/**
* Checks if the given user can create new views.
*/
boolean isViewCreationAllowed(@Nullable ApplicationUser user);
/**
* Checks if the given user can share views.
*/
boolean isViewSharingAllowed(@Nullable ApplicationUser user);
/**
* Checks if the issue can be edited by the given user. Besides permissions, the issue may be non-editable because
* of workflow.
*
* @param issue issue in question
* @param user user to make the edit
* @return true if the user can now edit the issue
*/
boolean isIssueEditable(@Nullable Issue issue, @Nullable ApplicationUser user);
/**
* Checks if the given user has the given global permission.
*
* @param permission global permission
* @param user user in question
* @return true if permission is granted
*/
boolean hasPermission(@NotNull GlobalPermissionKey permission, @Nullable ApplicationUser user);
/**
* Checks if the given user has the given project-level permission on an issue's project.
*
* @param permission project permission from {@link com.atlassian.jira.permission.ProjectPermissions}
* @param issue an issue
* @param user user in question
* @return true if permission is granted
*/
boolean hasPermission(@NotNull ProjectPermissionKey permission, @Nullable Issue issue,
@Nullable ApplicationUser user);
/**
* Checks if the given user has the given project-level permission on a project.
*
* @param permission project permission from {@link com.atlassian.jira.permission.ProjectPermissions}
* @param project a project
* @param user user in question
* @return true if permission is granted
*/
boolean hasPermission(@NotNull ProjectPermissionKey permission, @Nullable Project project,
@Nullable ApplicationUser user);
/**
* Retrieves the current user.
*
* @return current user
* @see StructureAuth
*/
@Nullable
ApplicationUser getUser();
/**
* Retrieves the security groups that the user is allowed to see. JIRA admin is allowed to see all groups.
* All non-admin users are allowed to see the groups they are in.
*
* @param user user
* @return groups that the user is allowed to see
*/
@NotNull
List getAvailableGroups(@Nullable ApplicationUser user);
/**
* Retrieves the security groups that the current user is allowed to see. JIRA admin is allowed to see all groups.
* All non-admin users are allowed to see the groups they are in.
*
* @return groups that the current user is allowed to see
*/
@NotNull
default List getAvailableGroupsForCurrentUser() {
return getAvailableGroups(getUser());
}
/**
* Retrieves the roles that exist in the system. The returned list is owned by the caller and may be modified.
*/
@NotNull
List getAvailableRoles();
/**
* Retrieves the list of projects that are enabled for structure and visible to the current user.
* The returned list is owned by the caller and may be modified.
*/
@NotNull
List getStructureProjectsForCurrentUser();
/**
* Retrieves the list of projects that are visible to the current user.
* The returned list is owned by the caller and may be modified.
* The returned projects may not be enabled for structure.
*/
@NotNull
List getProjectsForCurrentUser();
// === Quick access to JIRA components ===
@NotNull
JiraAuthenticationContext getAuthenticationContext();
@NotNull
IssueManager getIssueManager();
@NotNull
PermissionManager getPermissionManager();
@NotNull
PluginAccessor getPluginAccessor();
@NotNull
PluginEventManager getEventManager();
@NotNull
ProjectManager getProjectManager();
@NotNull
ProjectRoleManager getProjectRoleManager();
@NotNull
UserManager getUserManager();
@NotNull
JqlStringSupport getJqlStringSupport();
@NotNull
JqlQueryParser getJqlQueryParser();
@NotNull
SearchService getSearchService();
@NotNull
JqlOperandResolver getJqlOperandResolver();
@NotNull
SearchRequestManager getSearchRequestManager();
@NotNull
GlobalPermissionManager getGlobalPermissionManager();
@NotNull
TimeTrackingConfiguration getTimeTrackingConfiguration();
@NotNull
UserPreferencesManager getUserPreferencesManager();
@NotNull
CustomFieldManager getCustomFieldManager();
// === Web resource utilities ===
/**
* Requires all resources needed to render Structure Widget.
*/
void requireWidgetResource();
/**
* Requires a resource that may be localized. A localized version of the resource will have _locale suffix in its
* key - for example, com.almworks.jira.structure:my-resource_de
*
* The locale of current user is used to load the resource. If such resource is missing, nothing happens.
*
* @param resourceKey base resource key
*/
void requireLocalizedResource(@NotNull String resourceKey);
/**
* Marks resource as needed for loading. Must be called before a page starts being rendered. If resource is missing,
* JIRA logs a warning but rendering continues.
*
* @param resourceKey resource key
*/
void requireResource(@NotNull String resourceKey);
/**
* Marks resource as needed for loading, if the resource exists. If the resource is missing, nothing happens.
* Must be called before a page starts being rendered.
*
* @param resourceKey resource key
* @see #requireResource(String)
*/
void requireResourceIfPresent(String resourceKey);
/**
* Loads resources needed for the issue details layout.
*/
void requireIssueDetailsResources();
/**
* Loads resources associated with a context
*
* @param context context name
*/
void requireResourcesForContext(String context);
/**
* Loads resources needed for "quick edit" code to work (dialog with editing / creating an issue).
*/
void requireQuickEditResources();
/**
* Loads resources needed for standard JIRA keyboard shortcuts.
*/
void requireIssueShortcuts();
// === Searching and filtering tools ===
/**
* Used to figure out which projects are not visible to the user. The list of project IDs may contain non-existent
* IDs - they will also be reported as invisible.
*
* @param projects list of project IDs
* @param user the user
* @param overrideSecurity if true, permissions don't matter, only project existence is checked
* @param invisibleCollector a collector to get the IDs of invisible projects
*/
void filterInvisibleProjects(@Nullable LongSizedIterable projects, @Nullable ApplicationUser user,
boolean overrideSecurity, @NotNull LongCollector invisibleCollector);
/**
* Runs JQL search.
*
* @param query JQL (empty or null string will result in empty result)
* @return IDs of matching issues
* @throws SearchException if search problem happened
* @throws JqlParseException if JQL is invalid
*/
@NotNull
LongArray searchQuery(@Nullable String query) throws SearchException, JqlParseException;
/**
* Runs search.
*
* @param query query
* @return IDs of matching issues
* @throws SearchException if search problem happened
*/
@NotNull
LongArray searchQuery(@Nullable Query query) throws SearchException;
/**
* Runs JQL search with sorting.
*
* @param query JQL (empty or null string will result in empty result)
* @return IDs of matching issues
* @throws SearchException if search problem happened
* @throws JqlParseException if JQL is invalid
*/
@NotNull
LongArray searchAndSortQuery(@Nullable String query) throws SearchException, JqlParseException;
/**
* Runs search with sorting.
*
* @param query query
* @return IDs of matching issues
* @throws SearchException if search problem happened
*/
@NotNull
LongArray searchAndSortQuery(@Nullable Query query) throws SearchException;
/**
* Runs search with sorting and result count limit.
*
* @param query query
* @param limit maximum number of issues in the result
* @return IDs of matching issues
* @throws SearchException if search problem happened
*/
@NotNull
LongArray searchAndSortQuery(@Nullable Query query, int limit) throws SearchException;
/**
*
* Passes the issues through JIRA search engine and lets the caller collect either all matching or non-matching
* issues.
*
* The implementation sorts issues and splits them up into chunks (of 100+ ids each) and creates
* a JQL query for each.
* This has proven to be a quick method of checking. However, if the number of issues is likely to be the same order
* of magnitude as the full result of the query, it's better to run the full query and compare result with the list.
*
* The issues are checked to be accessible for the current user - so no need to run additional BROWSE permission checks.
* You can quickly check which issues among the list are visible to the user by running matchIssues() with
* null
query.
*
* If it is certain that the issue list is sorted, it's more efficient to call {@link StructurePluginHelper#matchIssuesSorted}.
* If you need to skip checking for the user access or check access for non-current user,
* use {@link com.almworks.jira.structure.api.auth.StructureAuth#sudo)}.
*
* @param query query to check against. If null, the issues are only checked to be visible to the user and to the
* Structure plugin
* @param issues issue IDs
* @param collectMatching if true, collector will receive the issues that match query and visible to the user;
* if false, collector will receive non-matching issues
* @param collector an instance for receiving the results. Chunks are processed in order, for each chunk the
* (non-)matching issues are added. No guarantees are made for the order of the results inside the same chunk.
* @throws com.atlassian.jira.issue.search.SearchException if a bad thing happens
*/
void matchIssues(@Nullable LongList issues, @Nullable Query query, boolean collectMatching,
@NotNull LongCollector collector) throws SearchException;
/**
* Passes the issues, sorted by their IDs, through JIRA search engine to collect matching or non-matching issues.
*
* This is more efficient method than {@link #matchIssues} if you already have IDs sorted.
*
* @param issuesSorted issues list
* @param query additional query
* @param collectMatching if true, collector receives matching issues, if false, collector receives non-matching issues.
* @param collector the collector
* @throws SearchException if a bad thing happens
* @see #matchIssues(LongList, Query, boolean, LongCollector)
*/
void matchIssuesSorted(@Nullable LongList issuesSorted, @Nullable Query query, boolean collectMatching,
LongCollector collector) throws SearchException;
/**
* A variation of {@link #matchIssuesSorted(LongList, Query, boolean, LongCollector)} that lets you specify
* the user and override security checks.
*
* @param issuesSorted issues list
* @param query additional query
* @param collectMatching if true, collector receives matching issues, if false, collector receives non-matching issues.
* @param user the user that will be used to check access
* @param overrideSecurity if true, user access will not be checked
* @param collector the collector
* @throws SearchException if a bad thing happens
* @see #matchIssues(LongList, Query, boolean, LongCollector)
* @see StructureAuth#sudo(ApplicationUser, boolean, CallableE)
*/
void matchIssuesSorted(@Nullable LongList issuesSorted, @Nullable Query query, boolean collectMatching,
@Nullable ApplicationUser user, boolean overrideSecurity, LongCollector collector) throws SearchException;
/**
* Checks if the query is valid and that the user has access to all the things mentioned in the query.
*
* @param user user
* @param query query
* @return a collection of error messages
*/
@NotNull
MessageSet validateQuery(ApplicationUser user, Query query);
/**
* Retrieves a query that limits scope to the projects enabled for Structure. You can use that query to combine with
* whatever business logic query you have.
*
* @return configuration query ("project in ...") or {@code null} if Structure is allowed for all projects
* @see StructureConfiguration#getConfigurationScopeQuery()
*/
@Nullable
default Query getConfigurationScopeQuery() {
return getConfiguration().getConfigurationScopeQuery();
}
// === I18n tools ===
/**
* Retrieves i18n helper for a concrete user.
*
* @param user the user
* @return i18n helper
*/
@NotNull
I18nHelper getI18n(@Nullable ApplicationUser user);
/**
* Retrieves i18n helper for the current user.
*/
@NotNull
default I18nHelper getI18n() {
return getI18n(getUser());
}
/**
* Returns comparator for sorting structures by name, according to the given user's locale.
*
* @param user the user to take locale from
* @return a comparator that can be used to sort structures
*/
@NotNull
Comparator getStructureComparator(@Nullable ApplicationUser user);
/**
* Returns a collator for strings in the user's locale.
*
* @param user the user
* @return collator
*/
@NotNull
Collator getCollator(@Nullable ApplicationUser user);
/**
* Returns the current user's locale.
*/
@NotNull
Locale getLocale();
// === Caching and lifecycle tools ===
StructureCacheHelper getCacheHelper();
/**
* Returns true when Structure cannot be used because it is locked, either for full restore or for system startup.
*/
boolean isStructureLocked();
// === Miscellaneous tools ===
/**
* Retrieves an instance of Issue
.
*
* @param issueId the ID of the issue
* @return the issue, or null if the issue cannot be found or there is an exception getting it
*/
@Nullable
Issue getIssue(long issueId);
/**
* Retrieves an instance of issue by issue key.
*
* @param key issue key
* @return the issue, or null if the issue cannot be found or there is an exception getting it
*/
@Nullable
Issue getIssue(String key);
/**
* Retrieves {@code StructureConfiguration}
*/
@NotNull
StructureConfiguration getConfiguration();
/**
* Creates a new instance of the given class, injecting all dependencies into the constructor. Those dependencies may
* include other Structure's services or system services.
*
* @param clazz class to be instantiated
* @param type parameter
* @return created instance
*/
T instantiate(@NotNull Class clazz);
}