Edit validators are very helpfull to verify that all issue data is consistent. While validation based on a single field is simple in Jira (it is done by the field type itself), it becomes difficult when valid field values depend on other field content. Edit validators help in those, but also in other cases as they allow to validate the content of multiple fields.
Examples for edit validators are:
Existing approaches
There are several approaches to implement edit validators, e.g.
It would be nice to have edit validators too. But existing solutions have some disadvantages:
Our requirements
So we decided to implement our own solution to fulfill the following requirements:
Our solution
Note that the code is extracted from real source without compilation and tests...
Please note also that we are using Ant as build tool. This means the Ant variables in descriptors are replaced during the build process in our environment.
Utility plugin
For our solution, we need an interface to implement as well as an utility class to get validator instances. Both reside in a general utility plugin. Other users might add this class to the plugin which is explained in the next step.
Please make sure the code resides in a version-2-plugin and is exported so it can be used by another version-2-plugin. In our environment, we use a descriptor very similar to this:
<atlassian-plugin key="${plugin.groupid}.${plugin.artifactid}" name="${plugin.name}" plugins-version="2"> <plugin-info> <description>${plugin.description}</description> <version>${plugin.version}</version> <vendor name="INTERSHOP Communications AG" url="http://www.intershop.de"/> <bundle-instructions> <Export-Package> com.intershop* </Export-Package> </bundle-instructions> </plugin-info> </atlassian-plugin>
Utility plugin - Validator interface
We need an interface which has to be implemented by all operation validators.
The interface looks like this:
package com.intershop.jira.operation.util; /** * Interface for operation validators. */ public interface OperationValidator { /** * Validates the values in the field values holder for this operation, user and issue. * Returns a collection of errors or null in case everything is fine. * @param user * @param issue * @param mapFieldValuesHolder * @return */ public ErrorCollection isValid(User user, Issue issue, Map<String, Object> mapFieldValuesHolder); }
Utility plugin - Validator utility
The validator utility class helps to make it easier to call all related operation validators later.
In our environment, we create validator instances via reflections. We parse our Jira configuration file for validator class names to execute. The configuration is stored into a property file and automatically reloaded if needed. The properties are then converted into a Java data structure, which allows for fast access. In the end, this allows us to quickly find the list of validator class names for an issue type and status. Since the file is automatically reloaded, changes can be done very easily. Configuration files can also be added to a VCS like SVN and deployed to several instances. The list of validators is created via reflections from the resulting list of class names.
The reason for using reflections to create instances is that validators can be added and changed at runtime. Together with reloaded configuration data, the list of validators to execute for an issue type and status can be adjusted very easily.
Other users might prefer to load all classes that implement the OperationValidator interface. They could then ask each instance whether the itis a validator for an issue of this type and status. Or, even simpler, add parameters to the existing isValid method accordingly so this method verifies the issue type and status.
Back to our solution: the utility class provides two methods:
/** * Base calls for operation validators. */ public abstract class OperationValidatorUtils { private static final PluginAccessor pluginAccessor = ComponentAccessor.getPluginAccessor(); /** * Creates validator instances from all validator classes configured in jira.properties for an operation. * @param strSection * @param issue * @return * @throws ClassNotFoundException * @throws NoSuchMethodException * @throws SecurityException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws IllegalArgumentException */ protected static List<OperationValidator> getValidators(String strSection, Issue issue) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { List<OperationValidator> listValidators = new ArrayList<OperationValidator>(); Plugin plugin = pluginAccessor.getPlugin("YOUR PLUGIN KEY"); //TODO: Define here the ID of the plugin that contains the //validator implementation (it is the ID of the next plugin) if ( ( null == plugin ) || ( ! plugin.isEnabled() ) ) //Don't know how to avoid deprecated code return listValidators; @SuppressWarnings("unchecked") List<Map<String, Object>> listValidatorData;//TODO: Define how to get the list of validators here. A map that contains at least the classname is assumed if ( null != listValidatorData) { for ( Map<String, Object> mapValidatorData : listValidatorData) { String strClassName = (String)mapValidatorData.get("classname"); @SuppressWarnings("rawtypes") Class clazz = Class.forName(strClassName, true, plugin.getClassLoader()); @SuppressWarnings("rawtypes") Class arParameterTypes[] = new Class[0]; @SuppressWarnings("rawtypes") Constructor constructor = clazz.getConstructor(arParameterTypes); Object arArguments[] = new Object[0]; Object objInstance = constructor.newInstance(arArguments); listValidators.add((OperationValidator)objInstance); } } return listValidators; } /** * Validates the values in the field values holder for this operation, user and issue against all configured validators. * Returns a list of problems or null in case everything is fine. * @param strSection * @param issue * @param user * @param mapFieldValuesHolder * @return * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException * @throws NoSuchMethodException * @throws ClassNotFoundException * @throws IllegalArgumentException * @throws SecurityException */ public static ErrorCollection isValid(String strSection, Issue issue, User user, Map<String, Object> mapFieldValuesHolder) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { List<OperationValidator> listValidator = getValidators(strSection, issue); for ( OperationValidator validator : listValidator ) { ErrorCollection errorCollection = validator.isValid(user, issue, mapFieldValuesHolder); if ( ( null != errorCollection ) && ( errorCollection.hasAnyErrors() ) ) { return errorCollection; } } return null; } }
Validator plugin
We introduced a new plugin named jira-issue-operation-workflow. It is a version-2 plugin and can therefore be easily updated without having to restart Jira. It exports all its content.
The plugin descriptor looks like this:
<atlassian-plugin key="${plugin.groupid}.${plugin.artifactid}" name="${plugin.name}" plugins-version="2"> <plugin-info> <description>${plugin.description}</description> <version>${plugin.version}</version> <vendor name="INTERSHOP Communications AG" url="http://www.intershop.de"/> <bundle-instructions> <Export-Package> com.intershop* </Export-Package> </bundle-instructions> </plugin-info> </atlassian-plugin>
Validator plugin - classes
The plugin contains currently only one example validator. The validator could verify that value for affects version is an earlier version than that for fix versions. It looks like the following example:
public class VersionAndBuildNumberEditOperationValidator implements OperationValidator { private final Logger logger = Logger.getLogger(VersionAndBuildNumberEditOperationValidator.class); public ErrorCollection isValid(User user, Issue issue, Map<String, Object> mapFieldValuesHolder) { ErrorCollection errorCollection = new SimpleErrorCollection(); //TODO: Your validation code if ( errorCollection.hasAnyErrors() ) { return errorCollection; } return null; } }
Edit action
After introducing all relevant classes, we have to make sure the validators are called when an issue is edited. To support this, we provide a plugin which overwrites the edit action. This plugin must also be a version-2 plugin. The descriptor starts very similar to the descriptors mentioned above. Later on, it contains a section related to the Edit operation:
<resource type="i18n" name="${plugin.name} - Edit issue - i18n" location="com.intershop.jira.web.action.issue.edit.EditIssue"/> <webwork1 key="${plugin.artifactid}-editissue.webwork" name="${plugin.name} - Edit Issue Webwork Module"> <actions> <action name="com.intershop.jira.web.action.issue.edit.EditIssue" alias="EditIssue"> <view name="error">/secure/views/issue/editissue.jsp</view> <view name="input">/secure/views/issue/editissue.jsp</view> <view name="issue-permission-error">/issue-permission-error.jsp</view> </action> </actions> </webwork1>
This code has been copied from atlassian-jira\WEB-INF\classes\actions.xml. Note that we do not introduce a new web item. We only replace the edit action, to make sure the changes also work for Greenhopper. The Ant variables are replaced with respective strings by our build process.
Edit action - the edit action class
The edit action is used only for the normal edit operation, but not by the quick edit operation. The implementation extends the normal edit action and basically only overwrites doValidation().
package com.intershop.jira.web.action.issue.edit; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.Map; import org.apache.log4j.Logger; import com.atlassian.jira.bc.issue.IssueService; import com.atlassian.jira.bc.issue.comment.CommentService; import com.atlassian.jira.config.ConstantsManager; import com.atlassian.jira.config.SubTaskManager; import com.atlassian.jira.issue.Issue; import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager; import com.atlassian.jira.issue.fields.screen.FieldScreenRendererFactory; import com.atlassian.jira.user.util.UserUtil; import com.atlassian.jira.util.ErrorCollection; import com.atlassian.jira.util.SimpleErrorCollection; import com.atlassian.jira.workflow.WorkflowManager; import com.intershop.jira.operation.util.OperationValidatorUtils; /** * Edit operation. * Overwritten to be able to support general validators in addition to field-based validators. */ @SuppressWarnings("serial") public class EditIssue extends com.atlassian.jira.web.action.issue.EditIssue { private static final Logger logger = Logger.getLogger(EditIssue.class); private Map<String, Object> mapFieldValuesHolder = null; public EditIssue(SubTaskManager subTaskManager, ConstantsManager constantsManager, FieldLayoutManager fieldLayoutManager, WorkflowManager workflowManager, FieldScreenRendererFactory fieldScreenRendererFactory, CommentService commentService, IssueService issueService, UserUtil userUtil) { super(subTaskManager, constantsManager, fieldLayoutManager, workflowManager, fieldScreenRendererFactory, commentService, issueService, userUtil); } /** * This method is overwritten to get the field values to validate during quick edit. */ @Override public void setFieldValuesHolder(@SuppressWarnings("rawtypes") final Map mapFieldValuesHolder) { super.setFieldValuesHolder(mapFieldValuesHolder); this.mapFieldValuesHolder = Collections.unmodifiableMap(mapFieldValuesHolder); } @Override protected void doValidation() { super.doValidation(); if ( ! hasAnyErrors()) { final Issue issue = getIssueObject(); ErrorCollection errorCollection; try { errorCollection = OperationValidatorUtils.isValid("editissue", issue, getLoggedInUser(), mapFieldValuesHolder); } catch (Exception e) { logger.error(e.getMessage(), e); errorCollection = new SimpleErrorCollection(); errorCollection.addErrorMessage(e.getMessage()); } if ( ( null != errorCollection ) && ( errorCollection.hasAnyErrors() ) ) { addErrorCollection(errorCollection); } } } }
When there are no problems from super's validation, the operation validator utility class is used to execute all validations. We use the first argument ("editissue") here to find the configuration mentioned previously. Exceptions are converted to errors.
As one can see overwriting a plain Jira action is simple. Thank you, Atlassian.
Quick Edit action
Quick edit support is much more difficult. There is no action which can easily be replaced in an own plugin. In our environment, we had a lot of trouble if either
So we decided to replace the original quick edit plugin - sorry, Atlassian.
Because this there is another version-2 plugin, which is named exactly as the original plugin (including version number).
Please note that we're using the original plugin descriptor - see later in this section.
Quick Edit action - The action class
The quick edit action class is very similar to the edit action.
package com.intershop.jira.quickedit.action; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.Map; import org.apache.commons.httpclient.HttpStatus; import org.apache.log4j.Logger; import webwork.action.ActionContext; import com.atlassian.jira.bc.issue.IssueService; import com.atlassian.jira.issue.Issue; import com.atlassian.jira.issue.fields.rest.FieldHtmlFactory; import com.atlassian.jira.quickedit.user.UserPreferencesStore; import com.atlassian.jira.user.UserIssueHistoryManager; import com.atlassian.jira.util.ErrorCollection; import com.atlassian.jira.util.SimpleErrorCollection; import com.atlassian.plugins.rest.common.json.DefaultJaxbJsonMarshaller; import com.atlassian.plugins.rest.common.json.JaxbJsonMarshaller; import com.intershop.jira.operation.util.OperationValidatorUtils; /** * This class extends the standard quick edit operation by own validation features. * Note that the class is only used because the original quick edit plugin atlassian-plugin.xml file is patched. */ @SuppressWarnings("serial") public class QuickEditIssue extends com.atlassian.jira.quickedit.action.QuickEditIssue { private static final Logger logger = Logger.getLogger(QuickEditIssue.class); private final IssueService issueService; private Map<String, Object> mapFieldValuesHolder = null; private ErrorCollection errorCollection = null; public QuickEditIssue(IssueService issueService, UserPreferencesStore userPreferencesStore, UserIssueHistoryManager userIssueHistoryManager, FieldHtmlFactory fieldHtmlFactory) { super(issueService, userPreferencesStore, userIssueHistoryManager, fieldHtmlFactory); this.issueService = issueService; } /** * This method is overwritten to get the field values to validate during quick edit. */ @Override public void setFieldValuesHolder(final Map<String, Object> mapFieldValuesHolder) { super.setFieldValuesHolder(mapFieldValuesHolder); this.mapFieldValuesHolder = Collections.unmodifiableMap(mapFieldValuesHolder); } /** * Overwrite this method so our error collection is returned if needed. */ @Override public String getErrorJson() { String strJson = null; if ( null == errorCollection ) { strJson = super.getErrorJson(); } else { final JaxbJsonMarshaller marshaller = new DefaultJaxbJsonMarshaller(); strJson = marshaller.marshal(com.atlassian.jira.rest.api.util.ErrorCollection.of(errorCollection)); } return strJson; } @Override protected void doValidation() { super.doValidation(); if ( ! hasAnyErrors()) { final IssueService.IssueResult result = issueService.getIssue(getLoggedInUser(), getIssueId()); final Issue issue = result.getIssue(); ErrorCollection errorCollection = null; try { errorCollection = OperationValidatorUtils.isValid("editissue", issue, getLoggedInUser(), mapFieldValuesHolder); } catch (Exception e) { logger.error(e.getMessage(), e); errorCollection = new SimpleErrorCollection(); errorCollection.addErrorMessage(e.getMessage()); } if ( ( null != errorCollection ) && ( errorCollection.hasAnyErrors() ) ) { this.errorCollection = new SimpleErrorCollection(); this.errorCollection.addErrorCollection(errorCollection); addErrorCollection(this.errorCollection); ActionContext.getResponse().setStatus(HttpStatus.SC_BAD_REQUEST); } } } }
Quick edit action - patched plugin
While build scripts are generally shared between all plugins, a plugin-specific build script for quick edit is used to patch the original plugin and adjust its content.
In addition to adding our overwritten action class, three things have to be changed in the plugin descriptor. In our environment, this happens using Ant (I guess it is also simple via Maven):
<unjar dest="${build.target}/${component.pathname}/${plugin.pathname}/target" overwrite="true"> <fileset dir="../static/lib" includes="jira-quick-edit-plugin-*.jar"> </fileset> <patternset> <include name="**/*"/> </patternset> </unjar>
<replaceregexp file="${build.target}/${component.pathname}/${plugin.pathname}/target/atlassian-plugin.xml" flags="s" match="^(.*?&lt;description&gt;.*?)(&lt;/description&gt;.*?)$" replace="\1 - Patched by Intershop to support edit validator.\2" byline="false"> </replaceregexp>
<replaceregexp file="${build.target}/${component.pathname}/${plugin.pathname}/target/atlassian-plugin.xml" flags="s" match="^(.*?)com.atlassian.jira.quickedit.action.QuickEditIssue(.*?)$" replace="\1com.intershop.jira.quickedit.action.QuickEditIssue\2" byline="false"> </replaceregexp>
Finally the content is stored into a jar with exactly the same name as the original quick edit plugin.
When this patched plugin version is uploaded into Jira, the original version is automatically disabled - despite being a system plugin. This helps to avoid trouble with bundled-plugins.zip, which is extracted each time Jira is restarted.
SOAP
After providing a solution for Edit and Quick Edit, SOAP issue updates should be validated as well. It is important to replace the original SOAP service by a custom service so that all 3rd party tools are using the new version.
We already extended SOAP by some methods, as providing information about SVN changes attached to an issue. But describing each step we did is beyond the focus of this article. As an introduction https://developer.atlassian.com/display/JIRADEV/Creating+a+JIRA+SOAP+Client can be used. In the end we have the following section in the plugin descriptor of our custom SOAP plugin:
<component key="rpcIssueService" name="Issue Service" class="com.intershop.jira.rpc.soap.service.IssueServiceImpl"> <interface>com.intershop.jira.rpc.soap.service.IssueService</interface> </component>
In addition to other methods not related to this article, the issue service implementation contains the following method to validate input.
/** * Before updating the issue, this method calls edit validators. */ @Override public RemoteIssue updateIssue(User user, String strIssueKey, Map mapActionParams) throws RemoteException { ErrorCollection errorCollection = null; final IssueResult issueResult = issueService.getIssue(user, strIssueKey); try { errorCollection = OperationValidatorUtils.isValid("editissue", issueResult.getIssue(), user, mapActionParams); } catch (Exception e) { throw new RemoteException(e); }
if ( ( null != errorCollection ) && ( errorCollection.hasAnyErrors() ) ) { StringBuilder sb = new StringBuilder(); if ( null != errorCollection.getErrors() ) { for ( String strKey : errorCollection.getErrors().keySet()) { if (0 < sb.length()) sb.append("\n"); sb.append(strKey + ": " + errorCollection.getErrors().get(strKey)); } } if ( null != errorCollection.getErrorMessages() ) { for ( String strMessage : errorCollection.getErrorMessages()) { if (0 < sb.length()) sb.append("\n"); sb.append(strMessage); } } throw new RemoteException(sb.toString()); } return super.updateIssue(user, strIssueKey, mapActionParams); }
This code calls the validator in the same manner as for Edit and Quick Edit.
Inline Edit
Inline editing is done via REST services. See next topic.
We plan to switch off inline editing completely (both for Jira and Greenhopper - note that Greenhopper does not support this at the moment). This is because permissions to edit an issue in our environment also depend on issue type, status and role. Atlassians current approach to configure those kind of permissions (see http://docs.atlassian.com/software/jira/docs/api/latest/com/atlassian/jira/security/WorkflowBasedPermissionManager.html) is not very convenient. It requires to deal with role IDs etc. We plan to provide an own security type, but for now inline editing will be switched off and is therefore not supported by our solution.
REST services
REST services are implemented in different classes. Without inspecting it in detail, it seems that Greenhopper uses an own REST implementation e.g. to update issues. As there is not only one service to handle, adding validators seems to be non-trivial and is currently not supported by our solution.
Bulk Edit
Not supported yet. Sorry.
Summary
As one can see with AUI and Greenhopper things are starting to become more complex. In addition to Edit, there is also Quick Edit and Inline Edit. Issues can also be updated via SOAP and REST.
As described our solution works for Edit, Quick Edit and SOAP. It seems to be simple to also do this for e.g. link actions. And since it's possible to overwrite the doExecute method in an action, one could also implement post functions.
The list of validators is (at least in our environment) issue type- and status-dependent. The validation happens on the server side so it cannot be hacked. Only Java is used. Updates of validators are possible since the plugins are all version-2 plugins, which can be reloaded. The related configuration can also be reloaded at runtime.
There are still some open topics: one system plugin was patched (Quick Edit).
A much better way for such kind of customisation would be a hook concept (or something similar) in the issue service implementation. While our solution requires overwriting several actions (and even then it is incomplete due to missing Inline Edit, REST and Bulk Edit support), it would be much more efficient to trigger data validation on the issue service's update method. Registering a custom issue service with according enhancements would allow to provide a fully integrated operation validation concept. But replacing a core service is not possible/recommended at the moment. Or does anybody know how to do this using only public parts of the Jira API?
Or, as another approach, why is it impossible to configure conditions, validators and post functions for operations like Edit? From the users point of view, operations and transitions are exactly the same, except that transitions change the issue status. So why is it impossible to handle both the same way and configure validators for Edit? Those validators could be executed too if an issue is updated via REST or Bulk Edit.
Despite this, hopefully this article will help someone when discussing validator concepts for issue editing in Jira.
Community moderators have prevented the ability to post new answers.
Interesting discussion. It sounds like you have implemented it pretty much the same as JSS. The same method of using edit functions is also in the script runner, I completed it several months ago but have not got round to writing the docs so not released yet. In the same way you did, when my plugin starts it installs a patched version of the quick edit plugin. I did this mostly as a fallback in case I had to abandon the behaviours plugin.
I had not done anything for soap, but REST is the way forward.
You mention bulk edit is not supported - this is quite a hole. How do you intend to deal with this?
Seems clear to me that there is a big gap in jira's API. You should be able to easily hook into the issue service to validate edits, rather than having to patch or override various system plugins. What's worse is the various methods of editing an issue are not consistent, they use different APIs, eg greenhopper, quick edit, bulk edit etc, which means you end up patching much more than you should have to (which would be nothing ideally). The method you have chosen is the only thing that could work currently, but is far from ideal. Changes need to be merged into the quick edit plugin every release, and just generally seems flaky. But that is not a criticism of your approach, as I say, Atlassian need to deal with this imho.
Thanks. It seems that we did not go into the completely wrong direction.
So we ended both with the same topic: the API (in this case IssueService) should be extended. You perfectly summarized the reasons. Any ideas how to get Atlassian to extend the API? What does Atlassian say?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I don't know... it's difficult to get things like this across to them through their jira instance. Needs someone who has some kind of relationship with the decision makers on the jira side. They may read this stuff though.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
For me, validators look much like an aspect on according service methods. So what's about provide validators as annotation to insert an aspect, similar to ideas in http://stackoverflow.com/questions/4829088/java-aspect-oriented-programming-with-annotations?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
Thank you very much for writing this article!
I'm following this to write my own validator, btw I'm getting some compile errors:
[ERROR] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Compilation failure F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[21,42] cannot find symbol symbol : class EditIssue location: package com.atlassian.jira.web.action.issue F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[29,47] cannot find symbol symbol : class EditIssue location: package com.atlassian.jira.web.action.issue F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[54,8] cannot find symbol symbol : variable super location: class com.xxx.yyy.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[51,4] method does not override or implement a method from a supertype F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[61,8] cannot find symbol symbol : variable super location: class com.xxx.yyy.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[62,15] cannot find symbol symbol : method hasAnyErrors() location: class com.xxx.yyy.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\xxx\yyy\jiraplugins\EditIssu e.java:[64,32] cannot find symbol symbol : method getIssueObject() location: class com.xxx.yyy.jiraplugins.EditIssue
I could solve most of the other dependancies except the above one.
FYI, I'm using atlassian sdk 4.0 and for the JIRA 5.1.5 version plugin.
Could you please help me on this?
Thanks a million in advance!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
Thank you very much for writing this article!
I'm following this to write my own validator, btw I'm getting some compile errors:
[ERROR] BUILD FAILURE [INFO] ------------------------------------------------------------------------ [INFO] Compilation failure F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[21,42] cannot find symbol symbol : class EditIssue location: package com.atlassian.jira.web.action.issue F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[29,47] cannot find symbol symbol : class EditIssue location: package com.atlassian.jira.web.action.issue F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[54,8] cannot find symbol symbol : variable super location: class com.teradata.engtools.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[51,4] method does not override or implement a method from a supertype F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[61,8] cannot find symbol symbol : variable super location: class com.teradata.engtools.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[62,15] cannot find symbol symbol : method hasAnyErrors() location: class com.teradata.engtools.jiraplugins.EditIssue F:\Edit1\editvalidation\src\main\java\com\teradata\engtools\jiraplugins\EditIssu e.java:[64,32] cannot find symbol symbol : method getIssueObject() location: class com.teradata.engtools.jiraplugins.EditIssue
I could solve most of the other dependancies except the above one.
FYI, I'm using atlassian sdk 4.0 and for the JIRA 5.1.5 version plugin.
Could you please help me on this?
Thanks a million in advance!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Did this solution still work in JIRA 7? I tried to extend the edit validation in JIRA 7.3.1 and it doesn't seem be to working anymore.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Were you able to do this with JIRA 7? I am currently trying this for JIRA 7.6 with no success unfortunately...
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
public void addJsonErrorMessages(String messages[]) { String arr$[] = messages; int len$ = arr$.length; for(int i$ = 0; i$ < len$; i$++) { String message = arr$[i$]; addErrorMessage(message); System.out.println(message); } errors = ErrorCollection.of(this); if(getLoggedInUser() == null) ActionContext.getResponse().setStatus(401); else ActionContext.getResponse().setStatus(400); }/* this code is influenced from https://answers.atlassian.com/questions/154953/adderrormessage-throws-exception-from-extended-quickcreateissue-action-in-jira-5*/
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
protected void doValidation() { super.doValidation(); ... /* whenever we are required to place validation error, we place the following type of statements*/ addError("components","Select Only One Component"); addJsonErrorMessages(new String[0]); ...
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
/* The following statements are added in the doValiation() after validation code ends*/ Map<String,String> maperr= getErrors(); Iterator it=maperr.entrySet().iterator(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry)it.next(); System.out.println(pairs.getKey() + " = " + pairs.getValue()+"type"); temp.addError(pairs.getKey().toString(), pairs.getValue().toString()); } temp2=ErrorCollection.of(temp); if (temp2.hasAnyErrors()==false ) { System.out.println(issueInputParameters.onlyValidatePresentFieldsWhenRetainingExistingValues()); System.out.println(issueInputParameters.retainExistingValuesWhenParameterNotProvided()); System.out.println(super.isSingleFieldEdit()); System.out.println(issueInputParameters.onlyValidatePresentFieldsWhenRetainingExistingValues()); System.out.println(issueInputParameters.getSummary()); updateValidationResult = issueService.validateUpdate(getLoggedInUser(), missue.getId(), issueInputParameters); System.out.println(updateValidationResult.isValid()+"Has"); setFieldValuesHolder(updateValidationResult.getFieldValuesHolder()); IssueResult updateResult=null; System.out.println("UVR"); updateResult = issueService.update(getLoggedInUser(),updateValidationResult); MutableIssue updatedIssue = updateResult.getIssue(); populateIssueFields(updatedIssue, false, temp2);//Very IMP to make it false System.out.println(updatedIssue.getSummary()); } else{ populateIssueFields(missue, true, temp2); }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Continuation in page2
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
private void populateIssueFields(Issue issue, boolean retainValues, ErrorCollection errorCollection) { long start = System.nanoTime(); List editFields; if(issueService.isEditable(issue, getLoggedInUser())) editFields = this.fieldHtmlFactory.getEditFields(getLoggedInUser(), this, this, issue, retainValues); else editFields = Collections.emptyList(); long fieldEnd = System.nanoTime(); OpsbarBean opsbarBean = (new OpsbarBeanBuilder(issue, getApplicationProperties(), simpleLinkManager, authContext, issueManager, pluginAccessor)).build(); IssueBean issueBean = new IssueBean(issue, issue.getProjectObject(), issue.getStatusObject(), opsbarBean); long issueEnd = System.nanoTime(); IssueWebPanelsBean panels = this.webPanelMapperUtil.create(issue, this); //panels.getInfoPanels(); //panels.getRightPanels(); long panelsEnd = System.nanoTime(); this.eventPublisher.publish(new IssueRenderTime(fieldEnd - start, issueEnd - fieldEnd, panelsEnd - issueEnd)); this.fields = new IssueFields(editFields, getXsrfToken(), errorCollection, issueBean, panels); } public String getJson() { if(this.fields !=null) { System.out.println("\nTest our logic in getJson()\n"); JaxbJsonMarshaller marshaller = new DefaultJaxbJsonMarshaller(); return marshaller.marshal(this.fields); } else { System.out.println("\nTest super class logic in getJson()\n"); return super.getJson(); } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
This code is required inorder to render the issue panels correctly in view-issue-page
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
hello Uwe Voellger,
you need to unjar jira-issue-nav-plugin-*.jar and try to extend IssueAction class.
public class IssueAction extends com.atlassian.jira.plugin.issuenav.action.IssueAction { private final UserIssueHistoryManager userIssueHistoryManager; private final WebPanelMapperUtil webPanelMapperUtil; private final PluginAccessor pluginAccessor; private final JiraAuthenticationContext authContext; private final IssueManager issueManager; private final SimpleLinkManager simpleLinkManager; private final EventPublisher eventPublisher; //private final FieldHtmlFactory fieldHtmlFactory; private IssueFields fields; private ErrorCollection temp2; private ErrorCollection errors; com.atlassian.jira.util.ErrorCollection temp =new SimpleErrorCollection(); private com.atlassian.jira.bc.issue.IssueService.UpdateValidationResult updateValidationResult; public IssueAction(IssueService issueService, UserIssueHistoryManager userIssueHistoryManager, FieldHtmlFactory fieldHtmlFactory, WebPanelMapperUtil webPanelMapperUtil, PluginAccessor pluginAccessor, JiraAuthenticationContext authContext, IssueManager issueManager, SimpleLinkManager simpleLinkManager, EventPublisher eventPublisher) { super(issueService, userIssueHistoryManager, fieldHtmlFactory, webPanelMapperUtil, pluginAccessor, authContext, issueManager, simpleLinkManager, eventPublisher); //this.fieldHtmlFactory=fieldHtmlFactory; this.userIssueHistoryManager = userIssueHistoryManager; this.webPanelMapperUtil = webPanelMapperUtil; this.pluginAccessor = pluginAccessor; this.authContext = authContext; this.issueManager = issueManager; this.simpleLinkManager = simpleLinkManager; this.eventPublisher = eventPublisher; } IssueService issueService=ComponentAccessor.getIssueService();
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
please note that it is no longer possible to switch off inline edit in Jira 6 so the disussed solution proposal finally does not help to guarantee consistent data.
Sorry, but I have no workaround for that.
Uwe
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
but using your solution for quick edit, I tried to implement for inline edit. It is working good for Jira 6.x version also.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
did you successfully implement a validator support that runs during inline edit? Could you share your solution here for that? I thought this is nearly impossible...
Thanks a lot,
Uwe
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
It's seems like that approach did't work on Jira 5.2.10
Uwe, do you have any ideas how to make it work on Jira 5.2.10?
Thanks!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
we are still on 5.2.8. There are no (known) issues with this version. You might mention problem details...
Migration of our Jira instance to latest Jira release takes some time...
Sorry.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
as already mentioned we do not use exactly the same sources as mentioned in this article. Because this you might get some compile problems. Basically it should be simple to solve these problems.
We do not use atlassian sdk, but I guess it also provides the classpath valid for compilation of a plugin. If so, the problem you mentioned might be related to missing plugin imports of your version-2-plugin that contains the edit operation. Try to specify them:
<bundle-instructions> <Import-Package> com.atlassian*, </Import-Package> </bundle-instructions>
Uwe
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Uwe/Jamie,
Thanks, it is compiling now, but I did not understand what to write/add at: .
List<Map<String, Object>> listValidatorData;//TODO: Define how to get the list of validators here. A map that contains at least the classname is assumed
I will be very happy if you can send me a sample plugin code(working). I'm in big need of this edit validation plugin.
And I'm waiting for Script Runner v 2.1 which has this implementation. Could you please let me know when it is releasing. We are waiting for JSS next version or Script Runner 2.1 for JIRA 5.1.4.
Thanks you very much in advance!
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hello,
editissue.task.status.open.validator.0.classname=com.intershop.jira.operation.validator.FieldHasSingleSelectionEditOperationValidator editissue.task.status.open.validator.1.classname=com.intershop.jira.operation.validator.VersionEditOperationValidator editissue.task.status.open.validator.1.fieldid=fixVersions editissue.task.status.open.validator.1.checks=planning:true
And I'm waiting for Script Runner v 2.1 which has this implementation. Could you please let me know when it is releasing.
Jamie?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Jamie, Please let me know your release(probable) date of v 2.1
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.