Call my REST endpoints from a Jira Event Listener

Hello, I wrote a plugin for Jira Data Center.
In that plugin, I introduced a couple of REST endpoints.
I also have an event listener that listens on Create or Update.

On Issue Create, I need to call one of my REST endpoints from my event listener to retrieve a value and do something with it. I only need to do a GET.

I think I need to do this with an authenticated request object from Jira but I can’t find a way that compiles, or works.

Does anyone have any examples of how to achieve this?

Thanks!

See if this works for you, using the com.atlassian.sal.api.net.TrustedRequestFactory class:

The documentation for TrustedRequest is here:
https://docs.atlassian.com/sal-api/3.0.0/sal-api/apidocs/com/atlassian/sal/api/net/Request.html

Thank you @KCWong ! I should have mentioned before that that was the first thing I tried.

I could not, and still cannot, get past the compile errors:

In Intellij:

Compile error:

Listener.java:[37,33] cannot find symbol
[ERROR]   symbol:   class TrustedRequest
[ERROR]   location: package com.atlassian.sal.api.net
Listener.java:[38,33] cannot find symbol
[ERROR]   symbol:   class TrustedRequestFactory
[ERROR]   location: package com.atlassian.sal.api.net

I believe this points to my POM, but let’s have a look, I have this:

        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>5.0.1</version>
            <scope>provided</scope>
        </dependency>

Not sure if this matters, but I also have this:

                        <!-- Add package import here -->
                        <Import-Package>
                            com.atlassian.plugin.osgi.bridge.external,
                            com.atlassian.sal.api,
                            com.atlassian.sal.api.auth,
                            com.atlassian.sal.api.pluginsettings,
                            com.atlassian.sal.api.transaction,
                            com.atlassian.sal.api.user,
                            *
                        </Import-Package>

If I can get past this, then I think I’ll finally be able to use TrustedRequestFactory.

I just added these somewhere in my code:

TrustedRequestFactory<TrustedRequest> trustedRequestFactory = 
				ComponentAccessor.getComponent(TrustedRequestFactory.class);
		TrustedRequest request = trustedRequestFactory.createTrustedRequest(MethodType.GET, "TestURL");

I have not added sal-api to pom.xml.
My dependencies:

	<dependencies>

		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<!-- <version>2.13.4</version> -->
			<version>2.9.7</version> <!-- Need this version due to Insight -->
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<!-- <version>2.13.4</version> -->
			<version>2.9.7</version> <!-- Need this version due to Insight -->
		</dependency>

		<!-- Plugin web fragment API, for the com.atlassian.plugin.web.Condition class -->
		<dependency>
			<groupId>com.atlassian.plugins</groupId>
			<artifactId>atlassian-plugins-webfragment-api</artifactId>
			<version>5.3.2</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>com.atlassian.plugin</groupId>
			<artifactId>atlassian-spring-scanner-runtime</artifactId>
			<version>${atlassian.spring.scanner.version}</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>com.atlassian.jira</groupId>
			<artifactId>jira-core</artifactId>
			<version>${jira.version}</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>com.atlassian.jira</groupId>
			<artifactId>jira-api</artifactId>
			<version>${jira.version}</version>
			<scope>provided</scope>
		</dependency>
		<!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. -->
		<!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x -->
		<!--
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-core</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
        </dependency>
        -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.10</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.atlassian.plugin</groupId>
			<artifactId>atlassian-spring-scanner-annotation</artifactId>
			<version>${atlassian.spring.scanner.version}</version>
			<scope>provided</scope>
		</dependency>
		
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
			<scope>provided</scope>
		</dependency>

		<!-- WIRED TEST RUNNER DEPENDENCIES -->
		<dependency>
			<groupId>com.atlassian.plugins</groupId>
			<artifactId>atlassian-plugins-osgi-testrunner</artifactId>
			<version>${plugin.testrunner.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>javax.ws.rs</groupId>
			<artifactId>jsr311-api</artifactId>
			<version>1.1.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<!--<version>2.2.2-atlassian-1</version>-->
			<version>2.3.1</version>
		</dependency>

		<!-- Uncomment to use TestKit in your project. Details at https://bitbucket.org/atlassian/jira-testkit -->
		<!-- You can read more about TestKit at https://developer.atlassian.com/display/JIRADEV/Plugin+Tutorial+-+Smarter+integration+testing+with+TestKit -->
		<!--
        <dependency>
            <groupId>com.atlassian.jira.tests</groupId>
            <artifactId>jira-testkit-client</artifactId>
            <version>${testkit.version}</version>
            <scope>test</scope>
        </dependency>
        -->
	</dependencies>

My POM is setup like this:

<pluginRepositories>
		<pluginRepository>
			<id>atlassian-public</id>
			<url>https://m2proxy.atlassian.com/repository/public/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</pluginRepository>
	</pluginRepositories>

and

<instructions>
						<Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>

						<!-- Add package to export here -->
						<Export-Package>
                            *
                        </Export-Package>

						<!-- Add package import here -->
						<Import-Package>
                            org.springframework.osgi.*;resolution:="optional",
                            org.eclipse.gemini.blueprint.*;resolution:="optional",
                            *;resolution:="optional"
                        </Import-Package>

						<!-- Ensure plugin is spring powered -->
						<Spring-Context>*</Spring-Context>
					</instructions>

And it compiles… granted, I did not test if it actually works.

Thank you very much @KCWong. I have all the same from what I can see but still get the same errors in my editor and when compiling.

I’m compiling with ‘atlas-package’ command. Is that how you are compiling?

After some futzing with my POM and a suggestion I found online, I added this:

        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-trust-api</artifactId>
            <version>4.7.0</version>
            <scope>provided</scope>
        </dependency>

After adding that, the compile highlighted a banned dependency.

[WARNING] Rule 0: org.apache.maven.plugins.enforcer.BannedDependencies failed with message:
make sure platform artifacts are not bundled into plugin
Found Banned Dependency: com.atlassian.**sal:sal-trust-api:jar**:4.7.0
Use 'mvn dependency:tree' to locate the source of the banned dependencies.

Then I ran ‘mvn dependency:tree’ and it downloaded the sal-trust-api

[INFO] --------------------------[ atlassian-plugin ]--------------------------
Downloading from atlassian-public: https://maven.atlassian.com/repository/public/com/atlassian/sal/sal-trust-api/4.7.0/sal-trust-api-4.7.0.pom
Downloaded from atlassian-public: https://maven.atlassian.com/repository/public/com/atlassian/sal/sal-trust-api/4.7.0/sal-trust-api-4.7.0.pom (1.5 kB at 2.1 kB/s)

After that, everything compiles. It still showed errors in my editor, even after doing a ‘Reload from Disk’. But then I closed IntelliJ and reopened it, and then Intellij scanned depenencies and resolved the errors.

So my example code looks like this:

System.out.println("Calling api now ---> "+url);

TrustedRequestFactory<TrustedRequest> trustedRequestFactory =
    ComponentAccessor.getComponent(TrustedRequestFactory.class);
TrustedRequest request = trustedRequestFactory.createTrustedRequest(Request.MethodType.GET, url);

try {
    String response = request.execute();
    System.out.println("Called api and got: "+response);
}
catch (ResponseException re){
    System.out.println("Got error ---> " + re.getMessage());
}

When I run it, I get the output from the first System.out.println:
calling api now —>

But the try/catch never returns. Is there a trick to getting the response?

I realize now that with the code above, ‘trustedRequestFactory’ is null. It throws a nullpointerexception that I wasn’t catching before. Now I get the output to std out saying the factory is null:

TrustedRequestFactory<TrustedRequest> trustedRequestFactory =
          ComponentAccessor.getComponent(TrustedRequestFactory.class);
if (trustedRequestFactory==null){
     System.out.println("Factory is null ");
}

After all this, I found a better way to achieve my desired result.

Since my Listener and my REST API code are both in the same java package, I realized there must be a way to access the getter method in my REST API implementation directly from my Listener.

Here’s an example directory structure:

The APIKeyConfig defines the REST API as can be seen here.

In my Listener, I found out that I could add

  • pluginSettingsFactory
  • transactionTemplate

to the listener constructor like so:

    @Autowired
    public IssueCreatedResolvedListener(EventPublisher eventPublisher, JiraAuthenticationContext jiraAuthenticationContext, PluginSettingsFactory pluginSettingsFactory, TransactionTemplate transactionTemplate) {
        this.eventPublisher = eventPublisher;
        this.pluginSettingsFactory = pluginSettingsFactory;
        this.jiraAuthenticationContext = jiraAuthenticationContext;
        this.transactionTemplate = transactionTemplate;
    }

Then I create the object, pass in some stuff, get a response, get the entity, cast it, call the static method, get the value!!

...
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.atlassian.sal.api.transaction.TransactionTemplate;
import com.atlassian.sal.api.user.UserManager;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

@JiraImport
private TransactionTemplate transactionTemplate;
@ComponentImport
private final PluginSettingsFactory pluginSettingsFactory;
@Context
private HttpServletRequest request;
...
UserManager um = ComponentAccessor.getComponent(UserManager.class);
APIKeyConfig apiKeyConfig = new APIKeyConfig(um,pluginSettingsFactory,transactionTemplate);
Response myResp = apiKeyConfig.get(request);
APIKeyConfig.Config myConfig = (APIKeyConfig.Config)myResp.getEntity();
System.out.println("After GET "+myResp.getStatus()+", "+myConfig.getKey());

I hope that makes sense to anyone else out there who wants to call their own api from their own java class in the same package!

1 Like