見出し画像

ForgeアプリでAtlassianAPIを呼び出す(続き)


はじめに

作成したForgeアプリケーションでアトラシアンのAPIを呼び出してみましょう!


前提条件

以下の記事の作業が完了していることを前提として進めております。





APIレスポンスをアプリのUIに表示する

まずは前回のおさらいをしましょう。
Forge上でJira Cloud APIを実行し、ログに出力するまでができていたと思います。ここまではsrc/resolvers/index.jsを編集してきました。今回はsrc/frontend/index.jsxを変更していきたいと思います。


src/resolvers/index.jsを編集する

import Resolver from '@forge/resolver';
import api, { route } from "@forge/api";


const resolver = new Resolver();

resolver.define('getText', async (req) => {
  const issueKey = req.context.extension.issue.key;
  const response = await api.asUser().requestJira(route`/rest/api/3/issue/${issueKey}`, {
    headers: {
      'Accept': 'application/json'
    }
  });
  
  console.log(`Response: ${response.status} ${response.statusText}`);
  console.log(await response.json());
  return 'Hello, world!';
});

export const handler = resolver.getDefinitions();

上記ではsrc/frontend/index.jsxに返却している値は文字列の'Hello, world!'です。
皆さんがアプリを読み込んだ時に2つのHello, world!が表示されているはずです。このHello, world!をAPIリクエストで取得した値に変更することが今回の目的です。


まず最初にどのAPIレスポンスを取得する必要があるか検討してみましょう。
今回は比較的取得が簡単なSummaryを取得してみたいと思います。


まずはGetIssueAPIをリクエストしたときにどんなデータが返却されるのか確認しましょう
公式ドキュメントを確認することですぐにわかります。

{
  "fields": {
    "watcher": {
      "isWatching": false,
      "self": "https://your-domain.atlassian.net/rest/api/2/issue/EX-1/watchers",
      "watchCount": 1
    },
    "attachment": [
      {
        "author": {
          "accountId": "5b10a2844c20165700ede21g",
          "accountType": "atlassian",
          "active": false,
          "avatarUrls": {
            "16x16": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=16&s=16",
            "24x24": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=24&s=24",
            "32x32": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=32&s=32",
            "48x48": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=48&s=48"
          },
          "displayName": "Mia Krystof",
          "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g"
        },
        "content": "https://your-domain.atlassian.net/jira/rest/api/3/attachment/content/10001",
        "created": "2023-06-24T19:24:50.000+0000",
        "filename": "debuglog.txt",
        "id": 10001,
        "mimeType": "text/plain",
        "self": "https://your-domain.atlassian.net/rest/api/2/attachments/10001",
        "size": 2460
      }
    ],
    "sub-tasks": [
      {
        "id": "10000",
        "outwardIssue": {
          "fields": {
            "status": {
              "iconUrl": "https://your-domain.atlassian.net/images/icons/statuses/open.png",
              "name": "Open"
            }
          },
          "id": "10003",
          "key": "ED-2",
          "self": "https://your-domain.atlassian.net/rest/api/2/issue/ED-2"
        },
        "type": {
          "id": "10000",
          "inward": "Parent",
          "outward": "Sub-task"
        }
      }
    ],
    "description": "Main order flow broken",
    "project": {
      "avatarUrls": {
        "16x16": "https://your-domain.atlassian.net/secure/projectavatar?size=xsmall&pid=10000",
        "24x24": "https://your-domain.atlassian.net/secure/projectavatar?size=small&pid=10000",
        "32x32": "https://your-domain.atlassian.net/secure/projectavatar?size=medium&pid=10000",
        "48x48": "https://your-domain.atlassian.net/secure/projectavatar?size=large&pid=10000"
      },
      "id": "10000",
      "insight": {
        "lastIssueUpdateTime": "2021-04-22T05:37:05.000+0000",
        "totalIssueCount": 100
      },
      "key": "EX",
      "name": "Example",
      "projectCategory": {
        "description": "First Project Category",
        "id": "10000",
        "name": "FIRST",
        "self": "https://your-domain.atlassian.net/rest/api/2/projectCategory/10000"
      },
      "self": "https://your-domain.atlassian.net/rest/api/2/project/EX",
      "simplified": false,
      "style": "classic"
    },
    "comment": [
      {
        "author": {
          "accountId": "5b10a2844c20165700ede21g",
          "active": false,
          "displayName": "Mia Krystof",
          "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g"
        },
        "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
        "created": "2021-01-17T12:34:00.000+0000",
        "id": "10000",
        "self": "https://your-domain.atlassian.net/rest/api/2/issue/10010/comment/10000",
        "updateAuthor": {
          "accountId": "5b10a2844c20165700ede21g",
          "active": false,
          "displayName": "Mia Krystof",
          "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g"
        },
        "updated": "2021-01-18T23:45:00.000+0000",
        "visibility": {
          "identifier": "Administrators",
          "type": "role",
          "value": "Administrators"
        }
      }
    ],
    "issuelinks": [
      {
        "id": "10001",
        "outwardIssue": {
          "fields": {
            "status": {
              "iconUrl": "https://your-domain.atlassian.net/images/icons/statuses/open.png",
              "name": "Open"
            }
          },
          "id": "10004L",
          "key": "PR-2",
          "self": "https://your-domain.atlassian.net/rest/api/2/issue/PR-2"
        },
        "type": {
          "id": "10000",
          "inward": "depends on",
          "outward": "is depended by"
        }
      },
      {
        "id": "10002",
        "inwardIssue": {
          "fields": {
            "status": {
              "iconUrl": "https://your-domain.atlassian.net/images/icons/statuses/open.png",
              "name": "Open"
            }
          },
          "id": "10004",
          "key": "PR-3",
          "self": "https://your-domain.atlassian.net/rest/api/2/issue/PR-3"
        },
        "type": {
          "id": "10000",
          "inward": "depends on",
          "outward": "is depended by"
        }
      }
    ],
    "worklog": [
      {
        "author": {
          "accountId": "5b10a2844c20165700ede21g",
          "active": false,
          "displayName": "Mia Krystof",
          "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g"
        },
        "comment": "I did some work here.",
        "id": "100028",
        "issueId": "10002",
        "self": "https://your-domain.atlassian.net/rest/api/2/issue/10010/worklog/10000",
        "started": "2021-01-17T12:34:00.000+0000",
        "timeSpent": "3h 20m",
        "timeSpentSeconds": 12000,
        "updateAuthor": {
          "accountId": "5b10a2844c20165700ede21g",
          "active": false,
          "displayName": "Mia Krystof",
          "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g"
        },
        "updated": "2021-01-18T23:45:00.000+0000",
        "visibility": {
          "identifier": "276f955c-63d7-42c8-9520-92d01dca0625",
          "type": "group",
          "value": "jira-developers"
        }
      }
    ],
    "timetracking": {
      "originalEstimate": "10m",
      "originalEstimateSeconds": 600,
      "remainingEstimate": "3m",
      "remainingEstimateSeconds": 200,
      "timeSpent": "6m",
      "timeSpentSeconds": 400
    }
  },
  "id": "10002",
  "key": "ED-1",
  "self": "https://your-domain.atlassian.net/rest/api/2/issue/10002"
}

こんな感じのレスポンスが返却されているんだなということはわかりましたね。


それでは実際に今回の目的であるSummaryを取得してみようと思います。
src/frontend/index.jsxを以下のように修正してください

import Resolver from '@forge/resolver';
import api, { route } from "@forge/api";


const resolver = new Resolver();

resolver.define('getText', async (req) => {
  console.log(req);
  console.log(req.context.extension.issue.key);
  const issueKey = req.context.extension.issue.key;
  const response = await api.asUser().requestJira(route`/rest/api/3/issue/${issueKey}`, {
    headers: {
      'Accept': 'application/json'
    }
  });
  
  console.log(`Response: ${response.status} ${response.statusText}`);
  const resData = await response.json();
  const summary = resData.fields.summary;
  
  return summary;
});

export const handler = resolver.getDefinitions();


tunnelをターミナルで実行しアプリを読み込むと以下のようにアプリの表示内容が変わるはずです。


Testはチケットの要約です。


これでアプリ画面にAPIの結果が表示されるようになりました。

応用でいろんな内容が表示できそうですね。

皆さんも是非試してみてください。

いいなと思ったら応援しよう!