Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

Logic Appsで簡易RSSリーダーを作ってTeamsに通知する

この記事はオルターブース Advent Calendar 2021の11日目の記事です。

adventar.org

弊社ではMicrosoft Teamsをメインに日々の業務を進めています。有用な記事などRSSでフォローしておく情報については個々のRSSで収集するより、Teamsに流しておくほうが社員でシェアでき、記事についてTeams上でディスカッションすることもできるため、Teamsに流すようにしています。

TeamsでRSSを購読する簡単な方法としては、TeamsのRSSコネクタを利用します。

f:id:hawa9:20211207220607p:plain

f:id:hawa9:20211207220628p:plain

もしくは、少し手間をかけてLogic Appsを使ってRSSを購読しTeamsに通知することもできます。

f:id:hawa9:20211207220706p:plain

Logic Appsでわざわざ作るのか?

では、なぜビルドインのコネクタがあるのに、Logic Appsで車輪の再発明のようなことをしたのか?それは、ビルドインのコネクタでは、Basic認証があるRSSで「パスワードあるいはIDに@を含む」と現状Basic認証が通りません。。。

https://feedback.azure.com/d365community/idea/414f7154-7b26-ec11-b6e6-000d3a4f032c

上記条件のRSSを購読する必要があったため、Logic Appsで簡易RSSリーダーを作ってみました。

このブログでご紹介するLogic Appsの全景は次ようになります。ただ、購読するRSSによっては処理を変える必要があるので、どのようなRSSにも対応してる訳ではありません。本当はビルドインコネクタを使いたいので、よろしければ上記のfeedbackに投票をお願いします!

f:id:hawa9:20211207220923p:plain

準備

Logic Appsのデザインを始める前に、処理に必要なAzureリソースを準備します。

  • ブログ記事管理のデータベースとしてAzure Storage Tableを利用するので、リソースを作成します。Azure Storage TableはLogic Appsのエンティティアクションで利用します。
az storage account create -g <resource group> -n <name> -l <location>
az storage table create -n rss --connection-string <Storage account connection string>
  • Logic AppsのJavaScriptコード実行アクションを利用するので、統合アカウントリソース作成します。

    1. 統合アカウントの作成

      f:id:hawa9:20211207221042p:plain

    2. Logic Appsと統合アカウントを関連付け

      f:id:hawa9:20211207221100p:plain

Logic Appsデザイン

Logic AppsでRSSリーダーを作っていきます。

  1. Logic AppsのHTMLトリガーでRSSを取得します。

    今回は弊社ブログのRSSを利用します。

    https://aadojo.alterbooth.com/feed

    f:id:hawa9:20211207221139p:plain

  2. RSSはXMLなので、Logic Appsで処理しやすいJSONへ変換します。

    XMLからJSONへの変換はを利用します。

     json(xml(triggerBody()))
    

    変換した値は作成アクションに入れます。

    f:id:hawa9:20211207221207p:plain

    JSON変換後は次のようになります。各ブログの記事がEntryリストに存在します。

     {
         "feed": {
             "@xmlns": "http://www.w3.org/2005/Atom",
             "@xml:lang": "ja",
             "title": "Alternative Architecture DOJO",
             "link": {
                 "@href": "https://aadojo.alterbooth.com/"
             },
             "updated": "2021-12-06T03:00:47+09:00",
             "author": {
                 "name": "alterbooth"
             },
             "generator": {
                 "@uri": "https://blog.hatena.ne.jp/",
                 "@version": "430259ab8ce45790e2f4b723542a620b",
                 "#text": "Hatena::Blog"
             },
             "id": "hatenablog://blog/10257846132689992429",
             "entry": [
                 {
                     "title": "Flask+MySQLのアプリを作ってAzureにデプロイする!",
                     "link": [
                         {
                             "@href": "https://aadojo.alterbooth.com/entry/2021/12/06/030047?utm_source=feed"
                         },
                         {
                             "@rel": "enclosure",
                             "@href": "https://cdn-ak.f.st-hatena.com/images/fotolife/m/mitsuzono-dev/20211206/20211206023654.png",
                             "@type": "image/png",
                             "@length": "0"
                         }
                     ],
                     "id": "hatenablog://entry/13574176438039949656",
                     "published": "2021-12-06T03:00:47+09:00",
                     "updated": "2021-12-06T03:00:47+09:00",
                 "summary": {
     ...
    
  3. Entryリストのブログ記事を日付(published)でソートします。

    Logic Appsと統合アカウントを関連付けたあと、JavaScriptコード実行アクションにソート処理を記載します。

     var ret = workflowContext.actions.作成.outputs.feed.entry.sort(function(a,b) {
         return (a.published < b.published ? -1 : 1);
     });
     return ret;
    

    f:id:hawa9:20211207221239p:plain

  4. JavaScriptコード実行アクションの結果(ソート後の値)を作成2アクションに入れます。

    f:id:hawa9:20211207221312p:plain

  5. データベースとしてAzure Storage Tableを利用するので、Partition KeyとRow Keyを変数アクションで用意します。

    f:id:hawa9:20211207221328p:plain

  6. Entryリストをソートした作成2アクションの出力で、For Eachループアクションを開始します。

    • Azure Storage Tableにデータが存在しない。-> 新規記事
    • Azure Storage Tableにデータが存在する。-> 過去記事

    f:id:hawa9:20211207221344p:plain

    ループは逐次処理となるように設定します。

    • コンカレンシー制御 有効
    • 並列処理の次数 1

    f:id:hawa9:20211207221410p:plain

  7. EntryJSONの解析アクションで解析します。

    f:id:hawa9:20211207221429p:plain

    スキーマは次のようになります。

     {
         "properties": {
             "author": {
                 "properties": {
                     "name": {
                         "type": "string"
                     }
                 },
                 "type": "object"
             },
             "content": {
                 "properties": {
                     "#text": {
                         "type": "string"
                     },
                     "@@type": {
                         "type": "string"
                     }
                 },
                 "type": "object"
             },
             "id": {
                 "type": "string"
             },
             "link": {
                 "items": {
                     "properties": {
                         "@@href": {
                             "type": "string"
                         },
                         "@@length": {
                             "type": "string"
                         },
                         "@@rel": {
                             "type": "string"
                         },
                         "@@type": {
                             "type": "string"
                         }
                     },
                     "required": [
                         "@@href"
                     ],
                     "type": "object"
                 },
                 "type": "array"
             },
             "published": {
                 "type": "string"
             },
             "summary": {
                 "properties": {
                     "#text": {
                         "type": "string"
                     },
                     "@@type": {
                         "type": "string"
                     }
                 },
                 "type": "object"
             },
             "title": {
                 "type": "string"
             },
             "updated": {
                 "type": "string"
             }
         },
         "type": "object"
     }
    
  8. JSONの解析アクションの結果idをURLエンコードので変換し、変数の設定アクションでRow Keyへ入れます。

     encodeUriComponent(body('JSON_の解析')?['id'])
    

    f:id:hawa9:20211207221503p:plain

  9. スコープアクション利用して次のアクションをカプセル化します。

    1. Azure Storage Tableのエンティティの取得アクションを実行します。
    2. 取得できた場合はJSONの解析2アクションを実行します。

      f:id:hawa9:20211207221520p:plain

      スキーマは次のようになります。

       {
           "type": "object",
           "properties": {
               "odata.metadata": {
                   "type": "string"
               },
               "odata.etag": {
                   "type": "string"
               },
               "PartitionKey": {
                   "type": "string"
               },
               "RowKey": {
                   "type": "string"
               },
               "Timestamp": {
                   "type": "string"
               },
               "link": {
                   "type": "string"
               },
               "published": {
                   "type": "string"
               },
               "title": {
                   "type": "string"
               },
               "updated": {
                   "type": "string"
               }
           }
       }
      
  10. 条件アクションでIF判定を行います。

    JSONの解析2アクションの本文がNULL(Azure Storage Tableに存在しない)かどうか判定します。

    f:id:hawa9:20211207221553p:plain

    Azure Storage Tableにidが存在しないとエンティティの取得アクションが失敗(StatusCode 404)するため、失敗でも条件アクションを実行するように設定します。

    f:id:hawa9:20211207221607p:plain

    • 条件アクションの結果がFalseの場合

      過去記事のため処理終了です。

  11. 条件アクションの結果がTrueの場合

    新規記事のため後続処理を行います。

    1. 記事のURLを取得したいので、JSONの解析アクションの結果linkリストでFor Eathループを行います。ループは逐次処理となるように設定します。

      • コンカレンシー制御 有効
      • 並列処理の次数 1

      f:id:hawa9:20211207221651p:plain

      f:id:hawa9:20211207221709p:plain

    2. linkの内容をJSONの解析3アクションで解析します。

      f:id:hawa9:20211207221723p:plain

      スキーマは次のようになります。

       {
           "properties": {
               "@@href": {
                   "type": "string"
               },
               "@@length": {
                   "type": "string"
               },
               "@@rel": {
                   "type": "string"
               },
               "@@type": {
                   "type": "string"
               }
           },
           "type": "object"
       }
      
    3. @hrefがブログのURLを含んでいるか条件2アクションで判定します。

      f:id:hawa9:20211207221803p:plain

      • 条件2アクションの結果がFalseの場合

        OJB画像となるので処理は行いません。

    4. 条件2アクションの結果がTrueの場合

      1. TeamsアクションでTeamsチャンネルに新着記事を投稿します。
      2. Azure Storage Tableに記事のidを設定します。

        f:id:hawa9:20211207221832p:plain

        Azure Storage Tableには次のようにデータが保存され、新着記事の判定に用います。

        f:id:hawa9:20211207221907p:plain

初回はAzure Storage Tableが空のため全記事が新規記事判定になってしまいますが、新着記事があると、こんな感じでTeamsに通知してくれます。

f:id:hawa9:20211207222106p:plain

Logic Appsの作り方など他のやり方もあるとは思いますが、課題は解決できたので仕事環境改善に繋がりました。本当は本当はビルドインコネクタを使いたいのでfeedbackに投稿をお願いします!

https://feedback.azure.com/d365community/idea/414f7154-7b26-ec11-b6e6-000d3a4f032c