Level 1 · Lesson 6
Read the ticket
Level 1 · Lesson 6Read the dish from the work item’s own summary, as the person looking at it, and let forge lint add the permission that needs.
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
Locked until the step before it is done.
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.
The package is already installed from lesson 4. Import its default export and route:
src/resolvers/index.jsapi 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/…”
Next step: next
Step 2: Read the summary on the staff member’s badge
Locked until the step before it is done.
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.
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:
src/resolvers/index.jsA 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”
Next step: next
Step 3: Cook the dish on the ticket
Locked until the step before it is done.
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.
Replace the fixed dish with the ticket’s summary:
src/resolvers/index.jsTake 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
Next step: next
Step 4: Let the inspector add the key
Locked until the step before it is done.
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.
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:
forge lintIt reports the missing scope. Let it write it:
forge lint --fixmanifest.ymlforge 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
Next step: next
Step 5: Sign the new licence again
Locked until the step before it is done.
A new scope is a new permission, so this is a major version again. Stop the tunnel if it runs, then:
forge deploy --approve MAJOR_VERSION_RULE
forge install --upgradeReload 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
Next step: next
Step 6: Try other dishes
Locked until the step before it is done.
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
Next step: next
Compare with yours: download the code after this lesson.