Is there a way I can have an 'init' function in a Forge app?

I am creating an experimental confluence app with forge where multiple components on multiple pages within a space have to interact. They will exchange (limited amounts of) data via the storage API.

It would be nice if I can somehow have a function run when the app loads for the first time. That way, I can have a single place where I make sure some basics are set up in app storage. As it is now, every component render function calls a ‘storage init’ function which checks if init is necessary, but it is still overkill.

Is such a ‘app-global’ init function possible?

Hi @GerbenWierda ,

When you say “when the app loads for the first time”, I think it might be more appropriate to think in terms of a per-tenant context here so you should be able to register for installation events. Here is some code to get you started:

manifest.yml:

modules:
  macro:
    - key: forge-install-event-demo-hello-world
      function: main
      title: Forge Install Event Demo
      description: Forge Install Event Demo
  trigger:
    - key: my-installation-handler
      events:
        - avi:forge:installed:app
      function: installation-func
  function:
    - key: main
      handler: index.run
    - key: installation-func
      handler: index.onInstallation
app:
  id: ari:cloud:ecosystem::app/...

index.jsx:

import ForgeUI, { render, Fragment, Macro, Text } from "@forge/ui";

const App = () => {
  return (
    <Fragment>
      <Text>Hello world!</Text>
    </Fragment>
  );
};

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

export const onInstallation = (event) => {
  console.log(`Oh wow, I've been installed:`);
  console.log(event);
}

Also see the Lifecycle events documentation.

Regards,
Dugald

1 Like

I do not get events when I try this.

  trigger:
    - key: dl-spike-one-trigger-appinstalled
      function: dl-trigger-appinstalled
      events:
        - avi:forge:installed:app
        - avi:forge:upgraded:app

  function:
    - key: dl-trigger-appinstalled
      handler: index.dl_handleTriggerAppInstalled

permissions:
  scopes:
    - read:confluence-content.summary
    - read:confluence-content.all
    - read:page:confluence
    - storage:app
    - read:space:confluence

export async function dl_handleTriggerAppInstalled( event, context) {
  console.log( `dl_handleTriggerAppInstalled`);
  console.log(`All info about my context: ${JSON.stringify(context, null, 2)}`);
  console.log(`All info about my event: ${JSON.stringify(event, null, 2)}`);
};

Not on logging in and loading anything from a space for the first time (which is what I am looking for and which I don't think this does), nor on `forge deploy` and `forge install --upgrade`

My app uses storage and before any component does anything (rendering, macro config) I want to run some logic to make sure the data in Storage is OK. A I understand it, the app storage scope is per-space, so I need an event per-space.

Hi @GerbenWierda ,

I tested my app and did get the avi:forge:installed:app event after the app was installed in a site since I was able to see my console.log(Oh wow, I’ve been installed:) log after installing by running forge logs. Did you try testing if you get the avi:forge:installed:app event after running forge install rather than forge install --upgrade?

The app gets storage scoped to it’s installation rather than a separate storage area per space.

Regards,
Dugald

Indeed. I had to uninstall/install the app to see these console messages. I had expected forge install -upgrade to have the same effect.

Thank you.

By the way, the event part of the result of that trigger is funny when printed via JSON.stringify. Fragment:

INFO    2023-03-23T09:39:45.923Z babd3d21-bb5a-4b13-851d-dbbe21c597a1 All info about my event: {
  "id": "e8e95feb-77de-44e8-84f7-76013a45e564",
  "context": {
    "0": "a",
    "1": "r",
    "2": "i",
    "3": ":",
    "4": "c",
    "5": "l",
    "6": "o",
    "7": "u",
    "8": "d",
    "9": ":",
    "10": "c",
    "11": "o",
    "12": "n",
    "13": "f",
    "14": "l",
    "15": "u",
    "16": "e",
    "17": "n",
    "18": "c",
    "19": "e",
    "20": ":",
    "21": ":",
    "22": "s",
    "23": "i",
    "24": "t",
    "25": "e",
    "26": "/",
    "27": "f",
    "28": "a",
    "29": "4",
    etc. ...

What I also found surprising was that Storage survived an app uninstall/install cycle.

Hey @GerbenWierda ,
regarding the persistence of Storage after app uninstall see this answer: How do I delete all forge storage data for an instance - #2 by rmassaioli

I already have this function triggered by a button:

const deleteAppStorage = async () => {
  console.log( `deleteAppStorage`);
  const myDict = await storage.query()
        .limit( 20)
        .getMany();
//  console.log(`deleteAppStorage: All info about my storage: ${JSON.stringify(myDict, null, 2)}`);
  myDict.results.forEach(function (i) { console.log(`deleting ${i.key}`); storage.delete(i.key);});
  console.log( `deleteAppStorage will return`);
}

This one deletes up to 20 entries (I do not have that much) but could be extended to a loop that runs until the query returns nothing anymore.