Course menuForge Kitchen

Level 1 · Lesson 6

Read the ticket

Read the dish from the work item’s own summary, as the person looking at it, and let forge lint add the permission that needs.

About 30 min6 steps

Behind, or starting here? Download the code as it should be before this lesson.

The card still asks for Pad thai on every ticket. This lesson makes the cook read the summary of the ticket the card is on. That means a call into Jira, and Jira has its own rules about who may read what.

Step 1: Bring in the Jira API

Atlassian’s own products · House systems

Product REST APIs · the house systems

Jira and Confluence have REST APIs for everything you see on screen: work items, comments, pages. From a function you call them through @forge/api, with requestJira or requestConfluence, and a path built with the route tag. Forge adds the authentication; you never handle a Jira token.

In the kitchen The building’s own systems: the ticket board and the menu book.

Atlassian docs: Product REST APIs (opens in a new tab)

The package is already installed from lesson 4. Import its default export and route:

Editsrc/resolvers/index.js

api and route come from @forge/api.

import Resolver from '@forge/resolver';
Added: import api, { route } from '@forge/api';
import { findDish } from '../supplier/apiNinjas';
const resolver = new Resolver();

Why: Product REST APIs

Stuck? “Cannot find module @forge/…”

Step 2: Read the summary on the staff member’s badge

Runs in the Atlassian cloud · Back of house

asUser() · on the staff member’s badge

asUser() makes the call as the person using the app, with exactly their permissions. If they cannot see a work item, neither can your code. The first time someone uses an asUser() call, Jira asks them to allow the app to act for them. Its opposite, asApp(), acts as the app itself; Level 2 needs it for work that runs with nobody watching.

In the kitchen The kitchen goes into the house systems on the badge of the person who asked, and can only open what they can.

Atlassian docs: Product REST APIs (opens in a new tab)

The resolver reads the summary with asUser().The cook reads the ticket on the staff member’s badge.

Add a helper that asks Jira for one work item, with only the summary field:

Editsrc/resolvers/index.js

A helper that asks Jira for one work item’s summary.

const resolver = new Resolver();
Added: // Ask Jira for the work item's summary, with the permissions of the person viewing it.
Added: async function readSummary(issueId) {
Added: const response = await api.asUser().requestJira(route`/rest/api/3/issue/${issueId}?fields=summary`);
Added: if (!response.ok) {
Added: throw new Error(`Jira answered ${response.status} for this work item.`);
Added: }
Added: const issue = await response.json();
Added: return issue.fields.summary;
Added: }
Added:
resolver.define('getDish', async () => {
const query = 'pad thai';
try {

Why: asUser() · Product REST APIs

route escapes the id safely, so a strange id can never change the path. A non-OK answer, such as 404 when the viewer cannot see the work item, becomes an error with the status in it.

Stuck? The app asks me to “Allow access”

Step 3: Cook the dish on the ticket

Runs in the Atlassian cloud · Back of house

Product context · the ticket number

Every resolver call arrives with a context that Forge fills in: who is calling, from which site, and from where in the product. On an issue panel, context.extension.issue holds the work item’s id and key. The browser cannot change it, so it is the right source for which ticket to read.

In the kitchen The runner always says which ticket the card is clipped to.

Atlassian docs: App context security (opens in a new tab)

Replace the fixed dish with the ticket’s summary:

Editsrc/resolvers/index.js

Take the work item id from the context, not from the browser, and use its summary.

return issue.fields.summary;
}
Removed: resolver.define('getDish', async () => {
Removed: const query = 'pad thai';
Added: resolver.define('getDish', async ({ context }) => {
Added: const query = await readSummary(context.extension.issue.id);
try {
const dish = await findDish(query, process.env.API_NINJAS_KEY);
return { query, dish };

Why: Product context

The card could have sent the work item id in invoke() instead, but anything the browser sends can be changed by the person using it. The context comes from Forge.

Stuck? The card says there is no recipe for my work item

Step 4: Let the inspector add the key

Declared in manifest.yml · The licence

Scope · a key to the house systems

A scope is a permission to use part of a product API, such as read:jira-work for reading work items. The app declares its scopes under permissions.scopes, the admin agrees to them at install, and Forge refuses any API call the scopes do not cover, even if the person could do it themselves.

In the kitchen Each key opens one door into the house systems. No key, no entry.

Atlassian docs: Permissions (opens in a new tab)

Lives on your laptop · Home kitchen

forge lint · the inspector

forge lint checks the manifest and code before a deploy and reports problems, such as an API call with no matching scope. forge lint --fix adds what it can work out for you. forge deploy runs the same checks first.

In the kitchen The inspector walks through, spots the missing key and writes it onto the licence for you.

The new call reads Jira, and the manifest has no scope for that yet. Ask the linter:

RunTerminal
forge lint

It reports the missing scope. Let it write it:

RunTerminal
forge lint --fix
Editmanifest.yml

forge lint --fix writes this for you.

fetch:
backend:
- address: api.api-ninjas.com
Added: scopes:
Added: - read:jira-work

Why: Scope · forge lint

Stuck? I added a scope or a supplier host

Step 5: Sign the new licence again

A new scope is a new permission, so this is a major version again. Stop the tunnel if it runs, then:

RunTerminal
forge deploy --approve MAJOR_VERSION_RULE
forge install --upgrade

Reload the Pad thai ticket. Because the cook now reads Jira as you, the card first asks you to allow access. Allow it once; the recipe appears after.

Stuck? Deploy stops after a permission change · The app asks me to “Allow access” · The tunnel is running while I deploy or upgrade

Step 6: Try other dishes

Create two more dish tickets in the Kitchen space, such as Lasagna and Tom yum soup, and open each one. Each card shows its own dish.

Rename one to something that is not a dish. The card shows the information message with the summary in it: the empty state from lesson 5, now with a real query.

Stuck? The card says there is no recipe for my work item

Compare with yours: download the code after this lesson.

Type at least two letters.