MattermostにRedashのチャートを貼り付ける

Jan 3, 2018 15:52 · 1348 words · 3 minute read redash Mattermost

はじめに

Redashでアクセス数を可視化してるのですが、わざわざRedashを開くのが面倒になってきたのでMattermostからRedashのチャートを取得できるkaakaa/matter-redashというものを作りました。

SlackだとRedash連携は色々あるのにMattermostでは見当たらない = 作るしかない。 re:dashにslack bot機能が追加されたよ! - Qiita hakobera/redashbot: Slack Bot for re:dash

どんなもの?

MattermostのSlash Commandとして動きます。

matter-redash.gif

どうやって動かす?

kaakaa/matter-redash: Redash integrations for MattermostのREADMEに書いています。

どういう風に動いてる?

  • MattermostのSlash Commandでmatter-redashへリクエスト飛ばす
  • 引数で指定されたチャート画像のスクリーンショットを取得
  • Mattermostへアップロード&チャートを投稿

という流れです。

以下、いくつかつまづいたところ。

Redash

Redashからチャート画像を取得するために/embed APIを使用しています。(RedashのAPI Specはどこかに無いだろうか・・) redash/embed.py at 1ef2238d65c0c87ed0bc74141cca44b7766b8453 · getredash/redash

このAPIを叩くとSVG入りのHTMLが取得でき、そのままではMattermostでレンダリングできないのでGoogleChrome/puppeteerを使ってスクリーンショットを撮り、その画像ファイルをMattermostにアップロードしています。

const embedUrl = util.format('%s://%s/embed/query/%d/visualization/%d?api_key=%s', protocol, redashHost, queryId, visualizationId, config.redash.apiKey);
console.log('Redash Embed URL: %s', embedUrl); // eslint-disable-line no-console |
const file = await webshot(embedUrl); |
const webshot = async (url) => {
    const tmpFile = tempfile('.png'); 
    const browser = await puppeteer.launch(); 
    const page = await browser.newPage();

    await page.goto(url); 
    await page.screenshot({path: tmpFile}); 
    await browser.close(); 

    return tmpFile;
};

そこら辺の実装方法についてはhakobera/redashbotを参考にさせていただきました。

Mattermost

Personal Access Token

MattermostにAPI経由で画像ファイルをアップロードするために投稿権限が必要で、今の所Personal Access Tokenのみサポートしてます。 なので、MattermostはPersonal Access Tokenが実装されたv4.1.2以上である必要があります。

Mattermost上でチャート画像を見やすくするため、一旦チャート画像をメッセージの添付ファイルとして投稿し、[ファイルに対する公開リンク](Sharing Files — Mattermost 4.5 documentation)を取得し、Message Attachmentsとして投稿するようにしています。

    // Upload webshot image file
    const imageFormData = new FormData();
    imageFormData.append('files', fs.createReadStream(file));
    imageFormData.append('channel_id', req.body.channel_id);

    const uploadFile = await client.uploadFile(imageFormData, imageFormData.getBoundary());

    // Get public link of uploaded file
    const fileId = uploadFile.file_infos[0].id;
    const post = await client.createPost({
        channel_id: req.body.channel_id,
        message: 'This message is posted solely to get the public link and should be deleted immediately.',
        file_ids: [fileId]
    });
    const link = await client.getFilePublicLink(fileId);
    console.log('Mattermost Public Link: %s', link.link); // eslint-disable-line no-console

    // Response to Mattermost
    res.header({'content-type': 'application/json'});
    res.send(commandResponse(url, link.link));

    // Delete unnecessary post.
    client.deletePost(post.id);

最後に不要なPostを削除していますが、これはMattermostの仕様上、一度Postに対する添付ファイルとして投稿しないとファイルの公開リンクを取得できないためです。 なので、実行ごとに(このメッセージは削除されました)という投稿が発生していまうので少し気持ち悪いです。

注意点

Personal Access TokenもPublic Linkも設定で無効にできたりするので、システムコンソールから有効にしておく必要があります。

おわりに

とりあえず運用してみて、使えそうならShare your Mattermost projects | MattermostからMattermostチームに報告する所存。

あと、Mattermostプラグインとして作り直したいな。

tweet Share