How to split resolvers in multiple files?

Hi,

As time goes by, I have more and more definitions in my src/resolvers/index.js file.
I would like to split them in different files, like storage.js, common.js, jira.js…
But I can’t find a way to implement that.

This is how you are supposed to do for 1 file:

manifest.yml:

modules:
  jira:globalPage:
    - key: my-global-page
      resource: index
      resolver:
        function: my-resolver
      ...
    function:
    - key: my-resolver
      handler: index.handler # 'handler' function in src/index.js

=> src/index.js that only contains

export { handler } from './resolvers';

=> and src/resolvers/index.js

import Resolver from '@forge/resolver';
const resolver = new Resolver();

resolver.define('getJiraText', (req) => {
  console.log(req);
  return 'Hello, Jira world!';
});

resolver.define('getSlackText', (req) => {
  console.log(req);
  return 'Hello, Slack world!';
});

export const handler = resolver.getDefinitions();

Finally in your main code, src/frontend/index.jsx

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

const helloJira = getRunMode(await invoke('getJiraText'));
const helloSlack = getRunMode(await invoke('getSlackText'));

So how to have different files in src/resolvers?

  • I imagine that we should not change manifest.yml each time we want to add another file (because of the change of major version it implies).
  • Another problem is with many definitions coming from different files, the risk is to have same names from different files, and currently you just use ‘invoke’ in your jsx file, and it does not relate to any file or function name.
  • Also having everything in the same file will lead to a big mess (that I hate :smiley: ).

Thank you for your help!

types.ts

import Resolver from '@forge/resolver';

const resolver = new Resolver();

export type ResolverFunction = Parameters<typeof resolver.define>[1];
export type ResolverRequest = Parameters<ResolverFunction>[0];

export enum Lambda {
 GetSlackText = 'getSlackText',
 GetJiraText = 'getSlackText',
}

slack.ts

import Resolver from '@forge/resolver';
import {Lambda, ResolverRequest} from './types'

const getSlackText = (req: ResolverRequest) => {
  console.log(req);
  return 'Hello, Slack world!';
}

export const setDefinitions = (resolver: Resolver) => {
  resolver.define(Lambdas.GetSlackText, getSlackText);
};

jira.ts

import Resolver from '@forge/resolver';
import {Lambda, ResolverRequest} from './types'

const getJiraText = (req: ResolverRequest) => {
  console.log(req);
  return 'Hello, Slack world!';
}

export const setDefinitions = (resolver: Resolver) => {
  resolver.define(Lambdas.GetJiraText, getJiraText);
};

index.ts

import {setDefinitions as setDefinitionsSlack from './api/slack';
import {setDefinitions as setDefinitionsJira from './api/jira';

const resolver = new Resolver();

setDefinitionsSlack(resolver);
setDefinitionsJira(resolver);

export const indexResolver = resolver.getDefinitions();

3 Likes

That’s brilliant @Alexandr ! Thank you so much.

I have updated your solution for those who don’t use TypeScript (yet :wink: )
And also to provide a way of making sure that there are no collision names.

src/resolvers/jira.js

import Resolver from '@forge/resolver';

const getJiraText = (req) => {
  console.log(req);
  return 'Hello, Jira world!';
}

const getText = (req) => {
  console.log(req);
  return 'Hello, world, from Jira!';
}

export const setDefinitions = (resolver, scope) => {
  resolver.define(scope ? scope + '.getJiraText' : 'getJiraText', getJiraText);
  resolver.define(scope ? scope + '.getText' : 'getText', getText);
};

src/resolvers/slack.js

import Resolver from '@forge/resolver';
import {Lambda} from './constants'

const getSlackText = (req) => {
  console.log(req);
  return 'Hello, Slack world!';
}

export const setDefinitions = (resolver, scope) => {
  resolver.define(scope ? scope + '.getSlackText' : 'getSlackText', getSlackText);
};

src/resolvers/index.js

import Resolver from '@forge/resolver';

import {setDefinitions as setDefinitionsSlack} from './slack';
import {setDefinitions as setDefinitionsJira} from './jira';

const resolver = new Resolver();

setDefinitionsSlack(resolver);
setDefinitionsJira(resolver, 'Jira');

export const handler = resolver.getDefinitions();

src/index.js

export { handler } from './resolvers';

src/frontend/index.jsx

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

invoke('Jira.getJiraText', { example: 'my-invoke-variable' }).then(setData);
invoke('Jira.getText', { example: 'my-invoke-variable' }).then(setData2);
invoke('getSlackText', { example: 'my-invoke-variable' }).then(setData3);

Here’s my solution.

1 Like

Yes @rcsr , thank you, your solution is also really interesting because it gives the ability to have distinct handlers for each module, and then avoid to have the whole code if you don’t need it. The bigger the app is, the more useful it is.

src/resolvers/index.js would become for example:

import Resolver from '@forge/resolver';

import {setDefinitions as setDefinitionsSlack} from './slack';
import {setDefinitions as setDefinitionsJira} from './jira';

const resolver1 = new Resolver();
setDefinitionsJira(resolver1, 'Jira');

const resolver2 = new Resolver();
setDefinitionsSlack(resolver2);
setDefinitionsJira(resolver2, 'Jira');

export const handler1 = resolver1.getDefinitions();
export const handler2 = resolver2.getDefinitions();

And in manifest.yml:

modules:
  jira:globalPage:
    - key: my-global-page
      resource: index
      resolver:
        function: my-resolver1
      ...
  jira:globalSettings:
    - key: my-settings-page
      resource: settings
      resolver:
        function: my-resolver2
      ...
    function:
    - key: my-resolver1
      handler: index.handler1
    - key: my-resolver2
      handler: index.handler2