Forge tunnel invoke always error:Entry point "resolver" for extension "my-jira-gadget" could not be invoked as it does not exist or does not reference a function or endpoint

This is my manifest:

modules:
  jira:dashboardGadget:
    - key: my-jira-gadget-v1
      title: my-jira-gadget-v1
      description: A dashboard gadget.
      resource: my-html # the resource used to view our dashboardGadget
      edit:
        resource: my-html # the same resource, used to edit our dashboardGadget configuration
      resolver:
        function: handler
  function:
    - key: handler
      handler: index.handler
resources:
  - key: my-html
    path: dist
    tunnel:
      port: 3001
app:
  id: xxxx...
  runtime:
    name: sandbox
    snapshots: false
permissions:
  content:
    styles:
      - unsafe-inline
  scopes:
    - read:jira-work
    - read:jira-user

This is my code(src/index.js):

// // This sample uses Atlassian Forge
// // https://developer.atlassian.com/platform/forge/
import api, { route, authorize } from '@forge/api';
import _Resolver from '@forge/resolver';
const Resolver = _Resolver.default;

const FILTERSEARCH = route`/rest/api/2/filter/search`;
const defaultOptions = {
  headers: {
    Accept: 'application/json',
  },
};

const fetch = async (params) => {
  console.log('Requesting Jira filter search');
  try {
    const { payload, context } = params;
    const options = Object.assign({}, defaultOptions, payload);
    const response = await api.asApp().requestJira(FILTERSEARCH, options);
    if (!response.ok) {
      throw new Error(`Failed to fetch filters: ${response.status} ${response.statusText}`);
    }
    const data = await response.json();
    console.log(`Response: ${response.status} ${response.statusText}`);
    console.log('Data:', data);
    return data;
  } catch (error) {
    console.error(`Backend failed to fetch filters: ${error}`);
  }
};

const resolver = new Resolver();
resolver.define('searchFilters', async (params) => {
  console.log('Searching Jira filters, resolver');
  return await fetch(params);
});

export const handler = resolver.getDefinitions();

When I call invoke(“searchFilters”),Browser always error:

Entry point “resolver” for extension “my-jira-gadget” could not be invoked as it does not exist or does not reference a function or endpoint

Why? Is there anything wrong with manifest.yaml?Can someone help?

node: v18.17.0
@forge/cli: v9.3.0
Mac os 14.2 (23C64)

Another problem: if I change runtime sandbox to nodejs18.x, jira dashboard can’t proxy to localhost:3001, it will show me : connection refused. Why?

“Another problem: if I change runtime sandbox to nodejs18.x, jira dashboard can’t proxy to localhost:3001, it will show me : connection refused. Why?”

This problem,I found that when I upgrade to node 20.9.0,It can be solved; Proxy is ok.

But still error:

Frontend ajax, fetching data:  searchFilters
ajax.ts:9 Frontend ajax, error fetching data:  Error: Entry point "resolver" for extension "my-jira-gadget" could not be invoked as it does not exist or does not reference a function or endpoint
    at invoke (https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/async-forge-ui-iframe-dashboard-gadget.fccd76be.js:38:140621)
    at async https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/async-forge-ui-iframe-dashboard-gadget.fccd76be.js:38:214231

Error: Entry point "resolver" for extension "my-jira-gadget" could not be invoked as it does not exist or does not reference a function or endpoint
    at fe.error (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:153277)
    at Object.<anonymous> (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:161050)
    at JSON.parse (<anonymous>)
    at Te.o (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:160909)
    at Te (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:161062)
    at s.on (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:165722)
    at je (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:165859)
    at https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:171845
    at e.try (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:144329)
    at https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:171642

This is web code:

import { invoke } from '@forge/bridge';

export const ajax = async (apiName: string) => {
  try {
    console.log('Frontend ajax, fetching data: ', apiName);
    const response = await invoke('searchFilters');
    return response;
  } catch (error) {
    console.error('Frontend ajax, error fetching data: ', error);
  }
};

What’s the reason to use the default? And which version are you using? In the version I have (1.5.33), it doesn’t seem to include default as a property (I get a type error if I try).

I use just import Resolver from '@forge/resolver';, so maybe that would be enough for you.

I changed my config of manifest.yaml:

runtime:
name: nodejs18.x

And My version:
@forge/resolver”: “^1.5.33”
Node version:v20.9.0

But if I use import Resolver from ‘@forge/resolver’; It will show:Resolver is not a constructor,then I console.log Resolver。And see the code,I changed the way of import。

This is the code of @forge/resolver(out/index.js) in node_modules:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const api_1 = require("@forge/api");
const isRequestPayload = (request) => {
    return typeof request.payload === 'object' && request.payload.product && request.payload.fetchUrl;
};
const defaultFunctions = {
//...
};
class Resolver {
//...
}
exports.default = Resolver;

I don’t think this is a problem,because I have tried two ways to import。

Finnally there is no error when I excute $node src/index.js,so I think it’s right for my way to import。

But when I run forge tunnel,invoke function also catch a error:

ajax.ts:9 Frontend ajax, error fetching data: Error: Entry point “resolver” for extension “my-jira-gadget” could not be invoked as it does not exist or does not reference a function or endpoint
at invoke (https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/async-forge-ui-iframe-dashboard-gadget.fccd76be.js:38:140621)
at async https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/async-forge-ui-iframe-dashboard-gadget.fccd76be.js:38:214231

I suspect that index.js is not executed at all during forge tunnelling, but I don’t know why; no logs are printed in index.js(I have added some console.log), and the manifest looks correct.

If you look at the definition of the class Resolver (in the out/index.js file you cited), you can see the constructor there:

class Resolver {
    constructor() {
        this.functions = Object.assign({}, defaultFunctions);
    }

You can also see the examples in the resolver documentation. My guess is it is exporting undefined from your index file because of the import. Have you tried running one of the sample apps? They also import as I suggested. The to-do app uses a custom UI with resolvers.

Thanks a lot, but I still haven’t found the reason.

So I changed my approach and did not request from the server side, I need not to configure a solver in manifest.

I use the method ”requestJira“ in @forge/bridge version 2.0+ and directly made API requests in browser, it work。

But it comes another problem:
When I run forge tunnel,it shows me:
For this app to display, you need to allow the app to access Atlassian products on your behalf.

I have already deploy my app to production and share it according to this topic: https://community.developer.atlassian.com/t/allow-the-app-to-access-atlassian-products-on-your-behalf/59730。

and add these permissions into manifest:

permissions:
  content:
    styles:
      - unsafe-inline
  scopes:
    - read:jira-work
    - read:jira-user
    - read:dashboard:jira
    - read:dashboard.property:jira
    - read:filter:jira
    - read:filter.column:jira
    - read:group:jira
    - read:issue:jira
    - read:field:jira
    - read:project:jira

this is my api: ‘/rest/api/3/filter/my’

How to solve this?

My guess would be that since you’re calling it in the frontend, you are acting as the user (rather than asApp() in a resolver) and so need permission for that. The example apps show working examples.