12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Slackの次世代プラットフォームを使ってJiraと連携するアプリを作る

Last updated at Posted at 2022-12-15

はじめに

KWCのSRE部に所属している、@kumico_kwcです。
この記事はKWC Advent Calendar 2022の9日目の投稿となります。本稿では、現在オープンベータとして提供されているSlackの次世代プラットフォームを使ったSlackアプリ開発を紹介します。

Slackの次世代プラットフォーム

SlackのマネージドインフラストラクチャとDenoによって、従来のSlackアプリ開発とは全く違う手法を提供してくれるプラットフォームです。Slack CLISDKを使うのですが、開発からデプロイ、アプリの設定まで一つのプラットフォームで一貫して行えるので、作業環境とSlackやブラウザを行ったり来たりしないでよくなります。また、デプロイ先に何を採用するか悩まなくてもよいので、コードを書くことに集中することができます。

次世代プラットフォームの詳細については、公式ドキュメントと以下の解説記事が非常に参考になりました。

SlackからJira Issueを作成できるアプリを作る

今回は、「JiraのIssueを作成できる」アプリを作りました。以下の順番で処理が進んでいくアプリになります。

  1. Link triggerで起動
  2. フォームの呼び出し
  3. フォームにJiraボードのID、Issueの要約・説明を入力
  4. 送信
  5. メッセージを返して終了

Functions、Workflows、Triggers

次世代プラットフォームでは、Slackアプリは主に3つのコンポーネントで構成されます。

Functions

Functionsは定義された入力に基づいてデータを受け取り、何らかの処理を行ってから、定義された出力を返します。Functionsは、組み込みFunctionsカスタムFunctionsの2つに分かれます。今回は、4,5のフォームから受け取ったデータをもとに依頼を送信する処理と、送信した後に投稿元のチャンネルにメッセージを返す処理までをカスタムFunctionとして実装しました。

KWCでは、jira-gatwayというJiraを外部から扱いやすくするためのエンドポイントを社内用で開発しています。以下のコードに出てくるJiraGatewayという単語はそのエンドポイントに関するものになります。

import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";

// 実装するFunctionの入出力を定義している
export const JiraGatewayFunction = DefineFunction({
  callback_id: "jiragateway-function",
  title: "Create Jira Issue",
  description: "Create Jira Issue",
  source_file: "functions/function.ts",
  // フォームからstring型のproject, summary, descriptionを受け取る
  input_parameters: {
    properties: {
      project: {
        type: Schema.types.string,
      },
      summary: {
        type: Schema.types.string,
      },
      description: {
        type: Schema.types.string,
      },
    },
    required: ["project", "summary", "description"],
  },
  output_parameters: {
    properties: {
      message: {
        type: Schema.types.string,
      },
    },
    required: ["message"],
  },
});

// 外部APIにデータを送信する箇所と出力メッセージを実装
export default SlackFunction(
  JiraGatewayFunction,
  async ({ inputs, env }) => {
    const data = {
      project: inputs.project,
      summary: inputs.summary,
      desription: inputs.description,
    };
    const headers = {
      "X-API-KEY": env.X_API_KEY,
      "Content-Type": "application/json",
    };
    const endpoint = "https://api.example.net";
    const response = await fetch(endpoint, {
      method: "POST",
      body: JSON.stringify(data),
      headers: headers,
    });
    if (!response.ok) {
      return {
        outputs: {
          message: "Failed to create Jira Issue!",
        },
      };
    }
    return {
      outputs: {
        message: "Created Jira Issue!",
      },
    };
  },
);

Workflows

Functionsを束ねたものがWorkflowsです。組み込みのFunctionsやカスタムFunctionsをWorkflow内でつなげていくことで、アプリを構築することができます。後述するTriggersからWorkflowsを起動するために、Triggersの出力を受け取るための入力を定義する必要があります。アプリにはn個のWorkflowを定義することができますが、今回は実装する機能が少ないので、ひとつのWorkflowで完結しています。

import { DefineWorkflow, Schema } from "deno-slack-sdk/mod.ts";
import { JiraGatewayFunction } from "../functions/function.ts";

// Triggersから値を受け取るための入力を定義
const JiraGatewayWorkflow = DefineWorkflow({
  callback_id: "jiragateway_workflow",
  title: "create jira issue",
  description: "create jira issue",
  input_parameters: {
    properties: {
      channel_id: {
        type: Schema.slack.types.channel_id,
      },
      interactivity: {
        type: Schema.slack.types.interactivity,
      },
    },
    required: ["channel_id", "interactivity"],
  },
});

// フォームを展開するための組み込みfunctionを追加
const JiraGatewayForm = JiraGatewayWorkflow.addStep(
  Schema.slack.functions.OpenForm,
  {
    title: "Create Jira Issue",
    interactivity: JiraGatewayWorkflow.inputs.interactivity,
    submit_label: "Create Jira Issue",
    fields: {
      elements: [{
        name: "project",
        title: "Project",
        type: Schema.types.string,
        description: "Select the Jira Project Board",
        enum: [
          "SRE team",
          "Backend team",
        ],
        choices: [
          { value: "SRE", title: "SRE" },
          { value: "BACK", title: "Backend" },
        ],
        default: "SRE",
      }, {
        name: "summary",
        title: "Summary",
        type: Schema.types.string,
      }, {
        name: "description",
        title: "Description",
        type: Schema.types.string,
        long: true,
      }],
      required: ["project", "summary", "description"],
    },
  },
);

// 上記で実装したカスタムfunction JiraGatewayFunctionを追加
const requestJiraGateway = JiraGatewayWorkflow.addStep(
  JiraGatewayFunction,
  {
    project: JiraGatewayForm.outputs.fields.project,
    summary: JiraGatewayForm.outputs.fields.summary,
    description: JiraGatewayForm.outputs.fields.description,
  },
);

// 投稿元チャンネルにメッセージを出力する組み込みfunctionを追加
JiraGatewayWorkflow.addStep(Schema.slack.functions.SendMessage, {
  channel_id: JiraGatewayWorkflow.inputs.channel_id,
  message: requestJiraGateway.outputs.message,
});

export default JiraGatewayWorkflow;

Triggers

Workflowsを呼び出すために必要なのが、Triggersです。Triggersにはメンションや、リアクションなど、Slackのイベントに関連したものや、Cronのように定期的に実行されるものなどが提供されています。今回は、次世代プラットフォームで導入されたLink Triggersを用いて、Workflowsを起動します。

import { Trigger } from "deno-slack-api/types.ts";
import JiraGatewayWorkflow from "../workflows/workflow.ts";

// 上記のJiraGatewayWorkflowを呼び出すためのTrigger
const JiraGatewayTrigger: Trigger<typeof JiraGatewayWorkflow.definition> = {
  type: "shortcut",  // <- Link Trigger
  name: "Create Jira Issue",
  workflow: "#/workflows/jiragateway_workflow",
  inputs: {
    interactivity: {
      value: "{{data.interactivity}}", // フォームを呼び出すために必要
    },
    channel_id: {
      value: "{{data.channel_id}}", // チャンネルにメッセージを送信するために必要
    },
  },
};

export default JiraGatewayTrigger;

Slack CLI

従来であれば、管理画面からアプリを登録する必要がありましたが、次世代プラットフォームではすべて、slackコマンドだけで行うことができます。アプリの権限やアイコンの設定もすべてマニフェスト(manifest.tsに記述)で管理し、マニフェストに基づいてslackコマンドがデプロイまで行ってくれます。

import { Manifest } from "deno-slack-sdk/mod.ts";
import JiraGatewayWorkflow from "./workflows/workflow.ts";

export default Manifest({
  name: "jiragateway",
  description: "create Jira Issue",
  icon: "assets/default_new_app_icon.png",
  workflows: [JiraGatewayWorkflow],
  outgoingDomains: [
    "api.example.net", // 外部と通信するfunctionがある場合は設定する
  ],
  botScopes: [
    "commands", 
    "chat:write",
    "chat:write.public"
  ],
});

デプロイ・ローカル実行

ローカルでアプリを実行する

$ slack run

Slackのクラウドにデプロイする

$ slack deploy

ローカルで実行する場合と、デプロイする場合どちらも初めて実行する場合には、Slackのワークスペースにアプリの登録が行われます。ワークスペースにアプリを登録する権限がない場合でも、CLIから管理者に登録を依頼することができます(便利)。

Triggersの作成

アプリの登録ができたら、以下のコマンドでTriggersを作成します。

$ slack trigger create --trigger-def /path/to/trigger.ts

アプリの呼び出し

今回は、Link Triggerを作成したため、ワークスペースにWorkflowのショートカットを登録するための特別なURLが発行されます。発行されたURLをチャンネルに投稿すると、ショートカットが生成されアプリを呼び出すことができるようになります。
image.png
2回目以降は、チャンネル上部に表示されるワークフローからアプリを呼び出せます。
image.png

おわりに

本稿では、次世代プラットフォームでのSlackアプリケーション開発手順について「JiraのIssueを作成できる」アプリを参考に紹介しました。従来の方法で開発したアプリを、次世代プラットフォームに乗せ換えるのはもう少し様子を見てからでもいいと感じてますが、新しくアプリを作る際は、次世代プラットフォームも選択肢の1つとして入ってくるのかなと思っています。本稿で紹介した内容が読んでくださった方のSlackアプリ開発になにか寄与できれば光栄です。

KWC Advent Calendar 2022も折り返しとなりました。後半も面白い記事が投稿されていきますので、お楽しみください!

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?