Quickstart

Add Weavy to Jira using Atlassian Forge

Here's an end-to-end example of adding Weavy to Jira as a Jira Issue Context using Atlassian Forge.

These code snippets include finding who is logged in to Jira, syncing that user to Weavy, and getting an access token.

Have Forge CLI ready, set the parameters to unlock the tutorial, and get started.

Don't have the parameters? Sign up for free or sign in and set up your environment to get the parameters.

 Looking for how to add Weavy to Confluence?

1. Create a Forge app

This example creates a Forge app for Jira; the template used here is a Jira Issue Context.
Open a terminal and create new Forge app
forge create
  • Enter a name for the Forge app (preferably something with Weavy so it's easy to find later)
  • Select Custom UI as Category
  • Select Jira as Product
  • Select jira-issue-context as the template

2. Install the Weavy UIKit

Open up your project in VS Code or similar. The project consists of a server part (src/index.js) and a client part written in React (static/hello-world).

Open up a terminal and go to the /static/hello-world folder. Install the Weavy UI kit.

Run this in the /static/hello-world folder
npm install @weavy/uikit-web

3. Modify index.js

Syncing users and getting the access token should always be done from the server side. Forge uses a bridge that allows you to define backend functions that can be called from the front end.

Copy and paste into index.js, found in the /src folder
import Resolver from '@forge/resolver';
import api, { fetch, route } from '@forge/api';

const resolver = new Resolver();

let _tokens = [];
const apiKey = "********************";
const backend = "WEAVY_URL";

resolver.define("token", async (req, res) => {
    // get jira user's account id
    const accountId = req.context.accountId;

    // check if we already have a token stored for the user or if we need to refresh the token
    if ((!req.payload.refresh || req.payload.refresh === "false") && _tokens.find((t) => t.accountId === accountId)) {
        res.json({ access_token: _tokens.find((t) => t.accountId === accountId).access_token });
    } else {
        // get an access token from weavy backend
        let response = await fetch(`${backend}/api/users/${accountId}/tokens`, {
            method: 'POST',
            headers: {
                'content-type': 'application/json',
                'Authorization': `Bearer ${apiKey}`
            },
            body: JSON.stringify({ expires_in: 3600 })
        });

        if (response.ok) {
            let data = await response.json();
            // store the access token so we don't need to call the api every time (unless we need to refresh the token)
            _tokens = [..._tokens.filter((t) => t.accountId !== accountId), { accountId: accountId, access_token: data.access_token }];
            return data.access_token;
        } else {
            return ({ message: "Could not get access token from server!", accountId: accountId, error: response })
        }
    }
});

resolver.define("syncUser", async (req, res) => {
    // get jira user data
    const userResponse = await api.asApp().requestJira(route`/rest/api/3/user?accountId=${req.context.accountId}`, {
        headers: {
        'Accept': 'application/json'
        }
    });

    var user = await userResponse.json();
  
    // user's account id - we will use this as the 'uid' when syncing the user to Weavy
    let uid = user.accountId;

    // user's name
    let name = user.displayName;

    // sync the user to Weavy
    let response = await fetch(`${backend}/api/users/${uid}`, {
        method: 'PUT',
        headers: {
            'content-type': 'application/json',
            'Authorization': `Bearer ${apiKey}`
        },
        body: JSON.stringify({ name: name })
    });
     
    return uid;

});

resolver.define("getUniqueId", async (req, res) => {
  // get contextual id from jira content  
  return `${req.payload.type}--jira-issue-${req.context.extension.issue.id}`;
});

export const handler = resolver.getDefinitions();
  • Line 10 - endpoint to get an access token for the user in Weavy
  • Line 39 - endpoint to add user information to Weavy based on who is logged into the Atlassian account
  • Line 69 - endpoint to get a unique ID for the building block, making it contextual

4. Modify app.js

The client part is in the App.js file where we add the Weavy building block.

If you want to test a different building block, change the selected block in the parameter input in the header.

Copy and paste into app.js, found in the /static/hello-world/src folder
import React, { useEffect, useState } from 'react';
import { invoke } from '@forge/bridge';
import { Weavy } from "@weavy/uikit-web";

function App() {  
  const [loaded, setLoaded] = useState(false);
  const [uid, setUid] = useState(null);

  useEffect(() => {
    const init = async () => {      
      const weavy = new Weavy();
      weavy.url = "WEAVY_URL";
      weavy.tokenFactory = async (refresh) => {
        return await invoke('token', { refresh: refresh });
      };

      // sync user
      await invoke('syncUser', {});  

       // get a unique id for the app based on jira issue
      let uniqueId = await invoke('getUniqueId', { type: 'chat'});  
      setUid(uniqueId );

      setLoaded(true);      
    }

    init();
  }, []);

  if (!loaded) {
    return 'Loading...';
  }
  
  return (
    <div style={{height: '500px', overflow: 'auto'}}>                
      {uid && 
        <wy-chat uid={uid}></wy-chat>      
      }     
    </div>
  );
}

export default App;
  • Line 21 - get a unique ID for the app based on Confluence page content
  • Line 37 - use that UID when rendering the building block, making it contextual

5. Set runtime version

For Weavy to work with Forge at this moment, we need to use the Forge runtime legacy version, to do so you need to set app.runtime.name to sandbox
Set runtime version
app:
  id: .....
  runtime:
    name: sandbox
Please update if it says anything other than sandbox for the runtime.

6. Modify manifest.yml

Forge requires some configuration in the manifest.yml for the app to have the correct permissions. 
Add the following at the end of the current manifest.yml file
permissions:
  external:
    frames: 
      - "*.weavy.io"
    scripts:
      - "*.weavy.io"
    images:
      - "*.weavy.io"
    fetch:
      backend:
        - "*.weavy.io"
        - wss://*.weavy.io
      client:
        - "*.weavy.io"
        - wss://*.weavy.io
  content:
    scripts:
      - 'unsafe-hashes'
      - 'unsafe-eval'
      - 'unsafe-inline'
      - 'blob:'
    styles:
      - 'unsafe-inline'
  scopes:
    - read:jira-user

7. Build and Deploy

First, we need to build the React project. Go to /static/hello-world folder and run:
Run in the /static/hello-world folder
npm run build

If you make any changes to the React part, you must build every time before you re-deploy the Forge app.

The next step is to deploy the package to Atlassian. Go to the root folder of the project and run:

Run in the root folder of your project
forge deploy

When successfully deployed, the last part is to install it - if the Jira Issue Context is already installed add --upgrade to the install command. Select Jira when asked where to install it. Then enter your Atlassian URL - for example your-domain.atlassian.net.

forge install

8. Take it for a test run

Navigate to the Jira site - your-domain.atlassian.net - and verify that the app is working.

The jira-issue-context can be tested by creating a new issue (or selecting an existing one). The Weavy building block should be visible in the right-hand panel.

Unlock the tutorial with your Weavy API key.