I am currently facing two issues while using Forge and I would like to get ideas on how to solve these problems.
When I scroll a page (containing my app) the component is called multiple times and eventually the app crashes.
Also, when I try to call the get user endpoint asynchronously in one of my resolvers, it returns:
INFO 18:48:17.817 70c53fbca7f2799f { code: 401, message: 'Unauthorized; scope does not match' }
Sometimes it returns the correct json response of the user details, but most times it returns âunauthorizedâ. I have all the scopes in place (read:confluence-user). But still, I get this behavior. Please, is there something I am missing or doing wrong? Here is the code block:
I think your map(async.... thing is the problem. Your promise.all does not wait for those Promises. They are âlooselyâ flying around and.
You should optimize the code so that there are awaits for all async calls.
Hi @clouless, I have re-written the block but the issues are still there. I am using a for-loop now and as I understand, the execution of the code will be in series (please, correct if I am wrong). Here is the new code block:
But at best ask some Atlassian staff member why your stuff is executed multiple times. Maybe it is a glitch in the forge infrastructure. I wish you good luck in finding the solution I am out of guesses myself
I see that you are using a Resolver, which is a Custom UI only API. Your usage of the resolver seems like it should work. However, in a follow up message, it looks like you are using useProductContext(), which is a UI kit only API. Perhaps attempting to mix the two is the source of the issue.
Is it possible for you to share more information about the app to help me understand the issue youâre facing, so I can find a solution for you? The full code for the app (or at least a minimal reproducible case, if youâd prefer to keep your business logic private) would be helpful, as well as the manifest.yml file (you can redact any personal or private information), so I can determine the module type that you are using, as well as whether you are using UI kit or Custom UI.
I am writing a UI Kit extension for this task. I am not using Resolver for this at all. I have other parts in my code that uses Resolver plus Custom UI, but this particular is a UI Kit extension.
It is using a âConfluence content byline itemâ. The component returns a ââ and all components within the ââ are UI Kit extensions.
So this is the app component that triggers the inline dialog:This text will be hidden
import ForgeUI, {
render,
Text,
ContentBylineItem,
InlineDialog,
useState,
useEffect,
Table,
Head,
Row,
Cell,
useProductContext,
SectionMessage,
ButtonSet,
Button,
} from '@forge/ui';
const fetchPage = async (contentId) => {
const res = await api
.asUser()
.requestConfluence(
`/rest/api/content/${contentId}?expand=metadata.properties.${PAGE_META_DATA_KEY}`
);
const data = await res.json();
return data;
};
const getUser = async (accountId) => {
const res = await api
.asUser()
.requestConfluence(`/rest/api/user?accountId=${accountId}`);
const data = await res.json();
return data;
};
const getRows = async (context) => {
let rows = [];
const page = await fetchPage(context.contentId);
const metadatas = page.metadata.properties[PAGE_META_DATA_KEY].value;
for (let i = 0; i < metadatas.length; i++) {
const meta = metadatas[i];
const user = await getUser(meta.uid);
const row = {
key: meta.id,
title: page.title,
creator: undefined,
date: meta.created,
user: user,
};
rows.push(row);
}
return rows;
};
const App = () => {
const context = useProductContext();
const [data, setData] = useState([]);
useEffect(async () => {
const rows = await getRows(context);
setData(rows);
}, []);
return (
<InlineDialog>
<SectionMessage title='XXXXXXXXXXXX' appearance='info'>
<Text>
All XXXXXXXXXXXX taken from this page.{' '}
{data.length == 0 || undefined === undefined
? 'No XXXXXXXXXXXX have been taken for this page. To take a XXXXXXXXXXXX go to ....'
: ''}
</Text>
</SectionMessage>
<Table>
<Head>
<Cell>
<Text content='Taken By' />
</Cell>
<Cell>
<Text content='Created Date' />
</Cell>
<Cell>
<Text content='Actions' />
</Cell>
</Head>
{data.map((page) => (
<Row>
<Cell>
<Text content={page.user.displayName} />
</Cell>
<Cell>
<Text content={page.date} />
</Cell>
<Cell>
<ButtonSet>
<Button text='View' />
</ButtonSet>
</Cell>
</Row>
))}
</Table>
</InlineDialog>
);
};
export const runViewXXXXXXXXXXXXInLineAction = render(
<ContentBylineItem>
<App />
</ContentBylineItem>
);
And this is my manifest.xml:
modules:
confluence:contentAction:
- key: create-XXXXXXXXXXXX-action
function: create-page-XXXXXXXXXXXX
title: Take XXXXXXXXXXXX
description: Create XXXXXXXXXXXX action at current timestamp
confluence:spacePage:
- key: XXXXXXXXXXXX-home-page
resource: app
resolver:
function: XXXXXXXXXXXX-app-resolver
title: Softcomply XXXXXXXXXXXX
description: Manage all page or space XXXXXXXXXXXX
route: softcomply-XXXXXXXXXXXX
icon: https://XXXXXXXXXXXX/images/pluginLogoBlue.png
confluence:contentBylineItem:
- key: view-page-XXXXXXXXXXXX-inline-action
function: view-page-XXXXXXXXXXXX
title: View Page XXXXXXXXXXXX
description: View list of XXXXXXXXXXXX created for the page
viewportSize: large
macro:
- key: view-page-XXXXXXXXXXXX-macro
resource: view-page-macro
resolver:
function: XXXXXXXXXXXX-app-resolver
title: View Page XXXXXXXXXXXX Macro
description:
To view page XXXXXXXXXXXX, please, select a XXXXXXXXXXXX to be displayed on the page. The selected XXXXXXXXXXXX
will be the default XXXXXXXXXXXX.
config:
function: view-XXXXXXXXXXXX-config
viewportSize: xlarge
function:
- key: page-XXXXXXXXXXXX-view
handler: index.run
- key: create-page-XXXXXXXXXXXX
handler: create.runXXXXXXXXXXXXAction
- key: view-page-XXXXXXXXXXXX
handler: viewInline.runViewXXXXXXXXXXXXInLineAction
- key: XXXXXXXXXXXX-app-resolver
handler: view.handler
- key: view-XXXXXXXXXXXX-config
handler: config.open
resources:
- key: app
path: static/custom-ui/packages/XXXXXXXXXXXX-page/build
- key: view-page-macro
path: static/custom-ui/packages/view-page-XXXXXXXXXXXX-macro/build
app:
id: ari:cloud:ecosystem::app/15802ba8-c2cc-47a5-9d00-44044248648e
name: XXXXXXXXXXXX
permissions:
scopes:
- read:confluence-content.summary
- read:confluence-content.all
- storage:app
- write:confluence-content
- read:confluence-props
- write:confluence-props
- read:confluence-user