- 投稿日:2021-02-21T22:12:46+09:00
JavaScriptでcsvダウンロードを実装する方法
はじめに
フロント(React)でcsvダウンロードを実装する機会があったため、備忘録です。
どうぞご活用ください。実装
const handleDLcsv = async () => { //アイテムの定義 const download_items = [ {'id': 1, 'name': 'apple', 'price': 100}, {'id': 2, 'name': 'orange', 'price': 120}, {'id': 3, 'name': 'melon', 'price': 800} ]; //csvヘッダー const array_data = [['id', 'name', 'price']]; //文字コード const bom = new Uint8Array([0xEF, 0xBB, 0xBF]); //csv用データ作成 download_items.map((item) => { const item_data = [item.id, item.name, item.price]; array_data.push(item_data); }) let csv_data = array_data.map(function(l){return l.join(',')}).join('\r\n'); //BLOB生成してダウンロード実行 const blob = new Blob([bom, csv_data], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'csv_file.csv'; const clickHandler = () => { setTimeout(() => { URL.revokeObjectURL(url); a.removeEventListener('click', clickHandler); }, 150); }; a.addEventListener('click', clickHandler, false); a.click(); }
- 投稿日:2021-02-21T18:37:52+09:00
Node.jsのバージョン管理
①
node --verionで現在のNode.jsのバージョンを確認。②
n ls-remote --allでダウングレード/アップグレードできるバージョンを確認。③
sudo n 12.0.0で指定のバージョンにダウングレード/アップグレードできる。
- 投稿日:2021-02-21T17:54:46+09:00
npmでcb.apply is not a function エラーが発生した対処
備忘録として。
現象
npmで何かパッケージをインストールしようとすると、以下のようなエラーが発生。
npmをアップデートしろとのエラーだがnpm updateやnpm instalが同じようなエラーで実行できない。npm WARN npm npm does not support Node.js v15.9.0 npm WARN npm You should probably upgrade to a newer version of node as we ... npm ERR! cb.apply is not a function ...環境
- Windows 10 pro
- Node.js バージョン 15.9.0 (Chocolateyからインストール)
- npm バージョン 7.5.3
原因
原因は、私の場合、昔にインストールしていたNode.js(アンインストール済み)のキャッシュが残っていたためのようでした。
npmのバージョンをnpm -vで確認すると、本来7.5.3のはずが、6.0.0になっていました。残っていたキャッシュによりバージョン判定がうまくいっていなかったのでしょう。対処
C:\Users\{ユーザー名}\AppData\Roamingからnpm,npm-cacheフォルダを削除。- 念のため、
npm cache clean --forceでキャッシュ消去- Chocolateyからnode.jsを再インストール。
- npmをインストール。
npm install -g npm@7.5.3
- 投稿日:2021-02-21T17:46:54+09:00
IoTデータの可視化サービス「Ambient」を試す(Node.js のパッケージを利用)
以前からサービスについて知っていたものの、クラウドサービスでのデータ可視化はあまりやってこなかったのもあり、まだ利用をしたことはなかった IoTデータ可視化サービスの「Ambient」。
(これまでは、HTML+JavaScript でグラフ描画ライブラリを使っての可視化をやってた)今回、よく自分が利用しているデバイス・サービスで利用する場合はどうすれば使えるのかを見てみたり、実際に Node.js のパッケージを使ってデータの送信と可視化の部分を試してみたりしました(送ったデータはランダムに生成したダミーデータを利用)。
Ambient の概要
既に Qiita でもタグがあり、記事もいくつも書かれていたりしますが、自分の理解を深めるためにも以下の公式ページをざっくり見ていって、その内容をメモしてみました。
●Ambient – IoTデータ可視化サービス
https://ambidata.io/可視化の例として、公式ページのトップに以下のような事例が出ています。
利用するための方法(情報を見てみる&チャネルを作成してみる)
Ambient を使うためのおおまかな手順は、以下の公式のチュートリアル(その中の1つ目)に書かれています。
●Ambientを使ってみる – Ambient
https://ambidata.io/docs/gettingstarted/【利用するための手順】
1. ユーザー登録(無料)
2. チャネル生成
3. マイコン側プログラミング
4. データー送信
5. 可視化(グラフ化)ユーザ登録とチャネル作成
最初は公式サイト上でユーザ登録を行い、その後は可視化対象となるデータを管理するための「チャネル」を作成するところまでは、公式サイト上で操作を行っていけば良さそうです。
以下は、サイト上で「チャネルを作る」ボタンを押した後の状態です。
チャネルが 1つ追加され、データ送信などに使うキーなどが発行されました。
マイコン側プログラミング
記事の途中で書いた手順では「マイコン側プログラミング」という項目で書かれてますが、公式情報の別のページ(以下のページ)を見ると、複数のテキストプログラミング言語・ビジュアルプログラミング環境からデータの送信を行えるようです。
●ライブラリー/リファレンス – Ambient
https://ambidata.io/refs/
- 上記のページに掲載されているもの
- Arduino / C++
- mbed / C++
- Python / MicroPython
- node.js
- Node-RED
また、M5Stackシリーズをビジュアルプログラミングで扱える UIFlow についても、GitHub で公開された独自ブロックを使うとデータの送信を行えるようです。
●UIFlow(Blockly)でAmbientにデータを送る – Ambient
https://ambidata.io/samples/m5stack/uiflow/
●GitHub - AmbientDataInc/UIFlow
https://github.com/AmbientDataInc/UIFlow自分が個人的によく利用しそうな方向だと「UIFlow・Node-RED・Node.js」あたりになりそうです。
データ送信・可視化
データ送信元として様々な環境が利用できる中で、今回は Node.js を使ってマイコンを利用せずに試してみようと思います。
●node.jsライブラリー ambient-lib – Ambient
https://ambidata.io/refs/node-js/Ambient を試す
現時点までで、 Ambient上でチャネルを作成するところまでは試しました。
チャネル作成時に発行されたキー等の情報を使い、Node.js から可視化用のデータを送ってみようと思います。Node.js で簡単なお試しまで
公式の情報を見つつ、パッケージのインストールから始めていきます。
パッケージのインストール
$ npm install ambient-libプログラムからデータを送る
ライブラリの読み込みや接続・データ送信を行う処理は、以下のように書くようです。
// ライブラリの読み込み var ambient = require('ambient-lib');// Ambientへの接続 ambient.connect(チャネルId, ライトキー[, リードキー[, ユーザーキー]]); // Ambientへのデータ送信 ambient.send(data, callback(err, res, body));データ送信に関しては、例えば上記の
dataの部分にvar data = {d1: 1.1, d2: 2.2};という形などで指定するようです。2つのデータを送る場合のサンプルとして、以下の内容が公式に書かれていました。ambient.send({d1: 1.1, d2: 2.2}, function(err, res, body) { if (err) { console.log(err); } console.log(res.statusCode); });上記の情報を活用しつつ、とりあえず
d1のみに 1回だけ数値を送る、ということをやってみます。
プログラムは以下のとおりで、ambient.connectでは「チャネルId・ライトキー」の 2つのみを指定しました。d1に送る数値は1.1を指定しています。const ambient = require("ambient-lib"); ambient.connect(【ご自身のチャネルId】, "【ご自身のライトキー】"); ambient.send({ d1: 1.1 }, function (err, res, body) { if (err) { console.log(err); } console.log(res.statusCode); });上記を実行してみた後に Ambient のチャネルを見てみると、以下の画像のような表示がされていました。うまく数値を送信することができていそうです。
Node.js でのお試し(続き)
Node.js でプログラムを書く
無事にデータを送ることができているようなので、次に連続したデータ送信を行ってみます。何かセンサー付きのデバイスから値を取得する方法もありますが、今回は簡単に試せるように Node.js上でセンサーの値に見立てた乱数を生成して、それを送信するという形にしてみています。
先ほどと別のチャネルを作り、以下の Node.js のプログラムを使ってデータを送ってみました。今回のプログラムでは、
d1とd2の 2種類のデータを送るようにしてみました(※ 送信する値は乱数で生成)。const ambient = require("ambient-lib"); ambient.connect(【ご自身のチャネルId】, "【ご自身のライトキー】"); const intervalFunc = function () { ambient.send({ d1: Math.random() * 100, d2: Math.random() * 70 }, function (err, res, body) { if (err) { console.log(err); } console.log(res.statusCode); }); }; setInterval(intervalFunc, 5500);データの送信間隔についてですが、以下の公式情報を見ると 「送信から次の送信まではチャネルごとに最低5秒空ける必要があります。それより短い間隔で送信したものは無視されます」 とあるため、5秒より少しだけ長い間隔を設定しました。
●諸元/制限 – Ambient
https://ambidata.io/refs/spec/また、1日に登録可能なデータ数の制限について 「1チャネルあたり1日3,000件までデーターを登録できます。平均すると28.8秒に1回のペースです。」 と書かれているため、1日じゅうデータを送る場合はデータの送信間隔をさらに長くする必要があるようです。
チャネルで 2つのデータを同一のグラフ内に表示させてみる
以下の公式情報の「チャートのカスタマイズ」の部分を参照しながら、グラフの表示方法を 1つのグラフ内で 2つのデータを同時に表示する形にしてみました。
●チャネルとチャートのカスタマイズ – Ambient
https://ambidata.io/docs/customize/表示方法の設定変更を行った後に、先ほどのプログラムをしばらくの間実行し続けると、以下のグラフが描画されました。
まとめ
今回、IoTデータ可視化サービスの「Ambient」を使った数値データのグラフ化を行ってみました。
記事内でふれていたように、UIFlow・Node-RED や Node.js以外の他のプログラミング言語でも利用できるようなので、今後は何らか特定のデバイスを使ってセンサーで取得した値をグラフ化するようなこともやってみようと思います。
- 投稿日:2021-02-21T14:08:27+09:00
ZoomAPIを使って、ミーティングルームを作成してみる。(Create Zoom Meeting)
この記事について
表題の通り、ZoomのAPIを使って、ミーティングルームを作成したときのメモです。
Node.jsを使って作成しました。
認証方法はOAuthを利用しました。ゴール
この記事では、2021年2月25日 10時~ 1時間のミーティングルームを作成することをゴールとします。
手順
1.「Zoom」アカウント作成
2.「Zoom API」利用のための事前準備
3.認証コードの取得
4.アクセストークンの発行
5.必要パラメータの作成
6.実行1.「Zoom」アカウント作成
まだアカウントを取得していない方は、下記にアクセスしてアカウントを作成します。
https://zoom.us/jp-jp/meetings.html2.「Zoom API」利用のための事前準備
2-1.クレデンシャル情報の取得
下記にアクセスします。
https://marketplace.zoom.us/
その後、右上の「Develop」から「Build App」をクリックします。「Choose your app type」と聞かれるので、
今回は、「OAuth」の中の「Create」を選択します。Create an OAuth app
「App Name」を入力します。 例)Create Meeting Test
「Choose app type」は「Account-level app」を選択します。
「Would you like to publish this app on Zoom App Marketplace?」は「OFF」にします。
※Zoom APP マーケットプレイスに公開したい場合はONにします。
入力を終えたら、「Create」をクリックします。App Credentials
この画面の「Client ID」と「Client Secret」をメモしておきます。
「Redirect URL for OAuth」には、一旦、「http://localhost:8000」と入力しておきます。
「Whitelist URL」にも同じく「http://localhost:8000」と入力しておきます。
右下の「Continue」をクリックして、「Scopes」追加画面に移動します。2-2.スコープの追加
Add Scopes
「+ Add Scopes」ボタンをクリックして、Meetingの「View and manage all user meetings」にチェックを入れ、「Done」をクリックします。
2-3.Client_ID:Client_Secretを「Base64」でエンコードする
Authorization The string "Basic" with your Client ID and Client Secret with a colon : in between, Base64 Encoded. For example, Client_ID:Client_Secret is Q2xpZW50X0lEOkNsaWVudF9TZWNyZXQ=.
下記にアクセスします。
https://www.base64decode.org/先ほど、メモしておいた、「Client ID」と「Client Secret」を使い、
自分のClient ID:自分のClient Secretをエンコードします。
※ClientIDとClientSecretの間に「:」があるので、お忘れなく。エンコードされた文字列をメモしておきます。
参考:https://marketplace.zoom.us/docs/guides/auth/oauth
以上で、事前準備は完了です。
3.認証コードの取得
Client_idに自分のクライアントIDを入力して、認証コード生成用のURLを作成します。
例)↓
https://zoom.us/oauth/authorize?response_type=code&client_id=あなたのクライアントID&redirect_uri=http://localhost:8000
URLにアクセスすると、Zoom API の認証が始まります。
「Authorize」をクリックして、自分のアカウントでのZoomの操作をZoom APIを通して行えるようにします。
認証に成功すると、画面遷移するので、code以下の部分をメモしておきます。
4.アクセストークンの発行
アクセストークンは以下のコードで発行できます。
(先にローカルPCの実行環境でnpm install requestを実行しておきます。)getAccessToken.jsvar request = require("request"); var options = { method: "POST", url: "https://zoom.us/oauth/token", qs: { grant_type: "authorization_code", code: "we473poKUZ_3dXwRRJrSumqswISsV4q1g", // 3.認証コードの取得 で取得したコードを入力する。 redirect_uri: "http://localhost:8000", }, headers: { Authorization: "Basic NjZxT3dmWElTN205RVdfdXV4UWFTZzppc21nQWVud3I0SmcyN0Z3YnE0UUE1TFB4QW1tSVBzdg", // さっきメモしたエンコードした文字列を「Basic 」の後に入れる。 }, }; request(options, function (error, response, body) { if (error) throw new Error(error); console.log(body); });
node getAccessToken.jsを実行します。
すると下記のような結果を得られます。$ node getAccessToken.js {"access_token":"eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI2YjhkMDg4Ny0xNWViLTRiODAtYWM2NC0yNTgzNDdiOTQ4NTIifQ.eyJ2ZXIiOjcsImF1aWQiOiI3ZGJkM2Q3NzVlMGQ3NTFjYzljZWQxMTUzMGJjYmM2ZiIsImNvZGUiOiJ3ZTQ3M3BvS1VaXzNkWHdSUkpyU3VtcXN3SVNzVjRxMWciLCJpc3MiOiJ6bTpjaWQ6NjZxT3dmWElTN205RVdfdXV4UWFTZyIsImdubyI6MCwidHlwZSI6MCwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiIzZFh3UlJKclN1bXFzd0lTc1Y0cTFnIiwibmJmIjoxNjEzODMxODQzLCJleHAiOjE2MTM4MzU0NDMsImlhdCI6MTYxMzgzMTg0MywiYWlkIjoidEtoaHhtZVVUZG1VT3lfaGF3Sko3dyIsImp0aSI6IjBiZDdhODQ3LTVmYTktNDRiNC1iYTM3LThkNDNkNjdjOThjNSJ9.MwILIjAoWSTQJCfh1TYc3BJIVK7ptyCVv4g5h82oNc_GLLL6_S9RHD_D4MUSvZMKzt_vtLHK7DszMb3rYD5tig","token_type":"bearer","refresh_token":"eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiJmNGQ3ODMxNC1lYmM3LTQzYTQtOThiMC1jODc1OGMzOTAxMzUifQ.eyJ2ZXIiOjcsImF1aWQiOiI3ZGJkM2Q3NzVlMGQ3NTFjYzljZWQxMTUzMGJjYmM2ZiIsImNvZGUiOiJ3ZTQ3M3BvS1VaXzNkWHdSUkpyU3VtcXN3SVNzVjRxMWciLCJpc3MiOiJ6bTpjaWQ6NjZxT3dmWElTN205RVdfdXV4UWFTZyIsImdubyI6MCwidHlwZSI6MSwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiIzZFh3UlJKclN1bXFzd0lTc1Y0cTFnIiwibmJmIjoxNjEzODMxODQzLCJleHAiOjIwODY4NzE4NDMsImlhdCI6MTYxMzgzMTg0MywiYWlkIjoidEtoaHhtZVVUZG1VT3lfaGF3Sko3dyIsImp0aSI6ImRmZTExNDgwLWY1NGQtNDFkMy04YTAyLWZhMTIzODUxYWZjNCJ9.CMSJQL9R6NCJFqisJ9dmz4r5yQCJB5-cotFgfWHXz_RosDVJ4ct9LJ25weixveL_7ZaXGmr5SHCZxqREov7kXA","expires_in":3599,"scope":"meeting:write:admin"}↑ここで発行されたaccess_tokenを下記「create_meetings.js」のheadersに「authorization: "Bearer [access_token]"」となるように貼り付けます。
create_meetings.jsvar http = require("https"); var options = { method: "POST", hostname: "api.zoom.us", port: null, path: "/v2/users/me/meetings", headers: { "content-type": "application/json", authorization: "Bearer eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiIwNzNjODYyZS05NGZjLTRiODYtYTM1Yy1lNTdjNjJkZTVjYjcifQ.eyJ2ZXIiOjcsImF1aWQiOiJiODZkMzNmNDQwOWNjNzI1MWFjNTA1OTI1YWU5ZWU1MCIsImNvZGUiOiJUQnpzeHFlMTJJXzNkWHdSUkpyU3VtcXN3SVNzVjRxMWciLCJpc3MiOiJ6bTpjaWQ6Y2NFdmFTMERUYnFQR3JVQTRBMVVudyIsImdubyI6MCwidHlwZSI6MCwidGlkIjowLCJhdWQiOiJodHRwczovL29hdXRoLnpvb20udXMiLCJ1aWQiOiIzZFh3UlJKclN1bXFzd0lTc1Y0cTFnIiwibmJmIjoxNjEzNjU3ODAzLCJleHAiOjE2MTM2NjE0MDMsImlhdCI6MTYxMzY1NzgwMywiYWlkIjoidEtoaHhtZVVUZG1VT3lfaGF3Sko3dyIsImp0aSI6IjBlNTNlZTQ2LWMwMjMtNDI0Ny1iNTgxLWYyYThkYTkyYjAwYSJ9.A6jiK0MIAw6EBurd0Gg0Z3me3MuhTvfwEvZ77RgTgtiODjZTmwkA0vPzCKbyGXjcMbeY96oY2Y5uWKvvBY2sYA", }, }; var req = http.request(options, function (res) { var chunks = []; res.on("data", function (chunk) { chunks.push(chunk); }); res.on("end", function () { var body = Buffer.concat(chunks); console.log(body.toString()); }); }); req.write( JSON.stringify({ // 作成するZoomイベントの詳細を入力します。 // 設定値については、下記URLの「Request Body」の「Schema」という個所を参照してください。 // https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate topic: "[Zoom API]Node.js sample - ミーティングルームの作成", type: 2, start_time: "2021-02-25T10:00:00Z", duration: 60, timezone: "Asia/Tokyo", password: "12345", agenda: "Zoom API のテスト", settings: { host_video: true, participant_video: false, cn_meeting: false, in_meeting: false, join_before_host: true, jbh_time: 0, mute_upon_entry: true, watermark: true, use_pmi: false, approval_type: 0, audio: "both", auto_recording: "none", }, }) ); req.end();
node create_meeting.jsを実行します。$ node create_meeting.js {"uuid":............................,"id":.........,}成功すると、上記の用に、作成されたミーティングルームに紐づくいろいろな情報が取得できます。
また、Zoom Meetings Pageで下記画像のように、ミーティングが作成さているはずです。
以上です。参考にしたサイト
https://marketplace.zoom.us/docs/api-reference/zoom-api/meetings/meetingcreate
https://marketplace.zoom.us/docs/guides/auth/oauth
- 投稿日:2021-02-21T13:59:43+09:00
【Node.js】WindowsでNode.jsの環境構築をする
プログラミング勉強日記
2021年2月21日
友人にNode.jsの環境構築を教えるために一度調べたので、そのやり方をまとめる。Node.jsをインストールする
Node.jsの公式サイトからNode.jsのインストーラーをダウンロードする。(今回は推奨版をダウンロードした。)
インストーラーをダウンロードしたら、開いて実行する。その後指示にしたがtt進めていく。基本的にはそのまま
Nextをクリックして進めていく。最後にFinishを押して終了する。Node.jsのバージョンを確認する
それぞれコマンドで下記を実行すると、バージョンを確認することができる。バージョンが表示されない場合は、インストールができていない。
$ node ^v $ npm -vバージョンが表示されている場合C:\>node -v v12.18.4 C:\>npm -v 6.14.6
- 投稿日:2021-02-21T13:31:39+09:00
HerokuでNode.jsのアプリからPostgreSQLに接続できない時の対処
N予備校 【2020年度】プログラミング入門 Webアプリ
[第4章24節 【サービス開発9】セキュリティ対策と公開]Herokuの設定で詰まったので解決した手順を記載。
Database / GitHubのOAuthなどの設定を終えてHerokuのアプリを起動し、ログインをしようとすると下記のエラーが発生
2021-02-21T03:05:26.710859+00:00 app[web.1]: Unhandled rejection SequelizeConnectionError: no pg_hba.conf entry for host "XX.XXX.XXX.XXX", user "xxxxxxxxxx", database "xxxxxxxxxxxx", SSL off 2021-02-21T03:05:26.710870+00:00 app[web.1]: at connection.connect.err (/app/node_modules/sequelize/lib/dialects/postgres/connection-manager.js:182:24) 2021-02-21T03:05:26.710870+00:00 app[web.1]: at Connection.connectingErrorHandler (/app/node_modules/pg/lib/client.js:194:14) 2021-02-21T03:05:26.710871+00:00 app[web.1]: at Connection.emit (events.js:198:13) 2021-02-21T03:05:26.710872+00:00 app[web.1]: at Socket.<anonymous> (/app/node_modules/pg/lib/connection.js:134:12) 2021-02-21T03:05:26.710872+00:00 app[web.1]: at Socket.emit (events.js:198:13) 2021-02-21T03:05:26.710872+00:00 app[web.1]: at addChunk (_stream_readable.js:288:12) 2021-02-21T03:05:26.710873+00:00 app[web.1]: at readableAddChunk (_stream_readable.js:269:11) 2021-02-21T03:05:26.710874+00:00 app[web.1]: at Socket.Readable.push (_stream_readable.js:224:10) 2021-02-21T03:05:26.710874+00:00 app[web.1]: at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) 2021-02-21T03:05:56.475031+00:00 heroku[router]: at=error code=H12 desc="Request timeout" method=GET path="/auth/github/callback?code=025f7e4e57093209889a" host=limitless-lowlands-54630.herokuapp.com request_id=31dc51eb-fd89-48fb-9155-a08b997709de fwd="153.207.171.208" dyno=web.1 connect=1ms service=30001ms status=503 bytes=0 protocol=https・アプリからDB接続ができない (pg_hba.confの設定が足りない?)
・GitHubの認証がタイムアウトする (一つ前のDB接続エラー起因かはこの時点では不明)エラーメッセージと pg_hba.conf、Herokuでぐぐってみたところ、解決方法としてHerokuの環境変数でDB接続をSSL化するか、node-jsのソース側でこのエラー制御を無視することができるようでした。
環境変数の追加が簡単そうだったので、方法1で対処しました。
【方法1】
参考にした記事
https://qiita.com/hiro93n/items/fe015ce9517f139edbebHerokuに環境変数を設定する
heroku config:set PGSSLMODE=require環境変数を更新したらHerokuが再起動して接続できるようになり、GitHub認証のタイムアウトエラーも解消しました。
2021-02-21T03:14:46.096182+00:00 app[api]: Release v11 created by user xxxxxxxxxx 2021-02-21T03:14:46.096182+00:00 app[api]: Set PGSSLMODE config vars by user xxxxxxxxxx 2021-02-21T03:14:46.573285+00:00 heroku[web.1]: Restarting 2021-02-21T03:14:46.590224+00:00 heroku[web.1]: State changed from up to starting 2021-02-21T03:14:48.006684+00:00 heroku[web.1]: Stopping all processes with SIGTERM 2021-02-21T03:14:48.223861+00:00 heroku[web.1]: Process exited with status 143 2021-02-21T03:14:50.316993+00:00 heroku[web.1]: Starting process with command `npm start`Warningの中に、node-jsが理由を説明しているログがありました。
2021-02-21T03:14:53.846841+00:00 app[web.1]: (node:21) DeprecationWarning: Implicit disabling of certificate verification is deprecated and will be removed in pg 8. Specify `rejectUnauthorized: true` to require a valid CA or `rejectUnauthorized: false` to explicitly opt out of MITM protection.暗黙的な証明書検証の無効化は非推奨となっていますので、pg 8で削除されます。rejectUnauthorized: true で証明書の検証をしているものをrejectUnauthorized: falseと指定することで、明示的に中間者攻撃防御を解除してください。
【方法2】
Herokuの公式ドキュメント[Heroku Posgres]
https://devcenter.heroku.com/articles/heroku-postgresql#connecting-in-node-js先ほどの理由からPosgreのDB接続用のインスタンスを生成するときに、ssl証明書検証の設定をfalseに明示的に設定する。
const client = new Client({ connectionString: process.env.DATABASE_URL, ssl: { rejectUnauthorized: false } });今回は方法1で接続できましたが、方法2も公式ドキュメントの情報なので問題なく解決できそうです。
もし4章の最後の節で詰まってる方がいましたらご参考まで。
- 投稿日:2021-02-21T12:59:36+09:00
Slackにバーンダウンチャートを定期的に送信するボットの作成方法
概要
近年、スクラムを導入した開発チームが増えつつあります。
スクラムチームの質を向上させるため、プロジェクト管理だけではなくて、チームメンバーが開発状況を把握しやすくすることが重要だと思います。今回は、JIRAのREST APIを使って、最新のスプリント情報を取得すると、バーンダウンチャートを生成して、日常的にSlackのChannelに送信するBotを紹介します。
要求仕様
node 12.16.1
npm 6.13.4JIRA REST API テスト
まずはChrome API Tester Toolを使って、JIRA APIのテストを行う。(スクラムボードのみ支持)
METHOD: GET
SCHEMA: https://{hostname}.atlassian.net/rest/agile/1.0/board/{rapidViewId}/sprint/{sprintId}/issueSlack Botを作成する
Botの作り方はワークスペースで利用するボットの作成を参照してください。
現在、元宵節を迎えることになりますので、MagicalYuanxiao(中国語:神奇小元宵)と名付けました。
次は、作成済みのAppを送信したいChannelに連携する。
Slack Bot送信テスト
HTTPクライアントのインストール
$ npm install axiosメッセージを送信する
sendMsg.js// test file // Run "node sendMsg.js" to test access slack token // If send message to channel is successfully, the connection is successful const axios = require("axios"); const MSG_URL = "https://slack.com/api/chat.postMessage"; const slackToken= "Your slack token"; // *Slack Bot User OAuth Access Token* async function run() { const res = await axios.post( MSG_URL, { channel: "#test", // *send to target channel* text: "Hello, I am magical yuanxiao!", // message content }, { headers: { authorization: `Bearer ${slackToken}`, }, } ); console.log("Done", res.data); } run().catch((err) => console.log(err));実行コマンド
$ node sendMsg.jsライブラリの導入
・アクセスJIRA(https://www.npmjs.com/package/jira-client)
・ファイルとディレクトリの読み取りと書き込み(https://www.npmjs.com/package/fs)
・HTTPリクエスト(https://www.npmjs.com/package/request)
・時間データの操作(https://www.npmjs.com/package/moment)
・グラフ描画(https://github.com/SeanSobey/ChartjsNodeCanvas)
・環境変数の設定(https://www.npmjs.com/package/dotenv)スプリントデータを保存する
app.js// jira API options const jira = new JiraApi({ protocol: "https", host: jiraHost, username: jiraUsername, password: jiraPassword, apiVersion: "3", strictSSL: true, }); // today's date const YYYYMMDD = moment().format("YYYYMMDD"); // step1: get sprint info to generate json from jira let sprint = {}; await jira .getSprintIssues(rapidViewId, sprintId) .then(function (issues) { sprint["id"] = issues.sprint.id; // スプリントID sprint["name"] = issues.sprint.name; // スプリント名 sprint["goal"] = issues.sprint.goal ? issues.sprint.goal : 0; // 目標点数 sprint["startDate"] = moment(issues.sprint.isoStartDate).format( "YYYYMMDD" ); // 開始日 sprint["endDate"] = moment(issues.sprint.isoEndDate).format("YYYYMMDD"); // 終了日 sprint["issuesPointSum"] = issues.contents.completedIssuesEstimateSum .value ? issues.contents.completedIssuesEstimateSum.value : 0 + issues.contents.issuesNotCompletedEstimateSum.value ? issues.contents.issuesNotCompletedEstimateSum.value : 0; // ストーリー点数合計 sprint["notCompletedIssuesPointSum"] = issues.contents .issuesNotCompletedEstimateSum.value ? issues.contents.issuesNotCompletedEstimateSum.value : 0; // 未完了ストーリー点数合計(include: todo, doing, review...) }) .catch(function (err) { console.error(err); }); // json data to be written let jsonData = { code: 0, data: sprint, updateDate: moment().format("YYYY/MM/DD HH:mm:ss"), msg: "success", }; // format json let text = JSON.stringify(jsonData); // directory and file name let file = path.join("./output/", YYYYMMDD + "_sprint_data.json"); // write into json await fs.writeFile(file, text, function (err) { if (err) { console.log(err); } else { console.log("File was successfully created: " + file); } });チャートデータを用意する
app.js// step2: get data for line chart var startAndEndDateDiff = moment(sprint.endDate).diff( moment(sprint.startDate), "days" ); // X axis labels for lint chart var xLables = []; for (let i = 0; i <= startAndEndDateDiff; i++) { xLables[i] = moment(sprint.startDate).add(i, "days").format("MM/DD"); } // Y axis values for lint chart var yValues = []; // from 0 to date difference(from today to startDate) var dateDifferenceArray = []; for (let i = 0; i <= moment().diff(moment(sprint.startDate), "days"); i++) { dateDifferenceArray.push(i); } var jsonArray = dateDifferenceArray.reverse().map(getJsonAsync); await Promise.all(jsonArray) .then(function (jsonData) { // 本スプリントの日別残ポイントデータを埋め込む yValues = jsonData.map((s) => s.data.notCompletedIssuesPointSum); }) .catch(function (err) { console.error(err); }); // guideline Y axis values var guidelineValues = []; for (let i = 0; i <= startAndEndDateDiff; i++) { guidelineValues.push( sprint.goal - Math.floor((sprint.goal * i) / startAndEndDateDiff) ); }バーンダウンチャートを作る
app.js// step3: create burn down chart image const height = 400; const width = 700; const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height }); (async () => { const configuration = { type: "line", data: { labels: xLables, datasets: [ { label: "Story Points remaining", borderColor: "rgba(255, 100, 100, 1)", data: yValues, fill: false, }, { label: "Guideline", borderColor: "rgba(122, 122, 122, 1)", borderDash: [10, 3], // dotted line data: guidelineValues, fill: false, borderWidth: 1, }, ], }, options: { scales: { yAxes: [ { ticks: { beginAtZero: true, // set Y min value to 0 }, }, ], }, elements: { point: { radius: 0, // do not show points }, }, title: { display: true, fontSize: 16, text: sprint.name + "(" + moment(sprint.startDate).format("MM/DD") + "~" + moment(sprint.endDate).format("MM/DD") + ")", // chart title }, }, }; const dataUrl = await chartJSNodeCanvas.renderToDataURL(configuration); const base64Data = dataUrl.replace(/^data:image\/png;base64,/, ""); await fs.writeFile( "./output/" + YYYYMMDD + "_burn_down_chart.png", base64Data, "base64", function (err) { if (err) { console.log(err); } } ); })();Slackにバーンダウンチャートを送信する
app.js// step4: upload created chart to slack request.post( { url: UPLOAD_URL, formData: { file: fs.createReadStream( "./output/" + YYYYMMDD + "_burn_down_chart.png" ), token: slackToken, filetype: "png", filename: YYYYMMDD + "_burn_down_chart.png", channels: slackChannel, // send to XXX channel title: YYYYMMDD + "_burn_down_chart.png", // show this name in slack }, }, function (error, response, body) { if (error) { console.log(error); } else { console.log("Send burn down chart to slack."); // console.log(body); } } );Slackスクリーンショット
以上でnodejsの実装部分は完成です。
あとは定時バッチなど行えば定期的にSlackにpostする予定です。Source Code (GitHub)
そのうちリポジトリのコード載せる(予定)
- 投稿日:2021-02-21T12:59:36+09:00
バーンダウンチャートを送信するSlackBotの作成
概要
近年、スクラムを導入した開発チームが増えつつあります。
スクラムチームの質を向上させるため、プロジェクト管理者だけではなくて、チームメンバーにも開発状況を把握しやすくすることが重要だと思います。今回は、JIRA REST APIを使って、最新のスプリント情報を取得すると、バーンダウンチャートを生成して、日常的にSlackのChannelに送信するBotを紹介します。
要求仕様
node 12.20.1
npm 6.14.10JIRA REST API テスト
まずはChrome API Tester Toolを使って、JIRA Agile REST APIのテストを行う。(スクラムボードのみ支持)
METHOD: GET
URL: https://{your-domain}.atlassian.net/rest/agile/1.0/board/{boardId}/sprint/{sprintId}/issueSlackBot を作成する
Botの作り方はワークスペースで利用するボットの作成を参照してください。
現在、元宵節を迎えることになりますので、MagicalYuanxiao(中国語:神奇小元宵)と名付けました。
次に、アプリにスコープを設定するように(chat:write、files:write)の権限を追加しました。
設定後:
さらに、作成済みのAppを送信したいChannelに追加しました。
SlackBot 送信テスト
HTTPクライアントのインストール
$ npm install axiosメッセージを送信する
sendMsg.js// test file // Run "node sendMsg.js" to test access slack token // If send message to channel is successful, the token is OK const axios = require("axios"); const MSG_URL = "https://slack.com/api/chat.postMessage"; const slackToken = "Your slack token"; // *Slack Bot User OAuth Access Token* async function run() { const res = await axios.post( MSG_URL, { channel: "#test", // *send to target channel* text: "Hello, I am magical yuanxiao!", // message content }, { headers: { authorization: `Bearer ${slackToken}`, }, } ); console.log("Done", res.data); } run().catch((err) => console.log(err));実行コマンド
$ node sendMsg.jsSlackBot 設計と実装
ここからバーンダウンチャートを送信するSlackBot
MagicalYuanxiaoのワークフローや実装部分を簡単に説明します。ワークフロー
ライブラリの導入
・JIRAアクセス(https://www.npmjs.com/package/jira-client)
・ファイルとディレクトリの読み取りと書き込み(https://www.npmjs.com/package/fs)
・HTTPリクエスト(https://www.npmjs.com/package/request)
・時間データの操作(https://www.npmjs.com/package/moment)
・グラフ描画(https://github.com/SeanSobey/ChartjsNodeCanvas)
・環境変数の設定(https://www.npmjs.com/package/dotenv)スプリントデータを保存する
app.js// jira API options const jira = new JiraApi({ protocol: "https", host: jiraHost, username: jiraUsername, password: jiraPassword, apiVersion: "3", strictSSL: true, }); // today's date const YYYYMMDD = moment().format("YYYYMMDD"); // step1: get sprint info to generate json from jira let sprint = {}; await jira .getSprintIssues(rapidViewId, sprintId) .then(function (issues) { sprint["id"] = issues.sprint.id; // スプリントID sprint["name"] = issues.sprint.name; // スプリント名 sprint["goal"] = issues.sprint.goal ? issues.sprint.goal : 0; // 目標点数 sprint["startDate"] = moment(issues.sprint.isoStartDate).format( "YYYYMMDD" ); // 開始日 sprint["endDate"] = moment(issues.sprint.isoEndDate).format("YYYYMMDD"); // 終了日 sprint["issuesPointSum"] = issues.contents.completedIssuesEstimateSum .value ? issues.contents.completedIssuesEstimateSum.value : 0 + issues.contents.issuesNotCompletedEstimateSum.value ? issues.contents.issuesNotCompletedEstimateSum.value : 0; // ストーリー点数合計 sprint["notCompletedIssuesPointSum"] = issues.contents .issuesNotCompletedEstimateSum.value ? issues.contents.issuesNotCompletedEstimateSum.value : 0; // 未完了ストーリー点数合計(include: todo, doing, review...) }) .catch(function (err) { console.error(err); }); // json data to be written let jsonData = { code: 0, data: sprint, updateDate: moment().format("YYYY/MM/DD HH:mm:ss"), msg: "success", }; // format json let text = JSON.stringify(jsonData); // params: directory and file name let file = path.join("./output/", YYYYMMDD + "_sprint_data.json"); // write into json await fs.writeFile(file, text, function (err) { if (err) { console.log(err); } else { console.log("File was successfully created: " + file); } });チャートデータを用意する
app.js// step2: get data for line chart var startAndEndDateDiff = moment(sprint.endDate).diff( moment(sprint.startDate), "days" ); // X axis labels for line chart var xLabels = []; for (let i = 0; i <= startAndEndDateDiff; i++) { xLabels[i] = moment(sprint.startDate).add(i, "days").format("MM/DD"); } // Y axis values for line chart var yValues = []; // from 0 to date difference(from today to startDate) var dateDifferenceArray = []; for (let i = 0; i <= moment().diff(moment(sprint.startDate), "days"); i++) { dateDifferenceArray.push(i); } var jsonArray = dateDifferenceArray.reverse().map(getJsonAsync); await Promise.all(jsonArray) .then(function (jsonData) { // 本スプリントの日別残ポイントデータを埋め込む yValues = jsonData.map((s) => s.data.notCompletedIssuesPointSum); }) .catch(function (err) { console.error(err); }); // guideline Y axis values var guidelineValues = []; for (let i = 0; i <= startAndEndDateDiff; i++) { guidelineValues.push( sprint.goal - Math.floor((sprint.goal * i) / startAndEndDateDiff) ); }バーンダウンチャートを作成する
app.js// step3: create burn down chart image const height = 400; const width = 700; const chartJSNodeCanvas = new ChartJSNodeCanvas({ width, height }); (async () => { const configuration = { type: "line", data: { labels: xLabels, datasets: [ { label: "Story Points remaining", borderColor: "rgba(255, 100, 100, 1)", data: yValues, fill: false, tension: 0, // straight line }, { label: "Guideline", borderColor: "rgba(122, 122, 122, 1)", borderDash: [10, 3], // dotted line data: guidelineValues, fill: false, borderWidth: 1, tension: 0, }, ], }, options: { scales: { yAxes: [ { ticks: { beginAtZero: true, // set Y min value to 0 }, }, ], }, elements: { point: { radius: 0, // do not show points }, }, title: { display: true, fontSize: 16, text: sprint.name + "(" + moment(sprint.startDate).format("MM/DD") + "~" + moment(sprint.endDate).format("MM/DD") + ")", // chart title }, }, }; const dataUrl = await chartJSNodeCanvas.renderToDataURL(configuration); const base64Data = dataUrl.replace(/^data:image\/png;base64,/, ""); await fs.writeFile( "./output/" + YYYYMMDD + "_burn_down_chart.png", base64Data, "base64", function (err) { if (err) { console.log(err); } } ); })();Slackにバーンダウンチャートを送信する
app.js// step4: upload created chart to slack request.post( { url: UPLOAD_URL, formData: { file: fs.createReadStream( "./output/" + YYYYMMDD + "_burn_down_chart.png" ), token: slackToken, filetype: "png", filename: YYYYMMDD + "_burn_down_chart.png", channels: slackChannel, // send to XXX channel title: YYYYMMDD + "_burn_down_chart.png", // show this name in slack }, }, function (error, response, body) { if (error) { console.log(error); } else { console.log("Send burn down chart to slack."); } } );実行結果
実行コマンド
$ node app.jsSlackのスクリーンショット
以上でnodejsの実装部分は完成です。あとは定時ジョブ行えば日常的にSlackにpostする予定です。
Source Code (GitHub)
https://github.com/peepa857/magical-yuanxiao
参照サイト
Python編記事-Slackに定期的にバーンダウンチャートを投稿するBotの作成方法
Jira Software でバーンダウンチャートを使用する方法
JIRA REST APIで課題情報を取得(jira-client-npmを使用)
Working with the Slack API in Node.js
node.jsでファイルの入出力操作
Chart.jsによるチャート作成検討事項
・ガイドラインについて、祝日と休日の判定処理
・JSON読み取りについて、本スプリント先日のデータが存在していないエラーの対処方法(期間中のみ)
・カスタム看板に対して戻り値が正しいかどうか検証する















