Rate this page:
If you frequently work with Confluence pages, you may find yourself spending a lot of time manually categorizing and organizing content. This can be a time-consuming process that takes away from other important tasks. With an app that extracts keywords from your Confluence pages, you can save time and streamline the organization process. Instead of sifting through large quantities of information, simply let the app do the work for you by identifying key themes and topics within your pages.
In this tutorial, we will build a Forge app that integrates with OpenAI APIs to extract keywords from the content of a Confluence page.
Make sure you have the following:
We'll be using the following components in this app: permissions, fragment, ContentAction, fetch API, api.asApp(), useProductContext, and useState.
npm
package version 3.3.0. Version 4.x.x is currently incompatible with the Forge runtime environment.tty.isatty
due to a limitation in the Forge runtime.An Atlassian cloud developer site lets you install and test your app on Confluence and Jira products set up for you. If you don't have one yet, set it up now:
You can install your app to multiple Atlassian sites. However, app data won't be shared between separate Atlassian sites, products, or Forge environments.
The limits on the numbers of users you can create are as follows:
The Atlassian Marketplace doesn't currently support cross-product apps. If your app supports multiple products, you can publish two separate listings on the Marketplace, but your app won't be able to make API calls across different products and instances/installations.
The GIF above is an example of how the app will work. When a user clicks Keyword extractor in the three dots menu, the extracted keywords will be added to the page as Confluence labels.
You can find the source code for this demo here.
A high-level outline of how the app works is:
Let’s see how to build this app.
Assuming your development environment is set up, you can get right to it. Follow these steps:
forge create
.keyword-extractor
.UI kit
and then the template confluence-content-action
.forge deploy
.forge install
to install the app on your Confluence instance. You’ll be asked to provide a destination site.manifest.yml
1 2modules: jira:issuePanel: - key: summarizer-hello-world-panel function: main title: summarizer icon: https://developer.atlassian.com/platform/forge/images/icons/issue-panel-icon.svg function: - key: main handler: index.run permissions: scopes: - 'read:jira-work' external: fetch: backend: - 'api.openai.com' app: id: <your-app-id>
To call certain Confluence and external APIs, you need to give your app permission to do so. This is done by adding scopes and external permissions to the app’s manifest.yml
file. This app will call three APIs:
read:page:confluence
permission is required to call this API.write:confluence-content
and write:label:confluence
permissions are required to call this API.external.fetch.backend
permission is used to define external domains your Forge functions can talk to.The confluence:contentAction
module entry was added by the confluence-content-action
template. You can learn more about this module here.
index.jsx
with the main top-level logic for your appThe top-level code calls other functions to interact with the Confluence and ChatGPT APIs which you’ll add as you work through the tutorial.
index.jsx
1 2// Import necessary libraries and modules import ForgeUI, { render, ContentAction, useState } from '@forge/ui'; import { useProductContext } from "@forge/ui"; import api, { route } from "@forge/api"; import { Configuration, OpenAIApi } from 'openai'; import tty from 'tty'; // Define the main component of the app const App = () => { // Get the current context (e.g., Confluence page) information const context = useProductContext(); const pageId = context.contentId // Use state to fetch and store page data asynchronously const [pageData] = useState(async () => { return await getPageData(pageId); }); // Define a prompt to be used for the OpenAI API const prompt = `Here is the data:"${pageData}" Give me the 5 most important keywords from the text. Return the results in the form of a JavaScript array. The response shouldn't contain anything apart from the array. No extra text or JavaScript formatting.` // Use state to call the OpenAI API and store the result (keywords) const [keywords] = useState(async () => { return await callOpenAI(prompt); }); // Use state to add the extracted keywords as labels to the current page const [response] = useState(async () => { return await addKeywordsToLabels(keywords, pageId); }); // Log the response from adding keywords as labels console.log(response) // Render nothing, as the main purpose is API interactions and data processing return (null); }; // Render the main component within a ContentAction export const run = render( <ContentAction> <App/> </ContentAction> );
This is the main part of the app, which contains the top-level logic to call APIs and render the UI.
manifest.yml
by defining index.run
.App()
is then triggered. This is where all the magic happens:
useProductContext
.getPageData()
method, which is defined later in this tutorial.callOpenAI()
method.addKeywordsToLabels()
method.index.jsx
to call a Confluence API to get the content of a pageindex.jsx
1 2// Function to fetch page data from Confluence const getPageData = async (pageId) => { const response = await api.asApp().requestConfluence(route`/wiki/api/v2/pages/${pageId}?body-format=storage`, { headers: { 'Accept': 'application/json' } }); // Log the response status and text console.log(`Response: ${response.status} ${response.statusText}`); // Extract and return the content of the page const responseData = await response.json() const returnedData = responseData.body.storage.value return returnedData }
This function calls the get page API using the api.asApp()
method.
created at
and author
.prompt
variable it sends to ChatGPT.Now that the app can retrieve all the content of a Confluence page via an API, the next step is to pass it to the OpenAI API to get the keywords.
index.jsx
to call the ChatGPT API to retrieve the keywordsIn the previous step, you added the variable prompt
, then constructed a prompt using the page content and a command that tells OpenAI what to do with that data - in this case, extract keywords. The code passes the prompt
variable to the callOpenAI
function, which calls the OpenAI API and returns the results.
Here is the code for the callOpenAI
function:
index.jsx
1 2// Function to interact with the OpenAI API using a given prompt const callOpenAI = async (prompt) => { // Polyfilling tty.isatty due to a limitation in the Forge runtime // This is done to prevent an error caused by a missing dependency tty.isatty = () => { return false }; // Create a configuration object for the OpenAI API const configuration = new Configuration({ apiKey: process.env.OPEN_API_KEY, // Replace with your actual API key organisation: process.env.OPEN_ORG_ID // Replace with your actual organisation ID }); // Log the API configuration for debugging purposes console.log(configuration) // Create an instance of the OpenAIApi with the provided configuration const openai = new OpenAIApi(configuration); // Log the prompt that will be sent to the OpenAI API console.log(prompt) // Create a chat completion request using the OpenAI API const chatCompletion = await openai.createChatCompletion({ model: "gpt-3.5-turbo", // Specify the model to use (GPT-3.5 Turbo) messages: [{ role: "user", // Role of the user in the conversation content: prompt // The user's input prompt }] }); // Extract the response content from the API response const response = chatCompletion.data.choices[0].message.content; // Log the generated response for debugging purposes console.log("Prompt response - " + response); // Return the generated response from the OpenAI API return response; }
Here, the app makes a basic API call to OpenAI. You can learn more about this through their documentation. The steps involved include:
The OPEN_API_KEY
environment variable is where you set the OpenAI API key that is needed to interact with their APIs. You might also need to pass an organisation ID, here referred to as OPEN_ORG_ID
.
To create an environment variable in Forge, enter the following command in your terminal:
1 2forge variables set --encrypt OPEN_API_KEY your-api-key forge variables set --encrypt OPEN_ORG_ID your-org-id
The --encrypt
flag instructs Forge to store the variable in encrypted form.
The last step is to add the keywords in the form of a JavaScript array as labels to the page. We’re going to do that using the add labels to content API.
index.jsx
1 2// Function to add keywords as labels to the current page const addKeywordsToLabels = async (keywords, pageId) => { // Parse the keywords and prepare them for adding as labels const bodyData = JSON.parse(keywords).map(label => ({ prefix: "global", name: label.split(" ").join("-") })); // Log the formatted data to be added as labels console.log(`bodyData - ${JSON.stringify(bodyData)}`) // Make a request to the Confluence API to add labels to the page const response = await api.asApp().requestConfluence(route`/wiki/rest/api/content/${pageId}/label`, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(bodyData) }); // Log the response status and text console.log(`Response: ${response.status} ${response.statusText}`); // Parse and return the response JSON const responseJson = await response.json() return responseJson }
Now it’s time to:
forge deploy
in the terminal again as the manifest.yml
file was updated.forge install --upgrade
and select the installation to upgrade. If you have followed along with this tutorial, it should list the development environment for your Confluence instance.You've shown incredible dedication and skill by finishing the Forge app tutorial. Well done! If you need help, reach out to our developer community. Keep up the excellent work and continue to explore new opportunities for your apps using Forge and OpenAI technology.
Rate this page: