confluence-forge

Confluence Forge app external fetch backend permissions error


I am building a Confluence app that pulls images from Imgur api and displays them in Confluence pages.

Here is the JSX

import ForgeUI, { render, Fragment, Macro, Text, Image, useProductContext, useState, useEffect } from "@forge/ui";
import { fetch } from '@forge/api';

const IMGUR_API_ENDPOINT = 'https://api.imgur.com/3/gallery/hot/viral/0.json';

const App = () => {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch(IMGUR_API_ENDPOINT, {
                    headers: {
                        'Authorization': 'Client-ID <client-id>' // Replace with your Client-ID
                    }
                });

                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }

                const result = await response.json();
                console.log("Received data:", result);  // Log received data
                setData(result.data);
                setLoading(false);
            } catch (err) {
                console.error("Error occurred:", err);  // Log any error
                setError(err);
                setLoading(false);
            }
        };

        fetchData();
    }, []);

    if (loading) return <Text>Loading images...</Text>;
    if (error) return <Text>Error: {error.message}</Text>;

    return (
        <Fragment>
            <Text>Hot Images from Imgur:</Text>
            {data && data.map(image => (
                <Fragment key={image.id}>
                    <Image src={image.link} alt={image.title} />
                    <Text>{image.title}</Text>
                </Fragment>
            ))}
        </Fragment>
    );
};

export const run = render(
    <Macro
        app={<App />}
    />
);

and here is the manifest.yml

  modules:
    macro:
      - key: imgur-gallery-macro
        function: main
        title: Imgur Gallery Viewer
        description: View hot images from Imgur in Confluence.
    function:
      - key: main
        handler: index.run
  app:
    id: ari:cloud:ecosystem::app/<removed>
  permissions:
    external:
      fetch:
        backend:
          - 'https://api.imgur.com/*'

I keep getting the error even though I have added the URL. At a loss on how to resolve this error.

  message: 'URL not included in the external fetch backend permissions: https://api.imgur.com/3/gallery/hot/viral/0.json. Visit go.atlassian.com/forge-egress for more information.',
  name: 'REQUEST_EGRESS_ALLOWLIST_ERR',
  status: 403
}

I have read and re-read the Forge egress article go.atlassian.com/forge-egress. I have tried formatting the URL with a wild card and without, with and without quotes, and as the full URL. I have gone through the steps of forge upgrade and forge tunnel and forge deploy after making changes. I am unsure what else I need to do to make this work. This is a API endpoint that allows anonymous users so it shouldn't require the full OAuth workflow or am wrong in saying that?


Solution

  • I posted the same question above to the Atlasssian developer forums https://community.developer.atlassian.com/t/custom-app-external-fetch-backend-permissions-error/72674 and based on the feedback there I was able to resolve the issue.

    The gist is that I needed to adjust the useEffect function - adding async and await

        const fetchData = async () => {
            // ... same as before
        };
        await fetchData();
    }, []);
    

    I also made a change in the Imgur image feed I was pulling; I changed it to the earthporn subreddit so I'd render some pretty naturescapes as opposed to some of the weird and disturbing stuff that was popping up on the hot and viral feed.

    Here is the final code

    import ForgeUI, { render, Fragment, Macro, Text, Image, useProductContext, useState, useEffect } from "@forge/ui";
    import { fetch } from '@forge/api';
    
    const IMGUR_API_ENDPOINT = 'https://api.imgur.com/3/gallery/r/earthporn/0';
    
    const App = () => {
        const [data, setData] = useState(null);
        const [loading, setLoading] = useState(true);
        const [error, setError] = useState(null);
    
        useEffect(async () => {
            const fetchData = async () => {
                try {
                    const response = await fetch(IMGUR_API_ENDPOINT, {
                        headers: {
                            'Authorization': 'Client-ID <client-id>' // Replace with your Client-ID
                        }
                    });
    
                    if (!response.ok) {
                        throw new Error('Network response was not ok');
                    }
    
                    const result = await response.json();
                    console.log("Received data:", result);  // Log received data
                    setData(result.data);
                    setLoading(false);
                } catch (err) {
                    console.error("Error occurred:", err);  // Log any error
                    setError(err);
                    setLoading(false);
                }
            };
            await fetchData();
        }, []);
    
        if (loading) return <Text>Loading images...</Text>;
        if (error) return <Text>Error: {error.message}</Text>;
    
        return (
            <Fragment>
                <Text>Our beautiful Earth from Imgur:</Text>
                {data && data.map(image => (
                    <Fragment key={image.id}>
                        <Image src={image.link} alt={image.title} />
                        <Text>{image.title}</Text>
                    </Fragment>
                ))}
            </Fragment>
        );
    };
    
    export const run = render(
        <Macro
            app={<App />}
        />
    );
    

    Also I added the images url to the manifest yml

    external:
          images:
            - 'https://i.imgur.com/*'