SlackのBlock Kitをjsxの記法で書ける jsx-slackを試してみた
TL;DR
- 僕は個人的に datadog_slack_reporterというものを作成して、datadogで監視しているサービスの台数をslackに通知しています
- Slackのメッセージ作成部分を、SlackのBlock Kitをjsxの方式で記述できる jsx-slack に置き換えてみました
- 面倒なjsonの作成部分がReactっぽく書けるので、めちゃくちゃ便利でした
- この資料には
jsx-slack
をTypeScriptで導入する上で必要な設定方法を記載します
jsx-slackとは何か
Slackのメッセージ作成をjsxのフォーマットで行うことができるnpmのpackageです。これを利用すれば、Reactのcomponentを書くような書き味で複雑なjsonの作成をすることができます。
上の図はjsx-slack
内で生成されるjsonが確認できるページです。こちらの右側のjsonがslackが要求するjsonのフォーマットとなっており、左側がjsx-slackを利用して記述するコードになります。jsx-slack
の動作確認ページはこちらになります。
成果物
最初に、今回作成したものの全体像をざっくり説明します。
Slackへのメッセージ出力
こちらが、今回僕が作成したbotが実際にslackへ投稿したメッセージです。
ソースコード (一部抜粋)
jsx-slackを使った slackへのメッセージ送信ボットのプログラムがこちらになります。全体像を掴むために、さらっと眺めてもらえれば大丈夫です。
Cloud Functionsのhandler
import moment from "moment-timezone"; import "source-map-support/register"; import { DatadogHostMetrics } from "./datadog"; import { DatadogClient } from "./DatadogClient"; import { SlackClient } from "./SlackClient"; import { slackMessageBlock } from "./SlackMessageBlock"; const datadogClient = new DatadogClient(); const slackClient = new SlackClient(); export async function datadog_handler(data: any): Promise<void> { const fromTime = moment({ hour: 0, minute: 0, second: 0 }) .tz("Asia/Tokyo") .subtract(1, "days") .format("X"); const toTime = moment({ hour: 23, minute: 59, second: 59 }) .tz("Asia/Tokyo") .subtract(1, "days") .format("X"); // datadogのAPIを叩いてデータを取ってくる const hostMetrics: DatadogHostMetrics[] = await datadogClient.countHosts(fromTime, toTime); // 送信するメッセージを生成する const blocks = slackMessageBlock(fromTime, toTime, hostMetrics); // メッセージを送信する await slackClient.post(blocks); }
messageの作成部分 (jsx-slackで書き直された場所)
/** @jsx JSXSlack.h */ import JSXSlack, { Block, Section } from "@speee-js/jsx-slack"; import moment from "moment-timezone"; import { DatadogHostMetrics } from "./datadog"; import { ProductMetrics } from "./ProductMetrics"; export function slackMessageBlock(fromTime: string, toTime: string, hostMetrics: DatadogHostMetrics[]) { const messages = []; for (const metrics of hostMetrics) { const productMetrics = new ProductMetrics(metrics); const message = ( <blockquote> <b> {productMetrics.name} </b> <br /> min:${productMetrics.minHostCount()} ~ max:${productMetrics.maxHostCount()} sum(host*hours):${productMetrics.sum()} </blockquote> ); messages.push(message); } return JSXSlack( <Block> <Section> <p>datadog monitoring daily report</p> {moment.unix(parseInt(fromTime, 10)).toString()} ~ {moment.unix(parseInt(toTime, 10)).toString()} {messages} </Section> </Block> ); }
messageの送信部分
import { WebClient } from "@slack/client"; export class SlackClient { private static username: string = "Datadog按分計算Bot"; private readonly channelID: string = process.env.CHANNEL_ID as string; private readonly token: string = process.env.SlackToken as string; private readonly client: WebClient; constructor() { this.client = new WebClient(this.token); } public post(blocks: any) { return this.client.chat.postMessage({ channel: this.channelID, text: "", blocks, username: SlackClient.username, }); } }
実際のコードはこちらを参照してください。 https://github.com/selmertsx/datadog_slack_report/pull/4
基本的な書き方
- 最初にslack本家のblock-kit-builderで、サンプルを見ながらどのようなblock kitが作りたいのか考えます。
- jsx-slackのリポジトリ内にはサンプルがいくつかあるので、そちらを参照しても良いでしょう
- その後、jsx-slackのサイトで目的のjsonが生成できる記法を探します。
jsx-slackの導入設定
TypeScriptで導入する際は少しだけ設定をする必要があります。公式ドキュメントを見ると下記のような記載があります。
https://github.com/speee/jsx-slack
A prgama would work in Babel (@babel/plugin-transform-react-jsx) and TypeScript >= 2.8 with --jsx react.
ということで、jsx-slackをTypeScriptで利用するにはtsconfigの設定が必要であることが分かります。以降、それらの設定を行っていきましょう。
@speee-js/jsx-slack
のinstall
npm i @speee-js/jsx-slack
今回の設定に必要なpackageを上記コマンドで全部installします。
tsconfigの設定
TypeScriptのjsxに関するドキュメントを読んでみましょう。
https://www.typescriptlang.org/docs/handbook/jsx.html#factory-functions
The exact factory function used by the jsx: react compiler option is configurable. It may be set using either the jsxFactory command line option, or an inline @jsx comment pragma to set it on a per-file basis.
上記コメントで示されているように、TypeScriptでjsxを利用するためには、ファイルごとのpragmaの設定と、コンパイラオプションの指定が必要になります。pragmaの設定とは、サンプルコードで示されていた /** @jsx JSXSlack.h */
の部分を指します。コンパイラオプションは、下記のように設定します。
{ "compilerOptions": { "target": "es2018", "module": "commonjs", "lib": [ "es5", "es2015", "es2016.array.include", "esnext.asynciterable" ], "strict": true, "outDir": "./", "esModuleInterop": true, "noImplicitAny": true, "allowJs": true, "jsx": "react" // <= 今回追加した設定 }, "include": [ "src/**/*.ts" ], "exclude": [ "node_modules", "__tests__", "**/__mocks__/*.ts" ] }
実際のコードはこちら
最後に
jsx-slack
導入の手順は以上になります。今回の例はちょっとシンプルすぎるのであまり有り難みを感じにくいかも知れませんが、もうちょっと複雑になってくると jsxを利用してReactっぽく書ける jsx-slack
に大きなメリットを感じられるかと思います。
Sidekiqのjobの信頼性向上方法と Sidekiq Proの検討について
自分のための覚書
TL;DR
- sidekiq proでは、server processが死んでも jobの復活がサポートされる
- sidekiq proにおいて、redis が死んでも、1000件程度のジョブならclientが保持し続けて、redisが復活したタイミングでenqueue してくれる
- ↑の状況において、client processが死ねば、蓄積された1000件のジョブは全て消える
- sidekiq enterpriseでは、unique な jobになるように諸々やってくれるが、完全に保証してくれるものでは無いので、そこんところを考えたjob設計にすること。
Pricing
- Pro
- $9,500 / yr
- Enterprise
- 250 thread $19,500 / yr
- 500 thread $23,400 / yr
Jobが失われないようにする仕組みについて
sidekiqにおいてjobが失われるケースは下記の3点
- redisに接続できないケース
- client processが死んだケース
- server processが死んだケース
この中でSidekiq Proが対策をしているのは redisに接続できない
と server processが死んだ
の2つのケース。
redisに接続できない場合
基本的に、下記の資料に書いてある内容。 https://github.com/mperham/sidekiq/wiki/Pro-Reliability-Client
通常版だと、redisが死んでいたらjobがpush出来ない。しかしPro版だとpushできなかったjobをclientのプロセスが保持しており、特定のjobがpushできるようになったタイミングで残っているjobも一緒にpushしてしまう。なお、client が保持できるjobの数は最新の1000件のみ。1000件を越えた場合は、永遠に失われてしまうので、そこは気をつけること。
server processが死んだ場合
- sidekiq basicは、Redis queueからのjobのフェッチにBRPOPを使っている
- sidekiqがjobを実行している最中にクラッシュしたら、そのjobは永遠に失われてしまう
- jobが失われない様に保証するための唯一の方法は、jobが完了するまでredisから削除しないこと
- sidekiq proはRedisのPROPLPUSH コマンドを利用してそれを実現している
- BROPLPUSH を使う場合は下記のように記述する必要がある
Sidekiq::Client.reliable_push! unless Rails.env.test? Sidekiq.configure_server do |config| config.super_fetch! config.reliable_scheduler! end
なお、super_fetchをする際は、redisデータベースの全走査を行うので、cache dataとjob dataで保持するredisを分けるのが望ましい。
JobのUnique制約について
大前提として jobは (class, 引数, queue)を組み合わせてuniqueであることが求められる。同じ引数のjobを異なるqueueに入れることができる。そのため、同一引数のjobで違う結果を期待してはいけない。基本的に unique な制約は時間に掛けることになる。
class MyWorker include Sidekiq::Worker sidekiq_options unique_for: 10.minutes def perform(...) end end
上記のような設定をした場合、jobが成功するか、jobが失敗して10分が経過するまで同一引数のjobを実行することは出来ない。
Office365のユーザーimport/exportはそれぞれフォーマットが異なる
TL;DR
- AzureADのデータについて、ロールバックできる仕組みが欲しい
- できる限り自前で実装せずに、公式の提供してくれる仕組みに乗っかりたい
- Office365に存在する import/exportを試してみた
- そのままでは IDaaSのロールバック用途には使えなさそう
- グループのexportがない
- importとexportでデータのフォーマットが異なる
方法
export
https://portal.office.com/AdminPortal/Home#/users
上記URLのエクスポートボタンを押すと、下記のようなフォーマットのcsvをダウンロードできます。 下記の項目のデータが、従業員の人数分得られます。
AlternateEmailAddresses,BlockCredential,City,Country,Department,DisplayName,Fax,FirstName,LastDirSyncTime,LastName,LastPasswordChangeTimestamp,LicenseAssignmentDetails,Licenses,MobilePhone,OathTokenMetadata,ObjectId,Office,PasswordNeverExpires,PhoneNumber,PostalCode,PreferredDataLocation,PreferredLanguage,ProxyAddresses,ReleaseTrack,SoftDeletionTimestamp,State,StreetAddress,StrongPasswordRequired,Title,UsageLocation,UserPrincipalName,WhenCreated
import
importは上記URLから「その他」「複数のユーザーのインポート」を選択すると、設定することができます。
importする際に必要なデータのフォーマットについて、この資料 を参考に見てみると、下記のようなフォーマットのデータを求めていると書かれています。
User Name,First Name,Last Name,Display Name,Job Title,Department,Office Number,Office Phone,Mobile Phone,Fax,Address,City,State or Province,ZIP or Postal Code,Country or Region chris@contoso.com,Chris,Green,Chris Green,IT Manager,Information Technology,123451,123-555-1211,123-555-6641,123-555-9821,1 Microsoft way,Redmond,Wa,98052,United States ben@contoso.com,Ben,Andrews,Ben Andrews,IT Manager,Information Technology,123452,123-555-1212,123-555-6642,123-555-9822,1 Microsoft way,Redmond,Wa,98052,United States david@contoso.com,David,Longmuir,David Longmuir,IT Manager,Information Technology,123453,123-555-1213,123-555-6643,123-555-9823,1 Microsoft way,Redmond,Wa,98052,United States cynthia@contoso.com,Cynthia,Carey,Cynthia Carey,IT Manager,Information Technology,123454,123-555-1214,123-555-6644,123-555-9824,1 Microsoft way,Redmond,Wa,98052,United States melissa@contoso.com,Melissa,MacBeth,Melissa MacBeth,IT Manager,Information Technology,123455,123-555-1215,123-555-6645,123-555-9825,1 Microsoft way,Redmond,Wa,98052,United States
exportされたフォーマットと比べてみると、項目が大きく異なることがわかります。 importするには、諸々データを加工してimport用のデータを作成し、consoleからuploadします。 このとき、200名を超えるデータは一度にimportすることはできません。 また、importする際のCSVのカラムについて、過不足があると登録することができません。
以上のことから、exportで取得したデータを、importにそのまま使えないというちょっとびっくりさせられる仕様のOffice365さんでした。
webpackをupdateしたらterserの問題でbuildができなくなる問題の対処法
TL;DR
- webpackのバージョンを
v4.29.0
にupdateしたらbuildできなくなった - どうやら最新の
terser-js
に問題があったらしい terser-js
を3.14
にしたら問題が解決した
事象
npm update
をして webpackのバージョンをv4.29.0
にした- その状態で
webpack --mode production
をしたら下記のエラー
ERROR in index.js from Terser TypeError: Cannot read property 'minify' of undefined at minify (xxx/node_modules/terser-webpack-plugin/dist/minify.js:175:23) at module.exports (xxx/node_modules/terser-webpack-plugin/dist/worker.js:13:40) at handle (xxx/node_modules/worker-farm/lib/child/index.js:44:8) at process.<anonymous> (xxx/node_modules/worker-farm/lib/child/index.js:51:3) at emitTwo (events.js:126:13) at process.emit (events.js:214:7) at emit (internal/child_process.js:772:12) at _combinedTickCallback (internal/process/next_tick.js:141:11) at process._tickCallback (internal/process/next_tick.js:180:9)
【GCP】Datastoreを作ってしまったGCPプロジェクトではFirestoreを使えない
表題の通り。それだけなんだけれども、同じ悲劇を経験する人がいないようにブログに書きました。
2019年1月、Firestoreが東京リージョンでも使えるようになった。 https://firebase.google.com/docs/firestore/locations?hl=en#location-r
せっかくなので、今作っているcloud functionsのデータベースをDatastoreではなくてFirestoreで構築しようと考えた。( FirestoreはDatastoreの上位互換であり、Datastoreで使える機能はFirestoreでも利用可能 )
ということでドキュメントを見ながらコードを書いて、動作確認をしようとしたら下記のエラー。
{ Error: 9 FAILED_PRECONDITION: This project contains a Cloud Datastore database and does not support Cloud Firestore API calls. at Object.exports.createStatusError (/Users/shuhei.morioka/project/speee/datadog_slack_report/node_modules/grpc/src/common.js:91:15) at Object.onReceiveStatus (/Users/shuhei.morioka/project/speee/datadog_slack_report/node_modules/grpc/src/client_interceptors.js:1204:28) at InterceptingListener._callNext (/Users/shuhei.morioka/project/speee/datadog_slack_report/node_modules/grpc/src/client_interceptors.js:568:42) at InterceptingListener.onReceiveStatus (/Users/shuhei.morioka/project/speee/datadog_slack_report/node_modules/grpc/src/client_interceptors.js:618:8) at callback (/Users/shuhei.morioka/project/speee/datadog_slack_report/node_modules/grpc/src/client_interceptors.js:845:24) code: 9, metadata: Metadata { _internal_repr: {} }, details: 'This project contains a Cloud Datastore database and does not support Cloud Firestore API calls.' }
ここで1~2時間くらい原因を調査したが分からず、issue trackerを見てみたらこんな文言が。
If you absolutely want to use Firestore proper, you need to open a new project and deploy your app to the new project, where you choose Firestore from the start.
https://issuetracker.google.com/issues/113075718
ということで、Datastoreをすでに使っているプロジェクトではFirestoreは使うことが出来ません。これから触る人は、誤って使わないように気をつけましょう〜
ちなみに
Cloud Firestore への自動アップグレード | Cloud Datastore ドキュメント | Google Cloud
Cloud Firestore の一般公開後、ある程度の時間を置いてから既存の Cloud Datastore データベースの所有者に連絡し、Cloud Firestore の Datastore モードへの自動アップグレードのタイミングを相談させていただく予定です。
ということです!
「U30のための最速キャリア戦略」の所感
読んだもの
読んでみた経緯
ここで紹介されていたスライドの内容があまりにも納得がありすぎたので、全部読んでみたくなった。
今更だけど #devboost で一番好きなスライド。2人以上の人間が関わるあらゆる場面で言えることだと思う。
— ゆずたそ (@yuzutas0) January 25, 2019
"意に沿わなくとも、組織の意思決定内容に貢献する" - Carrier Management Strategy for U30 https://t.co/9tC2JELGZS
気になったポイント
- 職位が上がるにつれて、コードの外側の設計スキルの能力が求められる
- 正しく意見をぶつける
- 意思決定者を理解する
- 正しい目標が何かを考え、適切に意思決定者に伝え、意思決定に反映していく
- そのためには、適切に状況(KPIや日常の会話から)を把握し、
- 現在の課題に対して説得力のある解釈を用意し
- 意にそぐわなくとも、組織の意思決定内容に貢献する
- 意思決定の場、議論、準備に全力を注ぐ
- 意思決定が意にそぐわないのは、自分の意思表明の方法に問題がある
- 人生において全力で取り込めるプロダクトの数はそう多くない
- 少なくとも、全力で取り組めば自分の経験になる
所感
職位が上がるにつれて、コードの外側の設計スキルの能力が求められる というのは最近常に感じる。プロジェクトの成否が、純粋に技術の問題だけに終始するという幸せな状況はほとんど見たことがない。技術力は、実現の可否判断や実現速度を大幅に上げてくれる要素ではあるものの、プロジェクトが正しい方向に向かっていることを担保してくれはしない。正しい方向性を目指すこと、軌道を正しい方向に修正するには技術以外の知見が必要であると感じるシーンに、ここ数年で何度も出くわしてきた。それはプロジェクトマネジメントの視点であったり、または市場理解、データ理解であったりした。
その中においてどのように振る舞っていくかの一つの示唆が、このスライドには多く含まれていた。松本さんのように真正面から全力でぶつかっても良いだろうし、仲間を信じて自分の専門スキルを研ぎ澄ましても良いだろう。
人生において全力で取り込めるプロダクトの数はそう多くない。自分がどのようなスキルセットを持って、課題に取り込んでいくのか。そしてその経験を糧にしていくのか考えされられる資料だった。