In Part I of the quest you used the Forge CLI to create a forge app based on one of the many available templates, you then customised the app and added a new module.
Then, in Part II of the quest you customised your app further by accessing application context data via both the front and back end.
Now, in Part III you will personalise your app by accessing and displaying the name of the current user.
In this section, you'll modify your app to call the Confluence REST API. Using the requestConfluence bridge method from the @forge/bridge
package, you'll use the accountId from the context data you retrieved in part II to lookup the user name using the bulk user lookup api.
The @forge/bridge
package simplifies requests to product REST APIs as well as other javascript APIs to interact with the products. The requestConfluence bridge method enables Forge apps to call the Confluence Cloud platform REST API as the current user.
src/frontend
directory and open the index.jsx
file.requestConfluence
to the imports from @forge/bridge
at the top of the file, it should now look like:
1 2import { invoke, view, requestConfluence } from '@forge/bridge'
useState()
variables under the existing ones in the App()
function; one to track the AccountID, and another to track the Name:
1 2const [accountID, setAccountID] = useState(null); const [name, setName] = useState(null);
getTheme()
function created in Part II to also set the accountID
with the context by adding the following to the end of the function:
1 2setAccountID(context.accountId);
useEffect()
hook to call the bulk user lookup api when the accountID
changes:
1 2useEffect(() => { const getUserInfo = async() => { if(accountID) { let bodyData = `{ "accountIds": [ "${accountID}" ] }`; const response = await requestConfluence(`/wiki/api/v2/users-bulk`, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: bodyData }); if(response.status === 200) { let data = await response.json(); console.log(data); setName(data.results[0].publicName); } else { console.log(response) } } else { console.log("accountID is null") } } getUserInfo(); }, [accountID]);
Heading
in the return statement as follows:
1 2<Heading as="h1">Hello {name ? name: 'World'}!</Heading>
Your src/frontend/index.jsx
should look like this:
1 2import React, { useEffect, useState } from 'react'; import ForgeReconciler, { Text, Heading, Lozenge } from '@forge/react'; import { invoke, view, requestConfluence } from '@forge/bridge'; const App = () => { const [data, setData] = useState(null); const [theme, setTheme] = useState(null); const [accountID, setAccountID] = useState(null); const [name, setName] = useState(null); useEffect(() => { const getUserInfo = async() => { if(accountID) { let bodyData = `{ "accountIds": [ "${accountID}" ] }`; const response = await requestConfluence(`/wiki/api/v2/users-bulk`, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: bodyData }); if(response.status === 200) { let data = await response.json(); console.log(data); setName(data.results[0].publicName); } else { console.log(response) } } else { console.log("accountID is null") } } getUserInfo(); }, [accountID]); useEffect(() => { const getTheme = async() => { const context = await view.getContext(); console.log(context); setTheme(context.theme.colorMode); setAccountID(context.accountId); } getTheme(); }, []); useEffect(() => { invoke('getText', { example: 'my-invoke-variable' }).then(setData); }, []); return ( <> <Heading as="h1">Hello {name ? name: 'World'}!</Heading> <Text>{data ? data : 'Loading...'}</Text> <Text>Current theme: <Lozenge>{theme ? theme : 'Loading...'}</Lozenge></Text> </> ); }; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> );
You might have noticed in the previous step, you didn't update your apps permissions. When you run a forge deploy
, the Forge CLI will automatically run pre-deployment checks before deploying the changes to your environment. As part of those pre-deployment checks forge lint
will detect if permissions are missing, and forge lint --fix
will add the required permissions to your manifest.yml
automatically.
Learn more about permissions
in Forge apps in the manifest documentation.
Follow the steps below to see how this process works for your app:
src/frontend/index.jsx
1 2forge deploy
forge lint
will detect the missing permissions:
1 2CHARLIE:Charlies-First-App catlas$ forge deploy Deploying your app to the development environment. Press Ctrl+C to cancel. Running forge lint... Error: The deploy failed due to errors in the app code. Fix the errors before rerunning forge deploy, or run forge deploy --no-verify to skip the linter. /Users/mpaisley/test/Charlies-First-App/src/frontend/index.jsx 20:49 error Confluence endpoint: POST /users-bulk requires "read:user:confluence" scope permission-scope-required /Users/mpaisley/test/Charlies-First-App/src/frontend/index.jsx 20:49 error Confluence endpoint: POST /users-bulk requires "read:user:confluence" scope permission-scope-required X 2 issues (2 errors, 0 warnings) Run forge lint --fix to automatically fix 2 errors and 0 warnings. Rerunning the command with --verbose may give more details.
1 2forge lint --fix
1 2CHARLIE:Charlies-First-App catlas$ forge lint --fix ✔ Fixed 2 errors and 0 warnings Run forge lint to review outstanding errors and warnings
manifest.yml
, you'll now see the following permissions have been added:
1 2permissions: scopes: - read:user:confluence
1 2forge deploy
forge install --upgrade
as the app permissions have changed:
1 2CHARLIE:Charlies-First-App catlas$ forge deploy Deploying your app to the development environment. Press Ctrl+C to cancel. Running forge lint... No issues found. ✔ Deploying Charlies First App to development... ℹ Packaging app files ℹ Uploading app ℹ Validating manifest ℹ Deploying to environment ✔ Deployed Deployed Charlies First App to the development environment. We've detected new scopes or egress URLs in your app. Run forge install --upgrade and restart your tunnel to put them into effect.
1 2forge install --upgrade
1 2CHARLIE:Charlies-First-App catlas$ forge install --upgrade To upgrade your app to use the latest scopes, select it from the list. Press Ctrl+C to cancel. ? Select the site or workspace to upgrade: (Use the Enter key to select) ┌───────────────┬────────────────────────┬────────────┬─────────────┬───────────────┐ │ Environment │ Site │ Product │ Version │ Major Version │ ├───────────────┼────────────────────────┼────────────┼─────────────┼───────────────┤ │ ❯ development │ cfatlas.atlassian.net │ Confluence │ Out-of-date │ 2 │ └───────────────┴────────────────────────┴────────────┴─────────────┴───────────────┘
1 2? Select the site or workspace to upgrade: cfatlas.atlassian.net Upgrading your app on the Atlassian site. Your app will be upgraded with the following additional scopes: - read:user:confluence ? Do you want to continue? (y/N)
1 2✔ Upgrade complete! Your app in the development environment is now the latest in Confluence on cfatlas.atlassian.net.
Your manifest.yml
should look something like this:
1 2modules: confluence:spacePage: - key: charlies-first-app-hello-world-space-page resource: main resolver: function: resolver render: native title: Charlies First App route: hello-world function: - key: resolver handler: index.handler resources: - key: main path: src/frontend/index.jsx app: runtime: name: nodejs20.x id: <your-app-id> permissions: scopes: - read:user:confluence
Get a Badge
Now that you've successfully built your first Forge App, why not share your success on the First Quest thread in the Forge Quest space on the Developer Community Forums?
You now know many of the basics for building your own Forge Apps. If you'd like to keep learning, head on over to the next tutorial where you'll learn to build a Jira Dashboard Gadget that displays weather information using data from the OpenWeather API.
We'd love to hear from you! Please take a minute to provide feedback on Forge Quest so we can continue to build and improve Forge Quest!
Rate this page: