How to retrieve the actual Jira instance I am in?

Perhaps it is the easiest thing to do but I stumbled into this. Looked inside context, issue data, UI Kit hooks, Javascript API and came empty-handed.

I can get some Ids but I really need the domain like https://myinstance.atlassian.net there.

Maybe there is a way to access the current URL?

1 Like

Hello @RichardVencu.

You can get the instance URL from the context using “xdm_e” as stated in Context guide - Context parameters (atlassian.com) .

Also, you can get the URL/URI from the HTTP request itself inside the controller of the framework you use (e.g. Connect for Express or Connect for Spring ) and pass it to the UI model of the templating language you use.

Hope this can help you.

This is a forge app, I don’t think the xdm_e query string parameter is available given that it does not run in an iframe (like Connect)

We asked about this in the decommissioned Forge Slack channel and got advise from the Forge team how to deduce the site information, though it turned out to be only applicable for Jira and not Confluence at the time (right now you can still apply the Jira approach on Confluence because Forge is not enabled for Confluence only sites).

Unfortunately I’ve misplaced my notes and cannot recall the details right now :man_facepalming: - will reply once more in case I find/recall those.

I do recall our workaround though, which is based on Tim Pettersen’s clever answer to Status URL for monitoring?:

There isn’t a formal status API that I’m aware of, but the AppLinks Manifest end-point is a good URL you can hit for most of our products (JIRA, Confluence, Stash, Bamboo, FishEye and Crucible). If it returns a 200, your server is probably in good shape.

It does not require authentication and is available at:

/rest/applinks/latest/manifest

in all products.

e.g.

https://jira.atlassian.com/rest/applinks/latest/manifest

https://confluence.atlassian.com/rest/applinks/latest/manifest

cheers,

Tim

This still seems to work fine on Atlassian Cloud, though you need to parse the response XML, see e.g.:

Jira
https://utoolity.atlassian.net/rest/applinks/latest/manifest

...
<name>Jira - Utoolity Support</name>
...
<url>https://utoolity.atlassian.net</url>
...

Confluence
https://utoolity.atlassian.net/wiki/rest/applinks/latest/manifest

...
<name>Confluence - Utoolity Support</name>
...
<url>https://utoolity.atlassian.net/wiki</url>
...
5 Likes

Great! Thank you, this works well:

getInstance = async () => {
    const response = await api
      .requestJira(`/rest/applinks/latest/manifest`);
    const results = await response.text();
    const jurl = /\<url\>(.*)\<\/url\>/;
    return jurl.exec(results)[1];
  }
3 Likes

I can jump in on that - the Jira only, slightly more explicit approach uses the baseUrl entry from /rest/api/3/serverInfo, e.g.:

export const getEnvironmentBaseUrl = async () => {
  let result;
  // NOTE/KLUDGE: We have no proper approach for Confluence so far, but can currently rely on a Confluence instance always having a
  // corresponding Jira instance, hence using the respective Jira pproach only for starters:
  //const hostProduct = getHostProduct();
  const hostProduct = "jira";
  if (hostProduct === "jira") {
    result = await api
      .asApp()
      .requestJira("/rest/api/3/serverInfo");
    const serverInfo = await result.json();
    result = serverInfo.baseUrl;
  }
  // TODO: Add Confluence specific version, once available
  return result;
}

(left the preparation and notes on the Confluence topic, easy to shorten for a Jira only situation, of course)

5 Likes

Hey @RichardVencu,

Would you mind describing a little about what you need this for? That way I can take it back to my team. We are still working on Forge context so any input is valuable :slight_smile:

Yes. I want to send to a backend system information about work logs. But the same organization might use several instances of jira cloud so I want to identify the remote worklog (i.e. Jira worklog) by: instance, issue, worklog.id so I can send back API requests to alter these work logs if the backend system finds incompatibilities with org rules.

I assume that this system would be your own external API? Could you use another identifier like installationContext for that? I’m curious to understand why it needs to be the site URL itself given that it is not required for calling the APIs from Forge?

Because I needed to go async at my external app, I have to return some confirmation after I finish processing the job. I am returning this via API directly to Jira cloud (skipping the Forge app completely) from my own external app, hence I need the full URL.

2 Likes

Another as of yet undocumented and thus possibly unsupported option:

I recently noticed that the GraphQL Gateway provides a tenantContexts GraphQL query which can be used to infer the site URL from the cloudId and vice versa, and it works for both Jira and Confluence:

A Jira or Confluence cloud instance, such as hello.atlassian.net has a backing cloud ID such as 0ee6b491-5425-4f19-a71e-2486784ad694

This field allows you to look up the cloud IDs or host names of tenanted applications such as Jira or Confluence.

You MUST provide a list of cloud IDs or a list of host names to look up but not both otherwise an error will be returned

Here’s an example query:

query tenantContexts($cloudIds: [ID!]) {
  tenantContexts(cloudIds: $cloudIds) {
    cloudId
    hostName
  }
}

Providing a cloud ID from the useProductContext() hook yields e.g.:

{
  "data": {
    "tenantContexts": [
      {
        "cloudId": "2ef567f6-17c6-421f-a6cc-2fb075be8be6",
        "hostName": "utoolity.atlassian.net"
      }
    ]
  },
  ...
}

@danielwinterw - I realize that the GraphQL API is not yet officially supported, but given Forge uses it heavily and the product fetch API just gaining a (so far undocumented) requestGraph() method, can you maybe sneak peek indicate whether this might be a stable approach already? :slight_smile:

2 Likes

This is actually a pretty awesome find. Using a graphql client like “graphql-request” it is possible to fetch an instances hostName by providing its cloudId which is available via forgeContext.

import {request, gql} from 'graphql-request';

const query = gql`
            query hostNameForCloudId {
                tenantContexts(cloudIds: ["${forgeContext.cloudId}"]) {
                    cloudId
                    hostName
               }
            }
            `;
const {tenantContexts} = await request("https://api.atlassian.com/graphql", query);
// tenantContexts contains cloudId and hostName
4 Likes

Hi @JulianWolf ,

I tried your code and got an error:

{
    "response": {
        "error": "{\"data\":{\"tenantContexts\":[{\"cloudId\":\"xxxx\",\"hostName\":\yyy\"}]},\"extensions\":{\"gateway\":{\"request_id\":\"zzz\",\"crossRegion\":false,\"edgeCrossRegion\":false,\"deprecatedFieldsUsed\":[]}}}",
        "status": 200,
        "headers": {}
    },
    "request"
    : {
        "query": "\n            query hostNameForCloudId {\n                tenantContexts(cloudIds: [\"xxxx"]) {\n                    cloudId\n                    hostNam
        e\n               }\n            }\n            "
    }
}

I placed the xxx, yyy, zzz myself in here.

Very weird that it does not contain any error message.

In postman, everything seems to work:

Do you know what I might be doing wrong?

Thanks.

Kind regards,

Tim

Is there really no simple and official way to get the Jira BaseURL in Forge? :roll_eyes: @danielwinterw

I need to get the baseUrl to create a simple Issue Link to display to end users, which is impossible because the RestAPI returns the “self” link of the issue as https://api.atlassian.com/ex/... - My context is a web trigger.

1 Like

in the end, I got it working through

export const getInstanceName = async (cloudId) => {

  const res = await fetch('https://api.atlassian.com/graphql', {
    method: 'POST',
    header: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        query: `query hostNameForCloudId {
        tenantContexts(cloudIds: ["${cloudId}"]) {
          cloudId
          hostName
        }
      }`
      }
    )
  });

  if (!res.ok) {
    console.error(`getInstanceName - graphQL API error: ${JSON.stringify(res)}`)
    return {};
  }

  let data = await res.json();

  return data.data.tenantContexts[0].hostName;
};

We’re seeing errors since a few days for both endpoints
/rest/api/3/serverInfo

/rest/applinks/latest/manifest

We access this URL to get the hostname of the product from a web trigger. The error is

Invalid URL: /rest/api/3/serverInfo

This API is officially documented here.

This is how we make the call which used to work until a few days ago.

This call is made within the context of a Forge Web Trigger!

const response = await api
        .requestJira(route`/rest/api/3/serverInfo`);
const results = await response.json();
return results.baseUrl;

I tried the graphql approach as well but seems not to work in a web trigger context either.

Any advice from your side? Already opened a support case with Atlassian.

Last time this API returned successfully: 2023-01-12T00:34:43.536Z

Thanks!

1 Like

Hi @LukasGotter ,
We also have this problem. Please, also let us know if you learn anything from your support case.

1 Like

Hi @denizoguz - Good news. The problem seems fixed now. Unfortunately, we don’t have any info from support if they identified the issue or if it was temporary. Will update you here in case of more information. Looking forward to hearing all works fine on your end too. Greetings!

Hi @LukasGotter
Thanks a lot. It is working for us too now. It was something temporary with Forge backend. Because it was working when we call it from a browser.

Yeah, I observed the error for a few days. It’ what you said Temporary :joy: