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.
Now, in Part II of the quest you will customise your app further by accessing application context data via both the front and back end. You will also learn how to automatically deploy the app, and troubleshoot the app using console logging.
This is part II in this tutorial. Complete Part I before working on this page.
In part I of the quest, you needed to run forge deploy
to update your app. Tunneling allows you to speed up development by avoiding the need to redeploy each code change, and by seeing each invocation as it executes. The Forge tunnel works similarly to hot reloading, so any changes you make to your app code can be viewed on your Atlassian site or Bitbucket workspace without losing the current app state. You don’t need to run any other commands; you only need to refresh the page.
To use the forge tunnel
command, Docker must be set up and running. To learn about Docker, visit the Docker getting started guides. If you don't want to run Docker, you can redeploy your app after each code change with the forge deploy
command as described in Part I.
Once Docker is set up, you can start tunneling by running:
1 2forge tunnel
You should see output similar to:
1 2Tunnel redirects requests you make to your local machine. This occurs for any Atlassian site where your app is installed in the specific development environment. You will not see requests from other users. Press Ctrl+C to cancel. Checking Docker image... 100% Your Docker image is up to date. Tunnel uses an anonymous ngrok account, which has a time limit of 2 hours. After that time, you'll need to restart your tunnel. === Running forge lint... No issues found. === Bundling code... ✔ Functions bundled. ✔ Resources bundled. === Snapshotting functions... No log output. Listening for requests... Reloading code... === Running forge lint... No issues found. === Bundling code... ✔ Resources bundled. Listening for requests...
You can now automatically deploy changes to your codebase and install packages, while tunneling. These changes appear on the Atlassian site or Bitbucket workspace where your app is installed.
When you are ready to close the tunnel, press Control + C.
The forge tunnel
command only forwards traffic when the user (in Jira, Confluence, Jira Service Management or Bitbucket) matches the Forge CLI user. For security reasons, you can’t see the traffic of other users.
For important caveats on how forge tunnel
works, see Tunneling.
In this section, you will modify your app to get the application context. Using the Product bridge API from the @forge/bridge
package, you will get the theme for the location the app is currently loaded in and display it using a StatusLozenge.
You can learn more about themes in Design tokens and theming
The @forge/bridge
package allows UI Kit apps to securely integrate with Atlassian products.
You might have noticed that the app created by the forge CLI is already using the invoke method. This means we can skip installing the npm package in this step.
Navigate to the src/frontend
directory.
Open the index.jsx
file. The content of the file should be as follows:
1 2import React, { useEffect, useState } from 'react'; import ForgeReconciler, { Heading, Text } from '@forge/react'; import { invoke } from '@forge/bridge'; const App = () => { const [data, setData] = useState(null); useEffect(() => { invoke('getText', { example: 'my-invoke-variable' }).then(setData); }, []); return ( <> <Heading as="h1">Hello <name>!</Heading> <Text>{data ? data : 'Loading...'}</Text> </> ); }; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> );
Add the view
method to the import ... from '@forge/bridge';
statement on line 3:
1 2import { invoke, view } from '@forge/bridge';
Copy the following code into the App method to create a function that uses the getContext
method as part of the view
object using the @forge/bridge package:
1 2const [theme, setTheme] = useState(null); useEffect(() => { const getTheme = async() => { const context = await view.getContext(); setTheme(context.theme.colorMode); } getTheme(); }, []);
This function uses view.getContext()
to get contextual information about the current environment in which the app is running. The data received depends on the module the app is being used in. In this example, we're interested in the theme colorMode so that's the information saved in the function.
The code here includes React Hooks. If you're unfamiliar and would like to learn more, check out the blog a deeper look at hooks in forge which explains why we've used this approach.
To see the full result returned from view.getContext()
, add a console.log(context);
on the line below.
To view console.log()
results logged from your src/frontend/index.jsx
open your browsers developer console.
For console.log()
results logged elsewhere in your app, check your forge tunnel session.
Add Lozenge
to the import ... from '@forge/react';
statement on line 2:
1 2import ForgeReconciler, { Heading, Lozenge, Text } from '@forge/react';
Add the following line to the Return statement for the App method:
1 2<Text>Current theme: <Lozenge>{theme ? theme : 'Loading...'}</Lozenge></Text>
This will display the current colorMode in a statusLozenge in your app.
Save your changes and then wait for the app to be bundled in your terminal window.
Open the confluence space as you did in Part I, (or refresh the page) to see your changes.
Note: When you refresh the page after opening your browsers developer console you should see something like in the log:
1 2{localId: '<the local id>', cloudId: '<the cloud id>', environmentId: '<the environment id>', environmentType: 'DEVELOPMENT', moduleKey: 'my-first-quest-hello-world-space-page', …}
Your src/frontend/index.jsx
should look like this:
1 2import React, { useEffect, useState } from 'react'; import ForgeReconciler, { Heading, Lozenge, Text } from '@forge/react'; import { invoke, view } from '@forge/bridge'; const App = () => { const [data, setData] = useState(null); const [theme, setTheme] = useState(null); useEffect(() => { const getTheme = async() => { const context = await view.getContext(); console.log(context); setTheme(context.theme.colorMode); } getTheme(); }, []); useEffect(() => { invoke('getText', { example: 'my-invoke-variable' }).then(setData); }, []); return ( <> <Heading as="h1">Hello <name>!</Heading> <Text>{data ? data : 'Loading...'}</Text> <Text>Current theme: <Lozenge>{theme ? theme : 'Loading...'}</Lozenge></Text> </> ); }; ForgeReconciler.render( <React.StrictMode> <App /> </React.StrictMode> );
In this section, you will modify the back end of the app to access context data and pass that data back to be displayed in your app.
src/resolvers
directory for your app.index.js
file, the contents should be as follows:
1 2import Resolver from '@forge/resolver'; const resolver = new Resolver(); resolver.define('getText', (req) => { console.log(req); return 'Hello, world!'; }); export const handler = resolver.getDefinitions();
This will look at the request context which is shared with your resolver when it is called from the front end, including the space key.1 2return 'Welcome to the ' + req.context.extension.space.key + ' space!';
Watch your forge tunnel as you reload your confluence space page, to see the data logged by the resolver. Notice the data looks a little different to the data displayed in the log to the developer console of your browser in the previous section - that's because in this case all of the request data is being logged - not just the context.
1 2mport Resolver from '@forge/resolver'; const resolver = new Resolver(); resolver.define('getText', (req) => { console.log(req); return 'Welcome to the ' + req.context.extension.space.key + ' space!'; }); export const handler = resolver.getDefinitions();
After confirming the app works locally, deploy it so that it continues working after the tunnel is closed.
1 2forge deploy
In the next part of this quest you will personalise your app by displaying the current users name, which you'll get by calling the Confluence API. You'll also learn about modifying the manifest to add the required permissions, and re-deploying an app with new permissions.
Rate this page: