In this tutorial, you will build a Forge app that integrates with OpenAI APIs to summarize comments in Jira issues. This app addresses the following challenges:
Make sure you have the following:
The tutorial uses these components to build the app: Jira issue panel module, permissions, text, fetch API, api.asApp(), .requestJira(), useState() and useEffect().
An Atlassian cloud developer site lets you install and test your app on Confluence and Jira products set up for you. If you don't have one yet, set it up now:
You can install your app to multiple Atlassian sites. However, app data won't be shared between separate Atlassian sites, products, or Forge environments.
The limits on the numbers of users you can create are as follows:
The Atlassian Marketplace doesn't currently support cross-product apps. If your app supports multiple products, you can publish two separate listings on the Marketplace, but your app won't be able to make API calls across different products and instances/installations.
To view a summary of an issue's Jira comments, the user clicks the Summarizer button on an issue page. The app displays an issue panel containing a summary of all the comments for the issue.
You can find the source code for this demo here: atlassian/forge-ai-jira-comment-summarizer.
Following the numbered points in the diagram:
Once your development environment is set up, follow these steps to create an initial version of your app:
forge create
command in the terminal.summarizer
.UI Kit
, the Jira
product, and then the template jira-issue-panel
.forge deploy
to deploy your app.forge install
in terminal. You will be asked to provide the site
in which you would like to install the app.Depending on your Jira site, you may see the name of your app in the menu, as pictured below, or you may see only its icon.
You can read an explanation of each code block below it.
manifest.yml
1 2modules: jira:issuePanel: - key: summarizer-hello-world-issue-panel resource: main resolver: function: resolver render: native title: summarizer icon: https://developer.atlassian.com/platform/forge/images/icons/issue-panel-icon.svg function: - key: resolver handler: index.handler resources: - key: main path: src/frontend/index.jsx permissions: scopes: - 'read:jira-work' external: fetch: backend: - 'api.openai.com' app: id: <your-app-id>
To call certain Jira APIs and external APIs, you need to give your app permission to do so. This is done by adding scopes and external permissions to the app’s manifest.yml file. The app will call two APIs:
read:jira-work
permission is required to call this API.external.fetch.backend
is used to define external domains your Forge functions can talk to.The jira:issuePanel
module entry was added by the jira-issue-panel
template. You can learn more
about this module here.
src/frontend/index.jsx
with the main top-level logic for your appThe top-level code calls other functions to interact with the Jira and Chat GPT APIs, which you’ll add as you work through the tutorial.
src/frontend/index.jsx
1 2import React, { useEffect, useState } from 'react'; import ForgeReconciler, { Text } from '@forge/react'; import { invoke, view } from '@forge/bridge'; const App = () => { const [summary, setSummary] = useState(); // Getting all the comments of the issue. useEffect(() => { const getCommentSummary = async () => { const commentsData = await invoke('getComments'); console.log("Comments - " + commentsData); if (commentsData) { // ChatGPT prompt to get the summary const prompt = `Here is a sample data where all the comments of a jira issue is joined together: "${commentsData}". I want to summarize this in a way that anybody can get an idea what's going on in this issue without going through all the comments. Create a summary or TLDR for this.` // OpenAI API call to get the summary. const summary = await invoke('callOpenAI', { prompt }); console.log("Summary - " + summary); setSummary(summary); } }; getCommentSummary(); }, []); return ( <> <Text>{summary}</Text> </> ); }; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> );
This is the main part of the app, which contains the top-level logic to render the UI and fetch data via Forge Resolvers.
App()
is then triggered. This is where all the magic happens:
getComments
resolver, which is defined later in this tutorial.callOpenAI
resolver, which is defined later in this tutorial.Text
component.src/resolvers/index.js
to call a Jira API to get all comments for this issuesrc/resolvers/index.js
1 2import Resolver from '@forge/resolver'; import api, { route, fetch } from '@forge/api'; const resolver = new Resolver(); resolver.define('getComments', async ({context}) => { // API call to get all comments of Jira issue with key const commentsData = await api.asApp().requestJira(route`/rest/api/3/issue/${context.extension.issue.key}/comment`, { headers: { 'Accept': 'application/json' } }); // API call to get all comments of Jira issue with key const responseData = await commentsData.json(); const jsonData = await responseData.comments let extractedTexts = []; // Extracting all texts in the comments into extractedTexts array await jsonData.map(comment => { if (comment.body && comment.body.content) { comment.body.content.map(contentItem => { if (contentItem.type === "paragraph" && contentItem.content) { contentItem.content.map(textItem => { if (textItem.type === "text" && textItem.text) { extractedTexts.push(textItem.text); } }); } }); } }); return extractedTexts.join(' '); });
The app first imports the resolver and API calls that will be used.
We can define the getComments
resolver. This resolver calls the
Get issue comments API
using the api.asApp() method.
issue.key
found in context
, and retrieve the comments associated with that issuecreated at
and author
, and join all the comments
together into a paragraph.prompt
variable it sends to ChatGPT.Now that the app can retrieve all the comments in a Jira Issue via an API, the next step is to pass it to OpenAI API to get the summary.
src/resolvers/index.js
to call the ChatGPT API to summarize the commentsIn the src/frontend/index.jsx
, you added the variable prompt
, then constructed a prompt using the comments and a command that tells OpenAI what to do with that data (in this case: summarize it). The code passes the prompt variable to an API call callOpenAI
, which would call OpenAI API and return the results.
Here is the code for the callOpenAI
call, which would be placed underneath the getComments
call:
src/resolvers/index.js
1 2resolver.define('callOpenAI', async ({payload, context}) => { const choiceCount = 1; // OpenAI API endpoint const url = `https://api.openai.com/v1/chat/completions`; // Body for API call const body = { model: getOpenAPIModel(), n: choiceCount, messages: [{ role: 'user', content: payload.prompt }] }; // API call options const options = { method: 'POST', headers: { Authorization: `Bearer ${getOpenAPIKey()}`, 'Content-Type': 'application/json', }, redirect: 'follow', body: JSON.stringify(body) }; // API call to OpenAI const response = await fetch(url, options); let result = '' if (response.status === 200) { const chatCompletion = await response.json(); const firstChoice = chatCompletion.choices[0] if (firstChoice) { result = firstChoice.message.content; } else { console.warn(`Chat completion response did not include any assistance choices.`); result = `AI response did not include any choices.`; } } else { const text = await response.text(); result = text; } return result; }); // Get OpenAI API key const getOpenAPIKey = () => { return process.env.OPEN_API_KEY; } // Get OpenAI model const getOpenAPIModel = () => { return 'gpt-3.5-turbo'; // return 'gpt-4'; }
Here, the app makes a basic API call to OpenAI. You can learn more about this through their documentation. The steps involved include:
The getOpenAPIKey()
function returns a Forge environment variable called OPEN_API_KEY
.
Before running the app for the first time, set this environment variable to the OpenAI API key
that is needed to interact with their APIs.
To create an environment variable in Forge, enter the following command in your terminal:
1 2forge variables set --encrypt OPEN_API_KEY your-key
The --encrypt
flag instructs Forge to store the variable in encrypted form.
Once all the above steps are done, you can:
forge deploy
in the terminal again as the manifest.yml
file was updated.forge install --upgrade
and select the installation to upgrade. If you have followed along
with this tutorial, it should list the development environment for your Jira instance.Great job on finishing the tutorial on developing a Forge app with OpenAI! Take a moment to celebrate this impressive achievement. If you require any additional help, reach out to our developer community. Keep up the excellent work and continue to explore new opportunities for your apps using Forge and OpenAI technology.
Rate this page: