Create hello world app using Forge
Table of Contents
1 Create the app
- Navigate to the directory where you want to create the app
forge create
- Enter the name of the app:
hello-world-app
- Select a category:
UI kit
- Select a template:
jira-issue-panel
A new folder will be created.
cd hello-world-app
2 jira-issue-panel template
ls hello-world-app
3 Change the panel title
Go to the manifest.yml
and modify the title.
4 Install the app
Run this command from the top level directory.
forge deploy
Then install the app.
forge install
Select a product: Jira
Enter the site URL: abc-dev.atlassian.net
Then continue.
5 Check your app
Go to Manage apps section of your Jira instance and you will find your app installed on the instance.
Now create an issue in a project, create a project if not already done. You will find a button on top called Forge app for Ravi
or whatever title you used in the manifest.yml
file above.
Cool isn't it? How long it took you to create, deploy and install the app?
6 Further changes to the app.
You can do that using forge deploy
. So let us say you change the title, so after you run this command the changes will be reflected back.
This is a manual way of deploying the changes.
First make sure docker is running.
sudo systemctl start docker
Then run forge tunnel
.
6.1 TODO For me it didn't work properly. If I run docker with sudo then it works but not with normal user. Not a big problem because I can always use forge deploy
.
7 Calling Jira API
Now instead of display a static text, let us consume and end point.
7.1 Install @forge/api
npm install @forge/api
7.2 Make changes in your code
Check this page: https://developer.atlassian.com/platform/forge/call-a-jira-api/
7.3 Run forge tunnel
if not running already
When you add comments and refresh your page then you will see a message in your console showing number of comments.
7.4 Forge might ask to update permission.
If Forge complains about the permissions just follows its instructions.
7.5 Show last comment
Modify the code from Atlassian to display last comment along with number of comments.
const fetchLastCommentForIssue = async (issueId) => { const res = await api .asApp() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } };
8 Make API calls on behalf of a user
In your index.jsx file change .asApp()
to .asUser()
. So your function will look like this.
const fetchLastCommentForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } };
Now just run sudo forge tunnel
again and refresh the issue. You will get a button to allow access.
9 Forge logs
If you run forge logs
then you can check the recent log items. Can be helpful for debugging.
INFO 2021-03-28T14:09:01.909Z e4d5f610-047b-4e01-9942-813e53732c20 Number of comments on this issue: 4 INFO 2021-03-28T14:10:18.192Z f2b0e068-aa1a-40e0-b3f3-8f999a443158 Number of comments on this issue: 4 INFO 2021-03-28T14:10:50.324Z 8b63305e-cf41-4d13-ad6b-a915da77c15f Number of comments on this issue: 4 INFO 2021-03-28T14:11:27.599Z 605b4de8-70e4-473a-b160-a465dbdcf49b Number of comments on this issue: 4
10 Display current project's name
Let us display current issue's project name as well in the panel.
const fetchProjectFromIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}`); const data = await res.json(); return data.fields.project.name; }; //call this method to get the value const projectName = useState(async ()=> await fetchProjectFromIssue(context.platformContext.issueKey));
This is how your code will look like now.
import ForgeUI, { render, Fragment, Text, IssuePanel, useProductContext, useState } from '@forge/ui'; const App = () => { const context = useProductContext(); const [comments] = useState(async () => await fetchCommentsForIssue(context.platformContext.issueKey)); const lastComment = useState(async ()=> await fetchLastCommentForIssue(context.platformContext.issueKey)); const projectName = useState(async ()=> await fetchProjectFromIssue(context.platformContext.issueKey)); const message = `Number of comments on this issue: ${comments.length}`; console.log(message); console.log(lastComment); console.log(projectName); return ( <Fragment> <Text>Hello world! Great it works.</Text> <Text>{message}</Text> <Text>{lastComment[0]}</Text> <Text>{projectName[0]}</Text> </Fragment> ); }; export const run = render( <IssuePanel> <App /> </IssuePanel> ); const fetchCommentsForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); return data.comments; }; const fetchLastCommentForIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}/comment`); const data = await res.json(); if (data.comments.length==0) { return "No comments yet, be the first one!" } else { const numberOfComments = data.comments.length const lastComment = data.comments[numberOfComments-1].body.content[0].content[0].text console.log(lastComment); return lastComment; } }; const fetchProjectFromIssue = async (issueId) => { const res = await api .asUser() .requestJira(`/rest/api/3/issue/${issueId}`); const data = await res.json(); return data.fields.project.name; };
11 Forge tunnel debug
Run forge tunnel debug
and open the url to check debugging. Add debugger;
in your code as well before that.