Allow custom issue type icons/Project avatar to be displayed with Forge apps

XMLWordPrintable

      Performing a request to the Get avatar image by ID https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-avatars/#api-rest-api-3-universal-avatar-view-type-type-avatar-id-get can be can be accessed anonymously when performing the search for default icons. However, using and displaying custom issue type icons/project avatars needs to have authentication.

      With Forge applications, performing the request encounters a 403 error. Therefore, performing the same authentication for custom issue type icons/project avatars as with the default ones icons would be beneficial, one possibility would be to use directly the icon URL:

      Avatar appearance='square' size='small' src={type.icon}/>

      Workaround

      The issue is that browser can’t match api.atlassian.com domain with document.URL and so doesn’t send authentication information when requesting custom avatars.

      We can convert the URL and replace api.atlassian.com with the siteURL and then it works as the browser can match and include authentication. Here’s and example code.

      import React, { useEffect, useState } from 'react';
      import ForgeReconciler, { Text, Image, useProductContext } from '@forge/react';
      import { requestJira } from '@forge/bridge';
      const convertIconUrl = (iconUrl, siteUrl) => {
        const match = iconUrl.match(/https:\/\/api\.atlassian\.com\/ex\/jira\/[^/]+(\/.*)/);
        if (!match) return iconUrl;
        return `${siteUrl}${match[1]}`;
      };
      const getIssueDetails = async (issueKey) => {
        const response = await requestJira(`/rest/api/3/issue/${issueKey}`);
        const data = await response.json();
        return {
          key: data.key,
          summary: data.fields.summary,
          iconUrl: data.fields.issuetype.iconUrl,
        };
      };
      const App = () => {
        const [data, setData] = useState(null);
        const context = useProductContext();
        useEffect(() => {
          if (!context) return;
          const issueKey = context.extension.issue.key;
          const siteUrl = context.siteUrl;
          getIssueDetails(issueKey).then((issue) => {
            setData({ ...issue, iconUrl: convertIconUrl(issue.iconUrl, siteUrl) });
          });
        }, [context]);
        return (
          <>
            {data ? (
              <>
                <Image src={data.iconUrl} alt={`${data.key} issue type icon`} />
                <Text>{data.key}</Text>
                <Text>{data.summary}</Text>
              </>
            ) : (
              <Text>Loading...</Text>
            )}
          </>
        );
      };
      

              Assignee:
              Suyash Kumar Tiwari
              Reporter:
              Guilherme Bueno (Inactive)
              Votes:
              55 Vote for this issue
              Watchers:
              30 Start watching this issue

                Created:
                Updated: