LoginSignup
0
0

Workato:Confluenceページ内のテーブルの値を取得してSlackへ通知する

Last updated at Posted at 2024-05-01

はじめに

Atlassianのアプリケーションを利用している組織では、プロジェクト管理ツールのJiraと情報共有ツール(エンタープライズWiki)のConfluenceの組み合わせで良く利用されます。

特に、Confluenceは職種(エンジニア・非エンジニア)や役職、部門を問わず、様々なユーザーに利用されますので、組織内の多くのナレッジが蓄積されます(Confluence上に集約されます)。また、多くのナレッジが蓄積されることから、このデータを活用したいという思いや、他部門からのリクエストも出てくるかと思います。

システム管理者としては、構造化すべきデータは、JiraやDB、その他のシステムで構造化して管理してほしいというのが理想(本音)かと思いますが、ユーザーの使い方がシステム管理者の理想通りにならないことはよくあると思います。

また、ユーザーは、自分の要件を満たすことができれば、自分のスキルの範囲で扱えるツール、準備することなく今すぐ使える始められる既に設定済みのツール(手軽で簡単に使えるツール)で対応するものかと思います。

しかし、Confluence上の情報(データ)は基本的には非構造化データであり、構造化データ(例えばJiraのIssue)のように特定のフィールドに値がセットされている(キーバリューでキーを指定して目的のデータを取得できる)わけではないため、Confluenceと連携先のシステムの中間でデータの加工を行う仕組みが必要となります。

また、テーブル(表)のような見た目構造化されている情報であっても、Confluence上は非構造データとして管理されるため、同じくConfluenceと連携先のシステムの中間でデータの加工を行う仕組みが必要となります。

Workatoを利用すると、このような非構造データの加工と連携の仕組みをノーコードとローコードで実装することができます。

PythonコネクタによるHTML解析

Workatoには、ローコードコネクタとしてPythonコネクタ(Python snippets by Workato)が用意されています。

また、Pythonコネクタではlxmlモジュールが利用可能となっており、lxmlモジュールを利用することで、HTMLを簡単に解析し、目的の値にアクセスすることが可能です。

PythonとlxmlによるHTMLスクレイピング

読む方にとっては釈迦に説法的な話になりますが、PythonとlxmlモジュールによるHTML解析(HTMLスクレイピング)には、Python, lxml, DOM, XPath に関する理解が必要になります。

lxmlによるHTMLスクレイピングについては、以下の内容が参考になると思います。

HTMLスクレイピングを進めるにあたっては、DOM(Document Object Model)を理解しておく必要があります。もしHTMLスクレイピングに初めてチャレンジされる方は、まずはDOM操作について理解されることをお勧めします。以下のページが参考になると思います。

また、必須ではありませんが、lxmlによるHTMLスクレイピングでは、XPathが利用できると効率的にスクレイピングできます。とりあえずXPathとは何か、すぐに試してみたいという方は、以下のページが参考になると思います。

なお、上記ではHTMLスクレイピングをすぐに始められる情報を紹介しています。DOMやXPathについて、より深い情報を知りたい方は、MDNや、専門書などのリソースを参照いただければと思います。

次項からは、Workatoでどのように実装を進めていくか説明します。

手順

今回は、毎日1回Confluenceの特定ページ(以下)上にあるテーブルの1行目の情報(赤枠)を取得し、その結果をLookup Tablesにセット(更新)&Slackへ通知するレシピを作成していきます。

image.png

Confluenceではページのデータは、HTML(とConfluenceの独自タグ)で非構造データとして管理されます。非構造データではありますが、幸いフォーマット化されたデータ(HTML)であるため、前述の通りlxmlを使用すれば簡単に目的のデータを取り出すことが可能です。

最終的には次のようなレシピとなりますが、ステップごとに実装手順を解説していきます。

image.png

Step 1

「Scheduer by Workato」コネクタを選択します。

image.png

「New recurring event」を選択します。

image.png

毎日12:00(日本時間)に実行することとします。
次の通り設定します。

  • Time unit: Days
  • Trigger every: 1 (days)
  • Trigger at: 12:00 AM
  • Timezone: Asia/Tokyo

image.png

Step 2

「Confluence」コネクタを選択します。

image.png

「Custom action」を選択します。
Confluenceコネクタには、ページを取得するアクションが標準で存在しないため、Custom actionで対応します。

image.png

次の通り設定します。

  • Action name: 任意の名称
  • Method: GET
  • Path: content/<ConfluenceのページID>?expand=space,body.view,version,container
  • Response type: JSON response

なお、Pathで指定したAPIについては、以下を参照ください。

image.png

Response bodyは、以下のJSONを利用してスキーマを生成します。

image.png

以下のJSONを利用したスキーマの設定にあたっては、以下のページを参考に対応してください。

[
  {
    "control_type": "text",
    "label": "ID",
    "type": "string",
    "name": "id"
  },
  {
    "control_type": "text",
    "label": "Type",
    "type": "string",
    "name": "type"
  },
  {
    "control_type": "text",
    "label": "Status",
    "type": "string",
    "name": "status"
  },
  {
    "control_type": "text",
    "label": "Title",
    "type": "string",
    "name": "title"
  },
  {
    "properties": [
      {
        "control_type": "number",
        "label": "ID",
        "parse_output": "float_conversion",
        "type": "number",
        "name": "id"
      },
      {
        "control_type": "text",
        "label": "Key",
        "type": "string",
        "name": "key"
      },
      {
        "control_type": "text",
        "label": "Name",
        "type": "string",
        "name": "name"
      },
      {
        "control_type": "text",
        "label": "Type",
        "type": "string",
        "name": "type"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Webui",
            "type": "string",
            "name": "webui"
          },
          {
            "control_type": "text",
            "label": "Self",
            "type": "string",
            "name": "self"
          }
        ],
        "label": "Links",
        "type": "object",
        "name": "_links"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Metadata",
            "type": "string",
            "name": "metadata"
          },
          {
            "control_type": "text",
            "label": "Icon",
            "type": "string",
            "name": "icon"
          },
          {
            "control_type": "text",
            "label": "Description",
            "type": "string",
            "name": "description"
          },
          {
            "control_type": "text",
            "label": "Homepage",
            "type": "string",
            "name": "homepage"
          }
        ],
        "label": "Expandable",
        "type": "object",
        "name": "_expandable"
      }
    ],
    "label": "Space",
    "type": "object",
    "name": "space"
  },
  {
    "properties": [
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Type",
            "type": "string",
            "name": "type"
          },
          {
            "control_type": "text",
            "label": "Username",
            "type": "string",
            "name": "username"
          },
          {
            "control_type": "text",
            "label": "User key",
            "type": "string",
            "name": "userKey"
          },
          {
            "properties": [
              {
                "control_type": "text",
                "label": "Path",
                "type": "string",
                "name": "path"
              },
              {
                "control_type": "number",
                "label": "Width",
                "parse_output": "float_conversion",
                "type": "number",
                "name": "width"
              },
              {
                "control_type": "number",
                "label": "Height",
                "parse_output": "float_conversion",
                "type": "number",
                "name": "height"
              },
              {
                "control_type": "text",
                "label": "Is default",
                "render_input": {},
                "parse_output": {},
                "toggle_hint": "Select from option list",
                "toggle_field": {
                  "label": "Is default",
                  "control_type": "text",
                  "toggle_hint": "Use custom value",
                  "type": "boolean",
                  "name": "isDefault"
                },
                "type": "boolean",
                "name": "isDefault"
              }
            ],
            "label": "Profile picture",
            "type": "object",
            "name": "profilePicture"
          },
          {
            "control_type": "text",
            "label": "Display name",
            "type": "string",
            "name": "displayName"
          },
          {
            "properties": [
              {
                "control_type": "text",
                "label": "Self",
                "type": "string",
                "name": "self"
              }
            ],
            "label": "Links",
            "type": "object",
            "name": "_links"
          },
          {
            "properties": [
              {
                "control_type": "text",
                "label": "Status",
                "type": "string",
                "name": "status"
              }
            ],
            "label": "Expandable",
            "type": "object",
            "name": "_expandable"
          }
        ],
        "label": "By",
        "type": "object",
        "name": "by"
      },
      {
        "control_type": "text",
        "label": "When",
        "render_input": "date_time_conversion",
        "parse_output": "date_time_conversion",
        "type": "date_time",
        "name": "when"
      },
      {
        "control_type": "text",
        "label": "Message",
        "type": "string",
        "name": "message"
      },
      {
        "control_type": "number",
        "label": "Number",
        "parse_output": "float_conversion",
        "type": "number",
        "name": "number"
      },
      {
        "control_type": "text",
        "label": "Minor edit",
        "render_input": {},
        "parse_output": {},
        "toggle_hint": "Select from option list",
        "toggle_field": {
          "label": "Minor edit",
          "control_type": "text",
          "toggle_hint": "Use custom value",
          "type": "boolean",
          "name": "minorEdit"
        },
        "type": "boolean",
        "name": "minorEdit"
      },
      {
        "control_type": "text",
        "label": "Hidden",
        "render_input": {},
        "parse_output": {},
        "toggle_hint": "Select from option list",
        "toggle_field": {
          "label": "Hidden",
          "control_type": "text",
          "toggle_hint": "Use custom value",
          "type": "boolean",
          "name": "hidden"
        },
        "type": "boolean",
        "name": "hidden"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Self",
            "type": "string",
            "name": "self"
          }
        ],
        "label": "Links",
        "type": "object",
        "name": "_links"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Content",
            "type": "string",
            "name": "content"
          }
        ],
        "label": "Expandable",
        "type": "object",
        "name": "_expandable"
      }
    ],
    "label": "Version",
    "type": "object",
    "name": "version"
  },
  {
    "properties": [
      {
        "control_type": "number",
        "label": "ID",
        "parse_output": "float_conversion",
        "type": "number",
        "name": "id"
      },
      {
        "control_type": "text",
        "label": "Key",
        "type": "string",
        "name": "key"
      },
      {
        "control_type": "text",
        "label": "Name",
        "type": "string",
        "name": "name"
      },
      {
        "control_type": "text",
        "label": "Type",
        "type": "string",
        "name": "type"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Webui",
            "type": "string",
            "name": "webui"
          },
          {
            "control_type": "text",
            "label": "Self",
            "type": "string",
            "name": "self"
          }
        ],
        "label": "Links",
        "type": "object",
        "name": "_links"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Metadata",
            "type": "string",
            "name": "metadata"
          },
          {
            "control_type": "text",
            "label": "Icon",
            "type": "string",
            "name": "icon"
          },
          {
            "control_type": "text",
            "label": "Description",
            "type": "string",
            "name": "description"
          },
          {
            "control_type": "text",
            "label": "Homepage",
            "type": "string",
            "name": "homepage"
          }
        ],
        "label": "Expandable",
        "type": "object",
        "name": "_expandable"
      }
    ],
    "label": "Container",
    "type": "object",
    "name": "container"
  },
  {
    "properties": [
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Value",
            "type": "string",
            "name": "value"
          },
          {
            "control_type": "text",
            "label": "Representation",
            "type": "string",
            "name": "representation"
          },
          {
            "properties": [
              {
                "control_type": "text",
                "label": "Webresource",
                "type": "string",
                "name": "webresource"
              },
              {
                "control_type": "text",
                "label": "Content",
                "type": "string",
                "name": "content"
              }
            ],
            "label": "Expandable",
            "type": "object",
            "name": "_expandable"
          }
        ],
        "label": "View",
        "type": "object",
        "name": "view"
      },
      {
        "properties": [
          {
            "control_type": "text",
            "label": "Editor",
            "type": "string",
            "name": "editor"
          },
          {
            "control_type": "text",
            "label": "Export view",
            "type": "string",
            "name": "export_view"
          },
          {
            "control_type": "text",
            "label": "Styled view",
            "type": "string",
            "name": "styled_view"
          },
          {
            "control_type": "text",
            "label": "Storage",
            "type": "string",
            "name": "storage"
          },
          {
            "control_type": "text",
            "label": "Anonymous export view",
            "type": "string",
            "name": "anonymous_export_view"
          }
        ],
        "label": "Expandable",
        "type": "object",
        "name": "_expandable"
      }
    ],
    "label": "Body",
    "type": "object",
    "name": "body"
  },
  {
    "properties": [
      {
        "control_type": "number",
        "label": "Position",
        "parse_output": "float_conversion",
        "type": "number",
        "name": "position"
      }
    ],
    "label": "Extensions",
    "type": "object",
    "name": "extensions"
  },
  {
    "properties": [
      {
        "control_type": "text",
        "label": "Webui",
        "type": "string",
        "name": "webui"
      },
      {
        "control_type": "text",
        "label": "Edit",
        "type": "string",
        "name": "edit"
      },
      {
        "control_type": "text",
        "label": "Tinyui",
        "type": "string",
        "name": "tinyui"
      },
      {
        "control_type": "text",
        "label": "Collection",
        "type": "string",
        "name": "collection"
      },
      {
        "control_type": "text",
        "label": "Base",
        "type": "string",
        "name": "base"
      },
      {
        "control_type": "text",
        "label": "Context",
        "type": "string",
        "name": "context"
      },
      {
        "control_type": "text",
        "label": "Self",
        "type": "string",
        "name": "self"
      }
    ],
    "label": "Links",
    "type": "object",
    "name": "_links"
  },
  {
    "properties": [
      {
        "control_type": "text",
        "label": "Metadata",
        "type": "string",
        "name": "metadata"
      },
      {
        "control_type": "text",
        "label": "Operations",
        "type": "string",
        "name": "operations"
      },
      {
        "control_type": "text",
        "label": "Children",
        "type": "string",
        "name": "children"
      },
      {
        "control_type": "text",
        "label": "Restrictions",
        "type": "string",
        "name": "restrictions"
      },
      {
        "control_type": "text",
        "label": "History",
        "type": "string",
        "name": "history"
      },
      {
        "control_type": "text",
        "label": "Ancestors",
        "type": "string",
        "name": "ancestors"
      },
      {
        "control_type": "text",
        "label": "Descendants",
        "type": "string",
        "name": "descendants"
      }
    ],
    "label": "Expandable",
    "type": "object",
    "name": "_expandable"
  }
]

Step 3

「Python snippets by Workato」コネクタを選択します。

image.png

「Execute code」を選択します。

image.png

次の通り設定します。

Name
任意の名称

Input fields

「html_body」というString型のフィールドを追加します。
その後、フィールドに次の内容を入力します。

<html><head></head><body>
[Value]
</body></html>

[Value]には、Step2の次のValueをセットします。

image.png

Output fields

String型で次のフィールドを追加します。

  • date
  • rate
  • comment

image.png

Code

次のコードを入力します。

import lxml.html

def main(input):

  root = lxml.html.fromstring(input['html_body'])

  date = ''
  rate = ''
  comment = ''
  date = root.xpath("//table/tbody/tr/td")[0].text
  rate = root.xpath("//table/tbody/tr/td")[1].text
  comment = root.xpath("//table/tbody/tr/td")[2].text

  return { 'date': date, 'rate': rate, 'comment': comment}

コード解説

入力値にセットされた値(html_bodyの値)をHtmlElementオブジェクトにします

root = lxml.html.fromstring(input['html_body'])

XPathでテーブルの1行目の各列をXPathで取得します。

  date = root.xpath("//table/tbody/tr/td")[0].text
  rate = root.xpath("//table/tbody/tr/td")[1].text
  comment = root.xpath("//table/tbody/tr/td")[2].text

XPathで取得した値を戻り値として返します

return { 'date': date, 'rate': rate, 'comment': comment}

Step 4

「Lookup tables by Workato」コネクタを選択します。

image.png

「Search entries」を選択します。

image.png

次の通り設定します。

  • Lookup table:検索したいテーブルを選択
  • Search by:検索条件をセット(ここでは idに1をセット)

image.png

Lookup Table

次のようなLookup tableを想定します。

image.png

Step 5

「Lookup tables by Workato」コネクタを選択します。

image.png

「Update entry」を選択します。

image.png

以下の通り値をセットします。

  • Entry ID: Step4の Entry IDをセット
  • Entry fields: Step3, Step2の対応するフィールドをセット

image.png

Step 6

「Workbot for Slack」コネクタを選択します。

image.png

「Post message」を選択します。

image.png

次の項目を設定します。

  • Channel name/DM: 通知先のチャンネルまたはユーザー

image.png

Message attachments

Title, Title linkに通知メッセージを入力します。

image.png

Attachment fields

日付、TTMレート、備考欄を追加し、Step3の対応するフィールドをValueへセットします。

image.png

実行結果

Lookup tables

Confluenceの1行目の各列の値が、Lookup Tablesの各フィールドに追加されます。

image.png

Slack

Confluenceの1行目の各列の値が、Slackメッセージとして通知されます。

image.png

まとめ

Workatoでは、Pythonコネクタを利用することで、Confluenceのような非構造化データ(HTML)も構造化データのように扱えるようになります。Confluenceと何かのシステムを連携したい方の参考になりましたら幸いです。

0
0
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
0
0