8. Testing your PCJ extensions

Test the migration

 Of course it is necessary to test that configuration objects are actually moved to a target instance of Jira. At the end of the day, that is what 99% of the users will want the extension to do for them.

The following steps may help you to test this in a relatively fast way:

Create the configuration objects you want to move in your development instance

Going back to the gadget extension example, you would create a dashboard that contains a gadget of the type Show Saved Filter with Columns and configure it with a valid filter.

Export a configuration containing those objects

Export these objects and check that the export completed without errors or warnings. If you look at the part of the XML file that corresponds to those objects, you should notice that instance-specific references (usually, all those that use IDs to refer to other objects) have been rewritten as instance-independent strings. Continuing with the gadget example, you would see a description like:

Exported file

.... <name>Test dashboard</name> <layout>AA</layout> <gadget> <type>rest/gadgets/1.0/g/com.ja.jira.plugin.searchrequest:sswc-gadget/gadget_wc.xml</type> <column>0</column> <row>0</row> <color>color1</color> <parameter> <key>columnNames</key> <value>issuetype@@issuekey@@summary@@priority</value> </parameter> <parameter> <key>filterId</key> <value>admin@@Due this week(RR)</value> </parameter> <parameter> ....

Remember that before the extension existed, this was exported with a filter ID, which would have been useless when moved into a different instance. Now, that has been replaced by the filter owner and name, which will be used at the destination instance to rebuild the reference correctly.

Delete the configuration object and its dependencies and simulate the import of the exported file

To save the trouble of having two different Jira instances for testing, you could remove the created gadget and the filter it refers to from the development instance and then import the exported file into the same instance again. Run an import simulation first. You should see something like this (expanding the sections which are relevant to those objects):

image2021-3-9_10-46-47.png

Note that there are two changes planned to create both the missing gadget and the missing filter. Note also that the details for the gadget refer to the filter by its owner and name, not by its ID.

Disable creation of the referred object and recalculate

We know that the operations to create the gadget and the filter are logically related: if the filter were missing then it would not be possible to create the gadget, as it requires that filter to be properly configured. You can test this in the import simulation. Disable the creation of the filter by clicking the green action icon. It will be greyed out. Click the Recalculate button. The results appear similar to the following:

image2021-3-9_10-48-32.png

Note that after disabling the changes on filters (they are greyed out), the creation of the gadget has a red box (this means an error) that says a filter with the required owner and name cannot be found.

Enable operations and run the actual import

So far, nothing has changed in Jira. You are still working with a simulation; it is like a preview of the changes, but none of them have actually occurred.

In the next step, re-enable changes to the filters and click Apply Configuration. This will apply the changes to your source Jira instance. The tree with the actual changes will be displayed:

Verify that the changes displayed match what you expected. They should be the same as those previewed in the simulation. Finally, check the dashboard in Jira and verify that the new gadget has been created with the correct configuration. Make sure the gadget is configured for a new filter, also created by the import, called "Due this week (RR)" and owned by user "admin".

Use the object dependencies report

The Object Dependencies report is a feature of Project Configurator that analyses dependencies between configuration objects in any instance of Jira.

In the case of the workflow examples discussed in this guide, you will notice the following "used by" relationships exist:

  • User "jsmith" is used by the "New service workflow" (as it is used in a post-function of the workflow)

  • Custom field "Release cut-off date" is used by the "New service workflow" (as it is used in a condition of the workflow)

Integrations with other apps are designed to work seamlessly with this feature. So, if you run the Object Dependencies report, you will see these relationships shown in the report, for example:

Note this can be quite useful for testing references between objects. Verifying the Object Dependencies report is likely the quickest and easiest way to check references are working as expected.

The Object Dependencies report includes only those objects that are directly or indirectly used by any of the projects in the instance. This means that the following will not appear: objects that are exclusively used by dashboards, filters, or Agile boards and the relations where any object is used by any dashboard, filter, or Agile board. In general, any object which is not used by any project (like an inactive workflow).

Pro Tip: Create the extension in two stages

 Creating the extension in two stages simplifies both the implementation and the testing.

When creating a custom entity extension, you can exclude from the initial implementation the methods that actually create or delete objects or set any of its properties. These methods will only be used during a real import, so the following operations should work correctly for the related custom entities, even when those methods are not yet implemented:

  • Export

  • Object dependencies report

  • Import conflict detection

  • Simulated import

This lets you partially implement the support for some custom entity, test that partial implementation, and later, with the confidence that a substantial part of the extension is already correct, proceed to complete the "create / delete / update" methods.

Use the Jira log if necessary

 It is possible to extract a lot of useful information from the Jira log file. If this is required, then set the log level to DEBUG for the package com.awnaba.projectconfigurator. See Change Logging Levels in Jira Server to learn how to temporarily change the log level for specific packages in Jira. 

After setting the log level to DEBUG, run any operation with Project Configurator, and you will see the increased content in the log file. For example, in the case of export with the workflow extension discussed in this guide, you will see entries like these:

Example Log file

... 2020-05-06 14:41:30,684+0200 http-nio-2990-exec-11 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.extensionpoints.management.ExtensionActivatorImpl] Starting extensions 2020-05-06 14:41:30,684+0200 http-nio-2990-exec-11 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.extensionpoints.management.ExtensionActivatorImpl] Enabling PC extensions in plugin: com.adaptavist.projectconfigurator.wes4jextension 2020-05-06 14:41:30,685+0200 http-nio-2990-exec-11 INFO admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.s.s.dynamic.contexts.GeminiOsgiBundleXmlApplicationContext] Refreshing GeminiOsgiBundleXmlApplicationContext(bundle=com.adaptavist.projectconfigurator.wes4jextension, config=bundle://232.0:1/META-INF/pc4j- extensions/spring/profile-pc4j-extensions.xml): startup date [Wed May 06 14:41:30 CEST 2020]; parent: NonValidatingOsgiBundleXmlApplicationContext(bundle=com.adaptavist.projectconfigurator.wes4jextension, config= osgibundle:/META- INF/spring/*.xml) ... 2020-05-06 14:41:30,739+0200 http-nio-2990-exec-11 WARN admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.projectconfigurator.wes4jextension.spring] Spring context started for bundle : com.adaptavist.projectconfigurator.wes4jextension id(233) v(1.0.0.SNAPSHOT) file:/Users/pepemaranon/Workspaces/jira-project-config-plugin/project-configurator-plugin/amps-standalone-jira- 8.8.1/target/jira/home/plugins/installed-plugins/plugin_3339388906409322341_wes4jextension-1.0.0-SNAPSHOT.jar If you want to debug the Spring wiring of your code then set a DEBUG level log level as follows. [ This is a dev.mode only message. ] log4j.logger.com.adaptavist.projectconfigurator.wes4jextension.spring = DEBUG, console, filelog ... 2020-05-06 14:41:31,176+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.extensionpoints.management.ExtensionActivatorImpl] Found 2 implementations of extension entities for plugin com.adaptavist.projectconfigurator.wes4jextension ... 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] XPath provided by extension: //function[arg[@name='class.name' and (text()='de.codecentric.jira.postfunction.SetAssigneeToSpecificUserPostFunction')]]/arg[@name='USERNAME_VALUE_FIELD']/text() 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] Node for this occurrence: 3 #text admin 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] XPath found one occurrence: admin 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] Found reference by string: admin 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] Finished processing this extension 2020-05-06 14:41:32,514+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] XPath provided by extension: //condition[arg[@name='class.name' and (text()='de.codecentric.jira.condition.DateComparisonCondition')]]/arg[@name='FIELD_ID']/text() 2020-05-06 14:41:32,515+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] Node for this occurrence: 3 #text 10001 2020-05-06 14:41:32,515+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] XPath found one occurrence: 10001 2020-05-06 14:41:32,515+0200 JiraTaskExecutionThread-5 DEBUG admin 881x49361x1 16cohu0 0:0:0:0:0:0:0:1 /secure/project-export!export.jspa [c.a.p.adapters.workflow.WorkflowTranslator] Finished processing this extension ...

Note the following details:

  • /secure/project-export!export.jspa is the relative URL where the export was launched from

  • There are entries that mark when Project Configurator starts searching for extensions and a specific one for each extension

  • There are also entries that show when the dynamic Spring context for each extension is started

  • The other entry shows that this workflow extension defines two extension points (one for the condition and another for the post-function)

  • For each extension point, it is reported when its processing begins and ends and the number and content of occurrences found for each workflow

Exploring the log files in this way can be useful when the extension appears not to work or even when it does not exist. This is often caused by a problem that prevents the starting of its dynamic Spring context. In these cases, Project Configurator will simply ignore the offending extension and continue with the rest of the operation.