- 投稿日:2019-05-30T23:15:33+09:00
Callback 使いから Promise マスターにクラスチェンジするぞ!
現在私は、非同期処理をするときは
callbackを使っています。
ですが、ネストが深くなるにつれて混乱してしまいます。( ゚Д゚)<メダパニ‼Node.js には
promiseという callback 地獄を解消するためのオブジェクトが用意されています。
promiseでの処理を覚えれば、見やすいコーディングが可能です!…と、言うことは理解できているのですが、何故か未だにあやふやな promise 処理。
こりゃもう、身体で覚えるしかねぇ!( ゚Д゚)
憧れの PROMISE マスターに、なりたいな。ならなくちゃ。絶対になってやるー!(*‘∀‘)幸い(?)なことに前回作った LINEWORKS の BOT 登録プログラムが callback 地獄になっているので、こちらを promise に書き換えてみたいと思います。
(前回の記事)API を使って LINEWORKS BOT を登録する
registerBot.jsconst api_id = "api id"; const consumer_key = "consumer key"; const token = "server list token"; const account_id = "ryo_saeba@hunter.city"; const domain_id = 00000000; const callback_url = "https;//www.xyz.jp/callback"; // トーク Bot のテナント登録 const request = require('request'); const uri_text = "https://apis.worksmobile.com/" + api_id; let options = { uri: uri_text + "/message/registerBot/v4", headers: { "Content-type": "application/json", "consumerKey": consumer_key, "Authorization": "Bearer " + token }, json: { "name": "test bot", "photoUrl": "https://developers.worksmobile.com/favicon.png", "description": "defeat the promise process bot", "managerList": [account_id] } }; request.post(options, (error, response, body) => { if(body.errorMessage) return; let botNo = body.botNo; // トーク Bot のドメイン登録 options.uri = uri_text + "/message/registerBotDomain/v3" options.json = { "botNo": botNo, "domainId": domain_id, "usePublic": true, "usePermission": false } request.post(options, (error, response, body) => { if(body.errorMessage) return; // メッセージ受信サーバー追加 options.uri = uri_text + "/message/setCallback/v2"; options.json = { "botNo": botNo, "callbackUrl": callback_url, "callbackEventList": ["text", "sticker", "image"] }; request.post(options, (error, response, body) => { if(body.errorMessage) return; // トーク Bot からメッセージ送信 options.uri = uri_text + "/message/sendMessage/v2", options.json = { "botNo": botNo, "accountId": account_id, "content": {"type":"text","text":"BOT 登録が完了しました(^ω^)"} }; request.post(options, (error, response, body) => { console.log(body); if(body.errorMessage) return; console.log("success"); }); }); }); });このコードを promise 処理したコードに置き換えていくのですが、このあとしばらく私の恥ずかしい失敗談が続きますので「お前の失敗なんざどうでもいいから成功したコードを見せてくれ!」という一般的なご意見をお持ちの方は、是非!読み飛ばして一番最後の完成コードをご確認くださいませ。
promise に置き換えるには(失敗談)
さぁ、それではこの callback たちを promise に置き換えてみましょう!
「簡単だよ! .then でつなげばいいんだよ!」という先輩エンジニアのアドバイスの元、よく理解していないまま私が書いたコードがこちらです!registerBot.js// パラメータ等は前回と同じなので割愛 const request = require('request'); const uri_text = "https://apis.worksmobile.com/" + api_id; // トーク Bot のテナント登録 new Promise((resolve,reject) => { request.post(options, (error, response, body) => { (body.errorMessage) ? reject() : resolve(body.botNo); }) }).then((botNo) => { // トーク Bot のドメイン登録 options.uri = uri_text + "/message/registerBotDomain/v3" options.json = { "botNo": botNo, "domainId": domain_id, "usePublic": true, "usePermission": false } request.post(options, (error, response, body) => { (body.errorMessage) ? reject() : resolve(botNo); }) }).then((botNo) => { // メッセージ受信サーバー追加 options.uri = uri_text + "/message/setCallback/v2"; options.json = { "botNo": botNo, "callbackUrl": callback_url, "callbackEventList": ["text", "sticker", "image"] }; request.post(options, (error, response, body) => { (body.errorMessage) ? reject() : resolve(botNo); }) }).then((botNo) => { // トーク Bot からメッセージ送信 options.uri = uri_text + "/message/sendMessage/v2", options.json = { "botNo": botNo, "accountId": account_id, "content": {"type":"text","text":"BOT 登録が完了しました(^ω^)"} }; request.post(options, (error, response, body) => { if(body.errorMessage) reject(); console.log("success"); resolve(); }); });今見ると、ひっどいですねぇ~。恥さらしですな。
のちに、このコードを見た先輩が「なんでこうなるのか、本気でわけわからん」とマジ顔でおっしゃられました。
きっと、今この記事を読んでいる皆様も同じ顔をしているのでしょう。
恥ずかしさのあまり、つい山籠もりをしてしたくなりますが、後回しにして先に進みます。上記コードですが、もちろんエラーになりました!(/・ω・)/
ReferenceError: reject is not definedようは「なんで何度も reject してんの?」ってことです。
コンピュータさん、頭の悪い子でごめんなさい。promise にとって、
rejectは一回こっきり。
最初にnewするときだけなのです。元のコードでやっていたエラー処理を
returnから単にrejectに置き換えたのがいけない。
理解が浅いからこういうことになるのです。
まったく恥ずかしいやつですね!(/ω\).catch でエラー処理をする
ってなわけで、たくさんの
rejectをひとつにすべく.catchでエラー処理をしていきます!
promise オブジェクトでは.catchでエラー処理いっぺんに指定できてしまうんです!
なんて素晴らしい仕様!
処理が複雑になり、.thenの数が増えていっても、エラー処理は1つだけ。
わかりやすいし、見やすいですね~。
ってなわけで.catchを追加してエラー処理を1つにまとめたものがコチラ。registerBot.js// パラメータ等は前回と同じなので割愛 const request = require('request'); const uri_text = "https://apis.worksmobile.com/" + api_id; // トーク Bot のテナント登録 new Promise((resolve,reject) => { request.post(options, (error, response, body) => { (body.errorMessage) ? reject() : resolve(body.botNo); }) }).then((botNo) => { // トーク Bot のドメイン登録 options.uri = uri_text + "/message/registerBotDomain/v3" options.json = { "botNo": botNo, "domainId": domain_id, "usePublic": true, "usePermission": false } request.post(options, () => {}); }).then((botNo) => { // メッセージ受信サーバー追加 options.uri = uri_text + "/message/setCallback/v2"; options.json = { "botNo": botNo, "callbackUrl": callback_url, "callbackEventList": ["text", "sticker", "image"] }; request.post(options, () => {}); }).then((botNo) => { // トーク Bot からメッセージ送信 options.uri = uri_text + "/message/sendMessage/v2", options.json = { "botNo": botNo, "accountId": account_id, "content": {"type":"text","text":"BOT 登録が完了しました(^ω^)"} }; request.post(options, () => {}); }).catch((err) => { // エラー処理 console.log(err); });おー!見やすくなったぞー(*´▽`)
などと最初は喜んだおバカな私。もちろんエラーになりましたとさ。ぎゃふん。
TypeError: Cannot read property 'botNo' of undefinedまぁ、ちゃんとエラーが
catchできていたので良しとしましょう。return で次の .then に値を渡す
さて、肝心のエラー内容ですが「botNo が空っぽだよ」と言われています。
そう、returnしていないので、次の.thenに何にも渡していないんですよね!
超絶おバカですね。
return botNo;
return文をそれぞれの.thenに追加して、これで大丈夫だろう!と実行した私。
エラーメッセージ出なかったぞ!やったぁ!成功だ!と、喜んだのも束の間。待てど暮らせど登録完了メッセージが送信されてこない。
「トーク Bot からメッセージ送信 API」の実行ログを見てみると、以下のエラーが。
{ errorMessage: 'Service fail, HTTP/1.1 400 Bad Request, {"code":400,"message":"Bad Request Parameters: Cannot access bot"}',
errorCode: '090',
code: 'SERVICE_UNAVAILABLE' }ふむ。Bot にアクセスできないとな?・・・なんで?
これ、2つ目の「トーク Bot のドメイン登録」をしていないメッセージ送信すると起きる現象なんですよね。
試しにもう一度トーク Bot からメッセージ送信 API を叩いてみる。…成功。つまり、だ。今はメッセージ送信ができて、登録時には、できない。
.thenで非同期処理をしているはずなのに、ドメイン登録時のrequestの結果を待たずに次の.thenに進んでいって、ドメイン登録完了前にメッセージを送信している?
つまーり!非同期処理になってないっ!!どうして!?.then 内の request は結局 promise 化していない
どうしても何も、私がよく理解していないまま使ったのが悪いのです。反省。orz
色々と調べてみたところ、.thenの中のrequest.post()は promise 化されていない模様。
promise 化されていないってことは非同期処理されていないってことで、もちろん順番はめちゃめちゃに。よし、トーク Bot のテナント登録の際に promise 化した request を使おう!
と、そこでふと思った。いちいち request を promise 化するの、面倒くさくね?
面倒くさいということは!誰かが便利なものを作っているはず!そうして私は
request-promiseと出会いました。先輩の皆様、ありがとうございます。
では、さっそくnpm install request-promiseしてコードを書き替えていきたいと思います。registerBot.js// パラメータ等は前回と同じなので割愛 const request = require('request-promise'); let botNo; // トーク Bot のテナント登録 request(options).then((body) => { botNo = body.botNo; // トーク Bot のドメイン登録 options.uri = uri_text + "/message/registerBotDomain/v3" options.json = { "botNo": botNo, "domainId": domain_id, "usePublic": true, "usePermission": false } return request(options); }).then((body) => { // メッセージ受信サーバー追加 options.uri = uri_text + "/message/setCallback/v2"; options.json = { "botNo": botNo, "callbackUrl": callback_url, "callbackEventList": ["text", "sticker", "image"] }; return request(options); }).then((body) => { // トーク Bot からメッセージ送信 options.uri = uri_text + "/message/sendMessage/v2", options.json = { "botNo": botNo, "accountId": account_id, "content": {"type":"text","text":"BOT 登録が完了しました(^ω^)"} }; return request(options); }).catch((err) => { console.log(err); });
return request(options);に書き換えているので、botNoが次の.thenに渡っていかないことに注意。
一番最初のbodyでbotNoを変数に入れておきましょう!さぁ、実行してみましょう!ドン!
だーいせーこーう♪ (*‘∀‘)<promise bot ゲットだぜ!
思った通りの動作をしてくれたので、良かった良かったホッと一息。
これで終わった!と、思いきや…。
実はある落とし穴があったことに、このときの私は気づいていなかったのだ。。。エラー処理ができていない!?
さっき、
.catchでエラー処理の設定をしました。
API ID や TOKEN などのパラメータの入力が間違っていた場合には、.catchに飛んでいってエラーを表示してくれるはず。
しかし、.catchには飛ばず、次の.thenに進んでいってしまい、エラーを連発しているのです!register-tenant { errorMessage: 'API ID not exists', errorCode: '052', code: 'NOT_FOUND' } register-domain { errorMessage: 'API ID not exists', errorCode: '052', code: 'NOT_FOUND' } register-callback { errorMessage: 'API ID not exists', errorCode: '052', code: 'NOT_FOUND' } send-message { errorMessage: 'API ID not exists', errorCode: '052', code: 'NOT_FOUND' }ナントイウコトデショウ
LINEWORKS API のエラーは .catch しない
これ、今回大変勉強になったところです。
例えばなんですが、requestする際にuriがNULLであるとか、パラメータの型指定が間違っているとかだと、ちゃんと.catchしてくれるのですよ。
でも、LINEWORKS API からの返答がエラーだった場合には.catchしてくれないのです。request 自体は成功しているから、API がエラーだって言おうが request は成功してるもん!
ってことなんですね。API に届いた時点で、成功。
その先でエラーだった場合には返ってきた値を見て.catchに投げる必要があるんですね。
try ~ catchにおけるthrowをする必要があるんですね。throw しないで、reject しよう
JavaScript Promise の本に書かれていますが、
promiseオブジェクトを使うときはPromise.rejectを使います。
bodyの中を見てエラーだったらPromise.reject(body);とすれば.catchに飛んでエラー表示してくれます。LINEWORKS API からの Response がエラーかどうかを判別するには、
errorCodeまたはerrorMessageが含まれているかを判定すれば良いでしょう。
なので、下記一文を諸所に追加します。
if(body.errorMessage) return Promise.reject(body);これで、ついに完成となりました!
完成したコードです!
registerBot.jsconst api_id = "api id"; const consumer_key = "consumer key"; const token = "server list token"; const account_id = "ryo_saeba@hunter.city"; const domain_id = 00000000; const callback_url = "https;//www.xyz.jp/callback"; const request = require('request-promise'); const uri_text = "https://apis.worksmobile.com/" + api_id; // トーク Bot のテナント登録 let options = { method: "POST", uri: uri_text + "/message/registerBot/v4", headers: { "Content-type": "application/json", "consumerKey": consumer_key, "Authorization": "Bearer " + token }, json: { "name": "test bot", "photoUrl": "https://developers.worksmobile.com/favicon.png", "description": "defeat the promise process bot", "managerList": [account_id] } }; let botNo; console.log("register-tenant"); request(options).then((body) => { if(body.errorMessage) return Promise.reject(body); console.log(body); botNo = body.botNo; // トーク Bot のドメイン登録 options.uri = uri_text + "/message/registerBotDomain/v3" options.json = { "botNo": botNo, "domainId": domain_id, "usePublic": true, "usePermission": false } console.log("register-domain"); return request(options); }).then((body) => { if(body.errorMessage) return Promise.reject(body); console.log(body); // メッセージ受信サーバー追加 options.uri = uri_text + "/message/setCallback/v2"; options.json = { "botNo": botNo, "callbackUrl": callback_url, "callbackEventList": ["text", "sticker", "image"] }; console.log("register-callback"); return request(options); }).then((body) => { if(body.errorMessage) return Promise.reject(body); console.log(body); // トーク Bot からメッセージ送信 options.uri = uri_text + "/message/sendMessage/v2", options.json = { "botNo": botNo, "accountId": account_id, "content": {"type":"text","text":"BOT 登録が完了しました(^ω^)"} }; console.log("send-message"); return request(options); }).then((body) => { if(body.errorMessage) return Promise.reject(body); console.log(body); }).catch((err) => { console.log("error:") console.log(err); });うん、長い!( ゚Д゚)
おわりに
ここまでお付き合いいただきありがとうございました。いや、本当に。
ここまでの道のりも長かったし、比例して記事も長くなってしまいました。
結局、コードの長さ的にはCallbackと変わらなかったなぁ。でも!ネストしていないから見やすいし、
promiseオブジェクトの構造がわかってると、こっちの方が拡張や修正はしやすいかも!
promise.finallyとかPromise.allとか、まだ仲良くなれていない子たちがいるから、憧れの PROMISE マスターへの道のりはまだまだ遠いけど、諦めずに頑張りたいと思います!(^ω^)ではまた!(^^)/
参考にさせていただきましたm(_ _)m
JavaScript Promiseの本
LINEWORKS DevelopersJavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる
今更だけどPromise入門
CoffeeScriptでPromiseを使ったときにハマった
request-promiseを使ったHTTPクライアントを作る
- 投稿日:2019-05-30T20:00:18+09:00
Node.jsのジョブキューbull使用感メモ
github: https://github.com/OptimalBits/bull
npm: https://www.npmjs.com/package/bullリトライ
デフォルトではリトライしない。
queueに対してではなく、jobをaddするときにオプションでattempsやbackoffを指定する。
https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#queueaddBackoffはfixed(固定)の他にexponentialもある。
failedイベントはリトライの度に発火するので、
たとえばattempsを5としてずっと失敗すると5回走る。今何回目のリトライなのかは
job.attemptsMadeで取得できる。
最終的に失敗したかどうかをハンドルする方法がわからない。
completedイベントは成功のときにしか発火しなかった。
errorイベントは全く発火しなかった。
以下のように試行回数を比較すれば一応できる。queue.on('failed', job => { logger.debug(`job[${job.id}] failed ${job.attemptsMade} times`) if (job.attemptsMade === job.opts.attempts) { logger.error(`finally, failed job[[${job.id}]]`) } })
- 投稿日:2019-05-30T18:17:36+09:00
ゼロから始める生体認証webアプリケーション作成(3)クライアント上での画像加工
はじめに
はじめに謝らせていただきます...
前回の記事で使ったopencv4nodejsですが....localホスト上で使う方法がわからなかったのでクビになりました。
代わりに本家opencvさんを使います。1.node.js+express+jage環境構築
Expressの開発環境構築~デバッグ環境構築 ここを参考にしましょう。
私が書くより絶対わかりやすい。expressの中身はこんな感じになっています。
作ったスクリプトなどはpublicにHTML(拡張子が.jade似合っているけど...)はviewsに入れます。
他もいじれるけど、私みたいな初心者は触らないに限る。2.スクリプトの修正
受け取った画像を100x100に縮小して、グレイスケール化します
processing_cv.jset imgElement = document.getElementById('imageSrc'); let inputElement = document.getElementById('fileInput'); inputElement.addEventListener('change', (e) => { imgElement.src = URL.createObjectURL(e.target.files[0]); }, false); imgElement.onload = function() { let input_img = cv.imread(imgElement); let resize_img = new cv.Mat(); let dsize = new cv.Size(100, 100); // 100x100にリサイズ cv.resize(input_img, resize_img, dsize, 0, 0, cv.INTER_AREA); input_img.delete(); let gray_img = new cv.Mat(); //グレイスケール化 cv.cvtColor(resize_img,gray_img, cv.COLOR_RGBA2GRAY, 0); cv.imshow('canvasOutput', gray_img); resize_img.delete(); gray_img.delete(); }; function onOpenCvReady() { document.getElementById('status').innerHTML = 'OpenCV.js is ready.'; }前回とやっていることは同じですが....ずいぶん長くなってしまいます。このスクリプトの保存場所はpublic/javascripts/です。
3.layout設定
opencv公式チュートリアルにあったテンプレートを参考にjade仕様に書き換えました。
layout.jadedoctype html html head meta(charset='utf-8') title= title link(rel='stylesheet', href='/stylesheets/style.css') body h2 Hello OpenCV.js p#status OpenCV.js is loading... div .inputoutput img#imageSrc(alt='No Image') | .caption | imageSrc input#fileInput(type='file', name='file') | .inputoutput canvas#canvasOutput | .caption canvasOutput script(src="/javascripts/processing_cv4.js",type='text/javascript') script(async='', src='https://docs.opencv.org/3.4.1/opencv.js', onload='onOpenCvReady();', type='text/javascript')4.表示の確認
localhost:3000に繋ぐとこんな感じに表示されます
次に有名なlennaの画像を与えてみます。
縮小してグレイスケール化した画像が出力されたことが確認できました。
まとめ
localhost上での画像加工を行うことができた。
次回は、少しjavascriptから離れて、顔認証システムを機械学習を用いて実装します。
- 投稿日:2019-05-30T18:17:36+09:00
ゼロから始める生体認証webアプリケーション作成(3)localhost上での画像加工
はじめに
はじめに謝らせていただきます...
前回の記事で使ったopencv4nodejsですが....localホスト上で使う方法がわからなかったのでクビになりました。
代わりに本家opencvさんを使います。1.node.js+express+jage環境構築
Expressの開発環境構築~デバッグ環境構築 ここを参考にしましょう。
私が書くより絶対わかりやすい。expressの中身はこんな感じになっています。
作ったスクリプトなどはpublicにHTML(拡張子が.jade似合っているけど...)はviewsに入れます。
他もいじれるけど、私みたいな初心者は触らないに限る。2.スクリプトの修正
受け取った画像を100x100に縮小して、グレイスケール化します
processing_cv.jset imgElement = document.getElementById('imageSrc'); let inputElement = document.getElementById('fileInput'); inputElement.addEventListener('change', (e) => { imgElement.src = URL.createObjectURL(e.target.files[0]); }, false); imgElement.onload = function() { let input_img = cv.imread(imgElement); let resize_img = new cv.Mat(); let dsize = new cv.Size(100, 100); // 100x100にリサイズ cv.resize(input_img, resize_img, dsize, 0, 0, cv.INTER_AREA); input_img.delete(); let gray_img = new cv.Mat(); //グレイスケール化 cv.cvtColor(resize_img,gray_img, cv.COLOR_RGBA2GRAY, 0); cv.imshow('canvasOutput', gray_img); resize_img.delete(); gray_img.delete(); }; function onOpenCvReady() { document.getElementById('status').innerHTML = 'OpenCV.js is ready.'; }前回とやっていることは同じですが....ずいぶん長くなってしまいます。このスクリプトの保存場所はpublic/javascripts/です。
3.layout設定
opencv公式チュートリアルにあったテンプレートを参考にjade仕様に書き換えました。
layout.jadedoctype html html head meta(charset='utf-8') title= title link(rel='stylesheet', href='/stylesheets/style.css') body h2 Hello OpenCV.js p#status OpenCV.js is loading... div .inputoutput img#imageSrc(alt='No Image') | .caption | imageSrc input#fileInput(type='file', name='file') | .inputoutput canvas#canvasOutput | .caption canvasOutput script(src="/javascripts/processing_cv4.js",type='text/javascript') script(async='', src='https://docs.opencv.org/3.4.1/opencv.js', onload='onOpenCvReady();', type='text/javascript')4.表示の確認
localhost:3000に繋ぐとこんな感じに表示されます
次に有名なlennaの画像を与えてみます。
縮小してグレイスケール化した画像が出力されたことが確認できました。
まとめ
localhost上での画像加工を行うことができた。
次回は、少しjavascriptから離れて、顔認証システムを機械学習を用いて実装します。
- 投稿日:2019-05-30T15:37:32+09:00
Webpack で ビルド時、環境ごとに読み込む環境変数を分けたい
概要
ほとんどの Laravel などのバックエンドフレームワークは環境変数ファイルが用意され、環境ごとに読み込まれる環境変数を動的に変えることができる機能が備わっています。
しかし、javascript の場合は環境変数を環境ごとに読み込む機能を自分で設定するか npm パッケージ を利用するしかなかったので、自分的ベストな方法をまとめておきます。結論
Webpack の alias 機能を使えばパッケージやライブラリなしで環境変数を分けることができます!!!!!!
設定方法
今回編集するファイルは下記ファイルになります。
webpack.config.js package.json前提環境
開発環境は node サーバをローカルにたてて開発。
本番、ステージサーバは CI・CD を利用してビルド後デプロイするといった環境で行いました。フォルダ構成
フォルダ構成は下記のとおり Webpack を利用した際の一般的な javascript の開発構成です。
環境変数をステージ毎に切り替えるための .env フォルダを用意しています。このフォルダにステージごとの環境変数を定義した js ファイルを用意します。.env |_ dev.js |_ stg.js |_ prd.js dist |_ main.js src |_ index.js node_modules package-lock.json package.json webpack.config.js環境変数ファイルの作成
.env フォルダ内に環境変数用の javascript ファイルを作成します。
dev.jsexport default { apiUrl : 'https://dev-example.com' }prd.jsexport default { apiUrl : 'https://example.com' }このような感じでステージごとの環境変数を設定していきます。
package.json の scripts 設定
package.json に
npm run <ステージ>の実行コマンドを定義します。
ステージごとに環境変数定義ファイルを動的に読み込むためにNODE_ENV=stgを設定します。
NODE_ENVはwebpack.config.jsファイルで参照できる環境変数になります。環境変数の名前は何でも大丈夫です。
下記のようにステージごとに渡してあげる環境変数を変えてあげましょう。package.json{ "scripts": { "stg": "NODE_ENV=stg webpack --mode production", "prd": "NODE_ENV=prd webpack --mode production", "start": "NODE_ENV=dev webpack-dev-server --mode development --hot --inline --watch-content-base" }, // 省略webpack の設定
webpack.config.js に alias の設定を行います。
webpack.config.jsconst environment = process.env.NODE_ENV || 'dev'; module.exports = { mode: 'development', resolve: { alias: { userEnv$: path.resolve(__dirname, `.env/${environment}.js`), }, }, // 省略 }alias は 深くネストされた javascript ファイルのパスをいちいち書くのがめんどくさい人用にエイリアスとして設定できるようにしてくれる Webpack の機能です。
この alias を利用してステージ毎に環境変数ファイルを動的に読み込むように設定しています。
process.env.NODE_ENVは package.json で定義した環境変数が取得できます。
この環境変数によって、読み込みたい環境変数が定義されたファイルを動的に変更します。
※環境変数を定義したファイルまでのパスは絶対パスで指定しましょう。userEnv$: path.resolve(__dirname, `.env/${environment}.js`)以上で設定は終わりです。
利用方法
javascript 側で環境変数を利用するときは下記のように import してあげましょう。
test.jsimport userEnv from 'userEnv'; console.log(userEnv.apiUrl);あとはローカルや CI・CDで npm コマンドを実行しましょう。
// ローカル開発環境 npm run start // CI・CDスクリプト npm run stg npm run prdこれでステージごとに環境変数を読み込むことができました。
環境変数を動的に切り替えるパッケージが結構あるみたいですが alias を使う方法だとパッケージいらずに環境変数を切り替えられるので簡単に利用できると思います。※ ちなみに node サーバではなくフロント側の環境変数の設定方法なので漏れてはいけない key などの情報は設定しないでね。全部漏れちゃいますので
環境変数用のパッケージに
cross-envやdotenvなどがありますがそれらを利用する場合の違いやメリット・デメリットがよくわかってないのでわかる人いたら教えてくださいm(_ _)m
- 投稿日:2019-05-30T15:06:05+09:00
Node.jsで文字列を暗号化・復号する 【string encryption in node.js】
どれだけニーズがあるのかわからないが、データベースに入ってるidとかを必要な時に難読化したかった。
例えば、
example.com/1/profileみたいなURLだと、「1のところを2に変えたら他の人のプロフィール見えるんじゃないの」みたいなのをトライできないようにしたい。で、とりあえずstringを入力したらそいつをencryptした文字列を返却するモジュールを作ってみよう。
加えて、Adminの立場的にはencryptされた文字列を見るのはめんどくさいのでdecryptもできるようにしよう。nodeにはcryptoという暗号化周りの組み込みモジュールが存在するので、そいつを利用する。
ドキュメント: https://nodejs.org/api/crypto.html
const crypto = require('crypto'); const ENCRYPTION_KEY = "HH95XH7sYAbznRBJSUE9W8RQxzQIGSpy" // 32Byte. このまま利用しないこと! const BUFFER_KEY = "RfHBdAR5RJHqp5wm" // 16Byte. このまま利用しないこと! const ENCRYPT_METHOD = "aes-256-cbc" // 暗号化方式 const ENCODING = "hex" // 暗号化時のencoding const rawString = JSON.stringify({userId: 1}) // 暗号化する対象。stringなら何でも。 function getEncryptedString(raw) { let iv = Buffer.from(BUFFER_KEY) let cipher = crypto.createCipheriv(ENCRYPT_METHOD, Buffer.from(ENCRYPTION_KEY), iv) let encrypted = cipher.update(raw) encrypted = Buffer.concat([encrypted, cipher.final()]) return encrypted.toString(ENCODING) } function getDecryptedString(encrypted) { let iv = Buffer.from(BUFFER_KEY) let encryptedText = Buffer.from(encrypted, ENCODING) let decipher = crypto.createDecipheriv(ENCRYPT_METHOD, Buffer.from(ENCRYPTION_KEY), iv) let decrypted = decipher.update(encryptedText) decrypted = Buffer.concat([decrypted, decipher.final()]) return decrypted.toString() } // 以下は試してみてるだけ console.log(`raw: ${raw}`) const encrypted = getEncryptedString(raw) console.log(`encrypted: ${encrypted}`) const decrypted = getDecryptedString(encrypted) console.log(`decrypted: ${decrypted}`)上記をコピペして適当なファイル名(encryption-sample.js とか)をつけて、
node encryption-sample.jsを叩くと下記のようなアウトプットになるはずraw: {"userId":1} encrypted: e3629d8ff7295f693569359d06fd4da2 decrypted: {"userId":1}今回は、getEncryptedString()に対してあるstring: Aを渡した場合に、どんな場合でも同じ結果string: A'が返ってきてほしかったので、
let iv = Buffer.from(BUFFER_KEY)の部分で必ず同じiv (= initialization vector)が生成されるようにしています。ただし、用途によっては毎回異なるivを生成し、encryption結果が毎回変わることが正な場合もある(というか本来の目的ではそちらのほうが正?)ので気をつけて下さい。
https://gist.github.com/vlucas/2bd40f62d20c1d49237a109d491974eb
それについては上記のコードが参考になると思います。
というか僕が参考にしました。
- 投稿日:2019-05-30T14:49:03+09:00
はじめての Nuxt.js
Nuxt はずっと前から気にはなってて、でも「今回は SSR しないからいいや」とか「今回は静的サイトジェネレーターで作るからいいや」とか思って避けてきたけど、なんか最近 Nuxt 使うって話を 3 回くらい聞いたので、ちょっと使ってみて構成とか眺めてみようと思う
先に使ってみた感想を述べておくと
- Nuxt の公式ドキュメントがちゃんと作られているので、これだけ見れば大体解決した
- 後述してますが TypeScript とか Vuex とかとても簡単に導入できるので感動した
- webpack 周りが隠蔽されてる
- 前に VueCLI v2 使ってみた時はけっこうビルド周りがむき出しになってた気がする(VueCLI v3 ではこの辺解決されてるっぽい)
- Vue で SSR する時に Nuxt を使うという印象を持っていたが、SSR モードか SPA モードか選べたので考えが違った
- フロントエンドチームが多いチームであれば、Angular などのガッチリ系フレームワークも検討しようと思っていたが、Vue が好きなら Nuxt でもいいかもしれない
- TypeScript のサポートも昔よりはかなり良くなってて、Vue v3 がリリース(今年リリースされる?)されたら Vue 自体が TypeScript で作り直されるので、さらに Vue + Nuxt + TypeScript 環境が良くなっていくのではないかと思う
今回作業したリポジトリは以下
https://github.com/kurosame/nuxt-boilerplateインストール
create-nuxt-appを使うと良さそう
npx でいけるとかすばらしいnpx create-nuxt-app nuxt-boilerplateプロジェクト名は GitHub のリポジトリ名にした
? Project name nuxt-boilerplate ? Project description My groundbreaking Nuxt.js project ? Use a custom server framework express ? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Prettier, Axios ? Use a custom UI framework vuetify ? Use a custom test framework jest ? Choose rendering mode Universal ? Author name kurosame ? Choose a package manager yarnとりあえず全部入れといたが、以下に該当する場合はインストールから除外してもいいと思う
(でも環境を CLI ツールで管理する時点で環境周りをメンテナンスすることは基本的に無いと思うので、全部入れてしまっても問題無いと思う)
- SSR しない
? Use a custom server framework None ? Choose rendering mode SPA
- モバイル対応しない
? Choose features to install Progressive Web App (PWA) Supportを選択しない
- HTTP リクエストしない(ほぼ無いと思うが)
? Choose features to install Axiosを選択しない
- デザインは全部自前
? Use a custom UI framework Noneサーバーの Node フレームワークはあまり詳しくないが、特に今使っているのが無ければ Express か Koa あたりを選んでおけば良いと思う
Vuetify は 1 年くらい使ってるが、かなり完成度高い UI フレームワークと思っているので、入れた
Linter や Prettier や Jest などのテストフレームワークは必須で入れておいた方が良い実行
package.json を開くと、いくつかスクリプトが設定してあるが、とりあえず以下を実行
yarn dev2 回目以降は
node_modules/.cacheの下にbabel-loaderのキャッシュを作ってる影響からか、少し速くなっている
でもホットリローディングをサポートしているので、1 回起動すれば遅さは気にならない
nodemonを使って./serverディレクトリ内のファイルを監視してサーバの再起動を行っている画面が表示されたら HTML ソースを見てみると、
server-rendered="true"及び HTML の各要素がそのままレンダリングされていると思う
ちゃんと SSR されていることが分かる
ちなみに SPA モードであれば、JS が実行されてから画面がレンダリングされるので、読み込む JS ファイルのみが指定してあり、HTML はスタイルなどを除けばほとんど空であるまた、実行すると
.nuxtというディレクトリができており、サーバを立ち上げた時に読み込ませるコンテンツが格納されている
./server/index.jsを見てみると、Nuxt のインスタンスを作成して、Promise を返すnuxt.renderを Express に Middleware としてセットしているしばらく開発で使うのは、dev スクリプトだけで良さそう
以下の他のスクリプトはある程度 Nuxt でアプリケーション作った後にちゃんと使ってみようと思う
- build
- minify して
.nuxt/dist/に格納- start
- production モードでサーバを立ち上げる
- generate
- Vue を静的コンテンツとしてビルドして
dist/に格納してくれる- Netlify や Firebase などでそのままホスティングできる(はず)
- VuePress 使ったことがあれば、それ
使ってみる
ちょっとだけ使ってみてやったことや思ったことを書いてみようと思う
TypeScript を使う
yarn add -D @nuxt/typescript ts-node
tsconfig.jsonがあればnuxtコマンドで勝手にデフォルト値を書いてくれるらしいtouch tsconfig.json npx nuxt先程インストールした
ts-nodeは Node で TS をトランスパイルせずに実行できるツールでこれが無いとnpx nuxtが動かないこれで導入は完了
後は、拡張子を
.jsから.tsに変えて中身を書き換えていく
以下はserver/index.jsを TS に修正している例server/index.tsimport NuxtConfiguration from '@nuxt/config' ... // Import and Set Nuxt.js options const config: NuxtConfiguration = require('../nuxt.config.js') config.dev = !(process.env.NODE_ENV === 'production') ...TS 導入前は上記の
config.devが any 型だったが、導入後はちゃんと型定義の boolean 型を参照しているpackage.json も忘れずに修正
package.json"scripts": { - "dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server", + "dev": "cross-env NODE_ENV=development nodemon server/index.ts --watch server", - "start": "cross-env NODE_ENV=production node server/index.js", + "start": "cross-env NODE_ENV=production ts-node server/index.ts", }Vue コンポーネントの TS 化についてはドキュメントでは
vue-property-decoratorの使用を薦めているyarn add vue-property-decorator
layouts/default.vueを例に修正すると<script lang="ts"> import { Component, Vue } from 'vue-property-decorator' @Component class Default extends Vue { clipped: boolean = false drawer: boolean = false fixed: boolean = false items: { icon: string; title: string; to: string }[] = [ { icon: 'apps', title: 'Welcome', to: '/' }, { icon: 'bubble_chart', title: 'Inspire', to: '/inspire' } ] miniVariant: boolean = false right: boolean = true rightDrawer: boolean = false title: string = 'Vuetify.js' } export default Default </script>ちなみに
vue-property-decoratorを使わなくても書ける<script lang="ts"> import Vue from 'vue' export default Vue.extend({ data(): { clipped: boolean drawer: boolean fixed: boolean ... } { return { clipped: false, drawer: false, fixed: false, ... } } }) </script>Vuex を使う
Nuxt をインストールした時にルート直下に store ディレクトリができており、既に Vuex 入ってる感があるが、中身は空である
Nuxt は store ディレクトリが存在すれば Vuex をインポートしてくれて、store をルートインスタンスに流してくれるらしいなので
- Vuex を手動で入れる必要がない
- どのコンポーネントからでも
this.$storeで参照できるまた、モジュールモードとクラシックモードの 2 つのモードがあるが、クラシックモードは廃止予定らしい
肥大化してくるとモジュールごとに State が参照できた方が良いので、モジュールモードで良さそうNuxt 側でモジュールモードとクラシックモードのどちらで Vuex インスタンスが作られるかの判定は
store/index.(js|ts)が存在して、Store インスタンスをエクスポートしていれば、クラシックモード
それ以外はモジュールモードモジュールモードで
store/index.(js|ts)という名前で作った場合、ルートモジュールという扱いで Store インスタンスの直下に State が存在する
store/モジュール名.(js|ts)という名前で作った場合、名前付きモジュールとして Store インスタンスの modules プロパティ配下に State が存在するそれぞれの State を参照する場合
- ルートモジュールは
this.$store.state.counterで参照できる- ルート以外は
this.$store.state.todos.listで参照できる
- 上記は
store/todos.(js|ts)というファイルを作った場合ちなみに私は上記のルートモジュールと呼ばれてる Store インスタンスの直下に State を置くのは 1 度もやったことない
以下のファイルを store ディレクトリ配下に追加
store/sample.tsinterface IState { counter: number } export const state: IState = { counter: 123 }簡単すぎる例だが、ファイルを追加するだけで Store に counter が保持できる
そして以下のように参照する
適当なVue<script> export default { mounted() { console.log(this.$store.state.sample.counter) // 123 } } </script>また、Vuex のヘルパー関数も使える
適当なVue<script> import { mapState } from 'vuex' export default { computed: { ...mapState({ sample: 'sample' }) }, mounted() { console.log(this.sample.counter) // 123 } } </script>Linter とコードフォーマッターを使う
TS を使っているが、TSLint は非推奨となるらしいので、ESLint の TS パーサーを使う
.eslintrc.jsを見る限り ES(JS)だけの Linter で良ければ、Nuxt をインストールした時点で出来てるので、後は rules や extends でルールを追加するだけで良さそう
package.jsonを見るとeslint-plugin-prettierとeslint-config-prettierが依存パッケージとしてインストールされており、.eslintrc.jsの extends に prettier が設定してあるので、ESLint と Prettier の競合は気にしなくて良さそうTS 用にLinterを使う場合の修正箇所は以下
yarn add -D @typescript-eslint/eslint-plugin.eslintrc.jsparserOptions: { - parser: 'babel-eslint' + parser: '@typescript-eslint/parser' }, - plugins: ['prettier'], + plugins: ['@typescript-eslint', 'prettier'],package.json"scripts": { - "lint": "eslint --ext .js,.vue --ignore-path .gitignore .", + "lint": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .", }また、ESLint のルールだと以下のような import は誤検知して、「no-unused-vars」を出してしまう
import NuxtConfiguration from '@nuxt/config'この場合、以下のように ESLint のルールは握りつぶして、
@typescript-eslintの方のルールを有効化する必要があるらしい
こちらの記事に書いてありますが、いくつかこのような誤検知するルールがあるらしいので、その都度同様の対応が必要.eslintrc.jsrules: { 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'error' }Airbnb の ESLint ルールを入れてみる
yarn add -D eslint-config-airbnb-base.eslintrc.jsextends: [ 'airbnb-base', ... ]いい感じに動いているが、Vue とか Vuex は Nuxt に直接依存しており、こちらで
package.jsonに書く必要が無いため、以下のような Lint エラーが出てる.../nuxt-boilerplate/pages/index.vue 63:1 error 'vuex' should be listed in the project's dependencies. Run 'npm i -S vuex' to add it import/no-extraneous-dependencies .../nuxt-boilerplate/plugins/vuetify.js 1:1 error 'vue' should be listed in the project's dependencies. Run 'npm i -S vue' to add it import/no-extraneous-dependencies上記は以下のように対応可能
.eslintrc.jssettings: { 'import/core-modules': ['vue', 'vuex'] },他にも Nuxt と ESLint で競合するルールはありそう
vue-router を使う
Nuxt ではエントリーポイントとなる JS などで routes を書いて
new VueRouterをやる必要はない
pages ディレクトリ配下にルーターから直接呼ばれるコンポーネントを配置するだけで良い以下の 2 つを配置
pages/sample/index.vue<template> <span>サンプルインデックス!</span> </template>pages/sample/test.vue<template> <span>サンプルテスト!</span> </template>それぞれ以下のようにしてルーティングさせる
適当なVue<nuxt-link to="/sample">Go sample index</nuxt-link> <nuxt-link to="/sample/test">Go sample test</nuxt-link>ファイル名が
index.vueであれば、ディレクトリ名が path になり、それ以外のファイル名であれば、ディレクトリ名/ファイル名が path になる
先程の Vuex とルールが似ているパラメータ付きの動的ルーティングもファイル名もしくはディレクトリ名の先頭にアンダースコアを付けることで実現できるらしい
nuxt-link というコンポーネントは router-link を extends したコンポーネントとなっており、nuxt-link を使わずに今まで通り
<router-link>を使って実装することも可能
ただし、nuxt-link を使うと画面表示領域にリンクがあれば、そのリンク先のコンテンツを先読みしてくれるので、遷移が速くなる試しに、以下のコードを適当な Vue の下の方に配置して、ファーストビューから見えないようにして、スクロールしてリンクを表示させると
index.vueだけプリフェッチされることが分かる
Chrome DevTool の Network タブで確認すると分かりやすい適当なVue<nuxt-link to="/sample">Go sample index</nuxt-link> <router-link to="/sample/test">Go sample test</router-link>その他
選択した UI フレームワークでサンプルコードが作られている
私は Vuetify を選んだのだが、割としっかりサンプルコードが Vuetify で作られてて、全部の UI フレームワーク分作られてると思うとちゃんとしてるなって思った
テストのサンプルがほぼ無い
Logo コンポーネントの 1 ケースしかテストのサンプルコードが無い
E2E のサンプルコードは無いでも最近は Vue のテスト周りのエコシステムが充実してきて、ドキュメントも豊富なので、問題ないと思う
Nuxt のアップデートは簡単にできるのだろうか
Angular みたいなマイグレーション機能はたぶん無いと思うが、Nuxt とか CLI 系のツールを使っているとアップデートが楽にできるとかなりありがたい
簡単にネットで調べた限りは Nuxt を最新にして出たエラーを潰してるっぽいさいごに
主にツールの導入になってしまい、まだコードはほとんど書いてないのですが、これから ToDo アプリ程度のものは作ってみて、Nuxt を学んでみようかと思います
続きは別記事でまた書こうかなと思いますフロントエンドは全部 1 から作る派なのですが、たまに自動で環境作ってくれる系ツールを使うとそのお手本のような構成に勉強になることがあります
また、今後フロントエンドの環境を作る上で活かせることもあると思うので、試しに使ってみるのもありかなと思いました
- 投稿日:2019-05-30T14:47:09+09:00
【日記】lesscコマンドが効かない!→対処したら色々勉強になった
環境:macOS Mojave
MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)結論:インストール方法を変えたら解決したよ
sudo npm i less --save→npm install -g less
でもPATHが通ってないことが原因だから、パスを指定して実行した方がいいね。
最近、この本でWebアプリ開発を学んでいます。
JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう ~ その2(iOS対応版)途中、LESSファイルからCSSファイルを生成するためのlesscコマンドを実行しようとしたところ、ターミナルでエラーが発生しました。
$ lessc less/app.less css/app.css bash: lessc: command not foundLESSそのものは正しくインストールできているはず。
何がいけないのでしょうかとグーグル先生に尋ねてみたところ、どうにもPATHが通っていないのが原因の模様。PATHって自動で通してくれるものだとばかり思っていました。lessが入っている場所を確認してみると、ありましたありました。
/usr/bin/lessに存在することを無事確認です。んで、PATHを通すってどうやるのかなと調べていたら、下記の記事を発見。
MacでPATHを通す
なるほど、.bash_profileというファイルに書き込めばいいみたいです。$ ls -a . .gitignore_global Applications .. .gitkraken Desktop .CFUserTextEncoding .hgignore_global Documents .DS_Store .lesshst Downloads .Trash .local Google ドライブ .bash_history .mongorc.js Library .bash_profile .npm Movies .bash_sessions .pylint.d Music .config .ssh Pictures .dbshell .stCommitMsg Public .gitconfig .viminfo .gitflow_export .vscodeありますね。
PATHの通し方はexport PATH=$PATH:新しく設定するPATHというコマンドを実行すればいいみたいです。
その後sourceコマンドで変更を反映させるのも忘れないように。
最後に、中身を再度確認して反映が通っているかどうか確認します。$ export PATH=$PATH:/usr/bin/ atsushinoMacBook-Pro:~ anaakikutsushita$ source ~/.bash_profile atsushinoMacBook-Pro:~ anaakikutsushita$ printenv PATH /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/bin/
/usr/bin/が新たに追加されているのが確認できました。
しかし既に/usr/binが存在していたみたいですね・・・。これはダブっちゃってるみたいだけどいいのかな?とにもかくにも、PATHが通っている状態にはなったはず。
これでlesscコマンドくんも効いてくれるでしょう。$ lessc less/app.less css/app.css bash: lessc: command not foundだめやんけ
さらに調べていると、どうやらインストールの方法からしてちょっと違っていたみたい。
私は技術書通りにsudo npm i less --saveでlessをインストールしていました。
ところがネットの情報を見る限り、npm install -g lessのように-gオプションをつけてインストールしている方法も多く見受けられました。実際のところこの差がどういう違いになるのかは分かりませんが、とりあえずやってみることにしました。
$ npm install -g less /usr/local/bin/lessc -> /usr/local/lib/node_modules/less/bin/lessc + less@3.9.0 added 60 packages from 123 contributors in 2.057sあっ、なんだかインストール場所が変わりましたよ的な表示が出ている。
もしかしてこれで正しくPATHが通るようになったりしてないかしら。
もう一度lesscコマンドを試してみましょう。$ lessc less/app.less css/app.css atsushinoMacBook-Pro:hoge anaakikutsushita$ cd css atsushinoMacBook-Pro:css anaakikutsushita$ ls -a . .. app.cssなんとすんなり実行できました。
生成されたCSSの中身を確認しても特に問題なさそうです。となると、やはり
-gオプションをつけてインストールしたことが決め手だったのでしょうか。
このオプションは何を指定するものなのか、ちょっと調べてみましょう。
すぐに次のような記事を見つけることができました。私が知りたいところだけ抜き出してみると、次のような感じでしょうか。
-gをつけるとグローバル領域へのインストールとなり、自動的にPATHが設定される。-gをつけない場合は、実行時にパスを指定する必要が出てくる。- 一方、インストール先がグローバル領域じゃなかったらどうなるのと言うと、
--saveオプションを付けた場合package.jsonに記載されることになる。なんとなくわかったようなわからんような感じです。とりあえず今後はなるべく
-gオプションは使わない方がいいなと思いました。
package.jsonとかグローバル領域とはなんなんでしょう。こう言うように知らない概念が何個も何個も並列で出てくるとすっごい疲れるんですがいい方法ないものでしょうか。ざっと説明を読んでみたところ、
package.jsonはnpmでインストールしたライブラリの情報をまとめておくファイルのようですね。
確かに、プロジェクトがどのライブラリに依存しているのかと言う情報はどこかにまとめられていないと非常に面倒なので、このファイルのありがたさは理解できます。
そしてこのpackage.jsonファイルが存在していれば、プロジェクトをcloneした時なんかに依存関係にあるライブラリをまとめてインストールしてくれる機能もあると言うことみたいですね。先の記事でポータビリティを議論していたのはそのためでしたか。じゃあ、なんでもかんでもpackage.jsonに書くようにしちゃった方が平和じゃね?
確かにPATHを通せる利点はあるけど、グローバルにインストールする利点ってなに?
少し調べた限りではグローバルインストールのデメリットばかりが目につきます。
メリットらしいメリットはPATHが自動的に通ること以外には見つけられませんでした。
それどころか、グローバルインストールだとライブラリのバージョンを厳密に指定できないみたいな不都合さもあることがわかりました。一つ賢くなったので、グローバルにインストールしてしまったlessは一旦アンインストールしておきます。
$ npm uninstall -g less removed 60 packages in 0.623s $ lessc less/app.less css/app.css bash: /usr/local/bin/lessc: No such file or directoryこれでよし。
グローバルにインストールしたことで実行できていたlesscも、アンインストールしたことで実行できなくなったことが確認できました。改めてパス指定して
lesscコマンドを実行してみましょう。
lesscがインストールされている場所をあらかじめ確認しておきます。
node_modules/less/bin/lesscにありますね。
早速実行してみましょう。$ node_modules/less/bin/lessc less/app.less css/app.css $実行できました。
app.cssの方も正常に作成されました。思いがけず知らないことがたくさん出てきてお勉強が大変でしたが、この機会に知っておけてよかったです。
今後もグローバルインストールはナシの方向でやっていきましょう。実はローカルインストールした後でもパスを直接指定せずにラクな方法でコマンドを実行するワザがあるみたいですね。
しかしそこまでは流石に頭が回らなかったため、パス指定の方法が面倒でやってられない時がきた時にまた改めて調べてみたいと思います。
- 投稿日:2019-05-30T09:26:06+09:00
nodejs selenium でページ全体をスクショ
https://github.com/nishisuke/selenium-chrome-try
npm init npm i -S selenium-webdriverhttps://github.com/SeleniumHQ/selenium/tree/master/javascript/node/selenium-webdriver#installation
からdriverをインストール今回はchrome 74
PATHを通す
export PATH=${DOWNLOADED_CHROME_DRIVER}:$PATHindex.jsconst { Builder } = require('selenium-webdriver'); const fs = require('fs'); (async () => { const driver = await new Builder() .forBrowser('chrome') .setChromeOptions([ '--headless', '--disable-gpu', ]) .build(); try { await driver.get('http://www.google.com'); const base64 = await driver.takeScreenshot(); const buffer = Buffer.from(base64, 'base64'); fs.writeFileSync('screenshot.jpg', buffer); } catch (e) { console.log(e) } finally { await driver.quit(); } })();node index.jsnodeでdom要素のみのスクショはまだできないっぽいです
- 投稿日:2019-05-30T09:26:06+09:00
nodejs selenium でページ全体をスクショする
https://github.com/nishisuke/selenium-chrome-try
npm init npm i -S selenium-webdriverhttps://github.com/SeleniumHQ/selenium/tree/master/javascript/node/selenium-webdriver#installation
からdriverをインストール今回はchrome 74
PATHを通す
export PATH=${DOWNLOADED_CHROME_DRIVER}:$PATHindex.jsconst { Builder } = require('selenium-webdriver'); const fs = require('fs'); (async () => { const driver = await new Builder() .forBrowser('chrome') .setChromeOptions([ '--headless', '--disable-gpu', ]) .build(); try { await driver.get('http://www.google.com'); const base64 = await driver.takeScreenshot(); const buffer = Buffer.from(base64, 'base64'); fs.writeFileSync('screenshot.jpg', buffer); } catch (e) { console.log(e) } finally { await driver.quit(); } })();node index.jsnodeでdom要素のみのスクショはまだできないっぽいです
- 投稿日:2019-05-30T08:30:56+09:00
Web Push Notification のサンプル
まずは、次のコードを実行します。
Web Push Notification Sample for both Server and Clientgit clone https://github.com/otiai10/web-push-notification-sample cd web-push-notification-sample npm install node server.jsブラウザーで http://localhost'8080 にアクセスし次のボタンをクリックします。
次に server.js の trigger: の部分を次のように改造してみます。
server.js(省略) trigger: (req, res) => { message_aa = `皆さん こんにちは。 ` const icon = `img/${Math.floor(Math.random() * 3)}.png`; const params = { title: "You've got a push-notification!!", msg: message_aa + `Hi, this is message from server. It"s ${new Date().toLocaleString()} now. You can send any message, e.g. notification icons and so`, icon: icon, }; Promise.all(subscribers.map(subscription => { return webpush.sendNotification(subscription, JSON.stringify(params), {}); })).then(res => console.log(res)).catch(err => console.log('ERROR', err)); }, }; (省略)再び、サーバーを起動し、ブラウザーでアクセスします。
server.js の変更により、表示されるメッセージが変わります。
- 投稿日:2019-05-30T02:12:38+09:00
Node.jsでインフラのテスト、構成管理、オペレーション自動化
とあるプロジェクトで昨年から約1年半、Ansibleでサーバ構築をしていて書きづらいなーと思いながらDocker/Kubenetesとか使いたいけど、それに合わせたシステムの改修をが間に合うわけもなく……
という鬱々とした気持ちを抱えておりました。が、不平不満を言うだけでははじまらないと思い立って、自分の好きなように書けるツールを作成してみました
Submarine - https://gitlab.com/mjusui/submarine
私がAnsibleを書きづらいと感じたのは、別にAnsibleが悪いわけではなく、単にプログラミングパラダイムの問題です
結論から言うと、タイトルどおりNode.jsで書きたかったんです紹介の前にインフラエンジニア以外の人向けにAnsibleがどういうものなのか、簡単に説明しておきます
Ansibleとは
サーバ構築、構成管理や自動化のためのツール。特徴としては以下のようなものがあります
- コードは全部
YAMLで表現されるオブジェクト指向や関数の概念はなく、条件分岐とループのみで処理が進んでいく(ただしコードをinclude/importできるので多少の再利用性はある)- 変数は基本すべてグローバルアクセス可能で、再代入も自由(一部スコープを制限する機能はある)
- サーバ構築でよく使う機能(ファイルコピーなど)は
moduleという単位でAnsibleやサードパーティが用意してくれたものを使うmoduleを自前で作成することもできる- Ansible自体はPythonで開発されており、Pythonの機能や、パッケージを使って
moduleも開発されている- サーバには
sshさえできればよく、専用のエージェントをインストールする必要はないプログラミング経験があまりないインフラエンジニアにも分かりやすいよう、シンプルに設計されているという印象です
Submarineとは
Node.jsで開発するインフラ管理のFrameworkです。以下のような特徴があります
- Ansibleは構成管理がメインのツールですが、Submairneはサーバのテストもできます
- テストの結果に応じて、コマンドを実行する/しないが決定されます
- Classと継承を使って、コードの再利用性を高めています
- Node.jsの機能を使えば、変数の再代入を制限したり、スコープを限定することもできます
- サーバの状態を取得する関数、取得した状態をテストする関数、サーバに変更を加える関数を分離することで、安全で読みやすい(条件分岐の少ない)実装ができます
- HTTPサーバを起動して、上記で実装した関数をエンドポイントとして公開する機能があります
今回はSubmarineの基本的な機能をサンプルコードをまじえて、紹介していこうと思います
サーバの状態を取得する
まずはサーバから情報を取得します
Node.jsconst Submarine = require('Submarine'); const Tutrial = class extends Submarine { query(){ return { hostname: 'hostname -s', ipv4_addrs: String.raw` ip -o -f inet a \ |awk '{print $4}' ` }; } } const tut=new Tutrial({ conn: 'bash' }); tut.current().then((stats)=>{ console.log(stats); });Node.jsが読める人は感覚的にわかるかもしれませんが
Submarineというクラスを継承してTutrialというオリジナルのクラスを定義しています。そして、そのクラスをnewでインスタンス化してtut.current()というところでTutrialクラスに定義したquery関数が実行されますクラスをインスタンス化するときに
{ conn: 'bash' }という引数を与えることでTutrialクラスに定義したquery関数の中のコマンド(hostname -sなど)がlocalhostのbashで実行される、ということを指定していますクラスをインスタンス化するときに例えば
{ conn: 'ssh', host: <ip address> }といった具合に指定すれば、指定したIPアドレスにsshして、コマンドが実行されますサーバの状態をテストする
今度は取得した情報をテストします
Node.jsconst Submarine = require('Submarine'); const Tutrial = class extends Submarine { query(){ return { nonexecutable: String.raw` which none \ 2> /dev/null \ || exit 0 `, executable: String.raw` which node \ 2> /dev/null \ || exit 0 `, }; } test(stats){ return { none_is_not_executable: stats.nonexecutable === '', node_is_executable: stats.executable, }; } } const tut=new Tutrial({ conn: 'ssh', host: '127.0.0.1' }); tut.check().then((done)=>{ console.log(done); });
queryの部分でnoneというコマンドとnodeというコマンドのパスをwhichコマンドで引くことができるか確認しています。noneというコマンドは、普通は存在しませんからtestの中でパスが空''であることを検証しています。一方nodeというコマンドは、このNode.jsのコードが実行できている以上、どこかに存在しますからwhichコマンドの結果に何らかのパスが含まれていることでしょう。test関数の戻り値に含まれている2つの値(none_is_not_executableとnode_is_executable)はtut.check()という部分で評価され論理積(AND)でTrue/Falseが決定されます。結果はdone変数に格納されていますサーバに変更を加える
サーバの状態を取得して、それをテストしました。テストの結果が問題なければ、何もする必要はありませんが、テストで異常が発見された場合は、それを修正しなければなりません。テストがFalseになった場合にだけ実行されるコマンドを以下のように定義します
Node.jsconst Submarine = require('Submarine'); const Tutrial = class extends Submarine { query(){ return { file_content: String.raw` [ -r /tmp/submarine/hogehoge ] && { cat /tmp/submarine/hogehoge } || { echo 'File not readable' >&2 } ` }; } test(stats){ return { file_content_is_hogehoge: stats.file_content === 'hogehoge', }; } command(props){ return String.raw` mkdir -p /tmp/submarine \ && echo ${props.msg} > /tmp/submarine/hogehoge `; } } const tut=new Tutrial({ conn: 'ssh', host: '127.0.0.1' }); tut.correct({ msg: 'fugafuga' }).then((done)=>{ console.log(done); });
/tmp/submarine/hogehogeというファイルの内容が'hogehoge'であるか確認し、そうでない場合はファイルが作成され、内容は'hogehoge'で書き換えられます
doneにはコマンドが実行された場合は、実行したコマンドのreturn codeやstdoutの情報が格納され、実行されなかった場合はtest関数のときの結果が格納されます複数サーバーで実行する
上記の例は1台のサーバに対してコマンドを実行していましたが、複数台のサーバに実行することも可能です
Node.jsconst Submarine = require('Submarine'); const Tutrial = class extends Submarine { query(files){ return { availables: String.raw` df -P \ |awk '{print $4}' \ |grep "^[0-9]*$" ` }; } format(stats){ return { available_max: Array.isArray(stats.availables) ? stats.availables.map( available => available * 1 ).sort((a ,b)=>{ return a < b ? -1 : a == b ? 0 : 1; }).reverse()[0] : stats.availables * 1 }; } } const Tutrials = Submarine.hosts( host => new Tutrial({ conn: 'ssh', host: host }), server1, server2, server3, server4, server5 ); const tut = new Tutrials(); tut.current().then( hosts => hosts.map( host => host.available_max ).reduce((a, b)=>{ return a + b; }) / 1024 / 1024 ).then((available_sum)=>{ console.log(available_sum); });
server1からserver5がそれぞれnew Tutrial...でインスタンス化されtut.current()で全台に対してquery関数が評価されます。結果はホスト1台のときと同じフォーマットの結果が、配列で返されます(コードのhostsに格納されている)このサンプルコードでは、5台のサーバのディスク空き容量を足し合わせています
おわりに
他にも、Ansibleの基本的な機能は置き換えられるようか機能が実装されております
各機能のTutrialを鋭意作成中です
https://gitlab.com/mjusui/submarine/tree/master/doc/en/Tutrial
- 投稿日:2019-05-30T00:57:41+09:00
2019年に知っておくべきJava ScriptのフレームワークTOP5
こんにちは、ベイマックスのでかい人形を買おうかどうか迷って半年になるテノヒラです
最近チームでwikiを書く文化が芽生えつつあるんですよ。
わたしもビギナーに向けてトレンドとかをお伝えするものがかければと思って記事を漁っていたら、フレームワークについてこじんまりかついい感じにまとめてある素晴らしい記事を発見したので翻訳してご紹介します!
翻訳する記事はこちら?
Top Javascript frameworks you should know in 2019
世界のトレンドと日本のトレンドはちょっと違ったりするので要チェックですところどころ意訳をしていますが、もし致命的な翻訳ミスがありましたら編集リクエストしてください
はじめに
JavaScriptは世界で一番人気のあるプログラミング言語です。
インターネットでほぼ全てのwebサイトのフォームに使われています。
JavaScriptまたはJavaScriptのフレームワーク、ライブラリ関係なくどこでもJavaScriptを見つけることができます。JavaScriptはフロントエンドだけでなくサーバーサイドの言語として使われています。
いまではJavaScriptでwebサイトを完全に構築させることができるのです下記が2019年で最も人気なフレームワーク5選です。
1) Angular
2) React
3) NodeJS
4) Vue.js
5) Backbone.js
Angular
Angularは最も人気のあるJavaScriptのフレームワークで、Typescriptのオープンソースです。
最初は2016年9月14日にGoogleがリリースしました。
AngularはMVC (Model-View-Conrtoller)パターンをベースにしています。
これは完璧なフレームワークで、企業のwebアプリケーションを構築するすべてを提供しています。
なのでAngularはwebアプリケーションを構築する上で最も人気のあるJavaScriptなのです。
大企業では大抵採用されています。React
Reactはユーザーインターフェイスを創るためのオープンソースのライブラリです。
2013年にFacebookによって開発されました。
Reactはコンポーネントベースで拡張性があり、webインターフェースを構築するのに速いフレームワークです。
Angularと異なっている部分は、Reactはwebインターフェースを構築する機能を提供しています。最近はReactがwebインターフェースを構築する最も人気があるJavaScriptライブラリのひとつとなりました。
ただ、Reactは最も手のかかるJavaScriptのライブラリです。
需要高いのでやれるようになれば思わず笑みが溢れるよう給与がもらえますNode JS
Nodejs、Node.jsもしくはNodeはオープンソースで、Google ChromeのJavaScript V8 Engineに基づいたサーバーサイドのJavaScriptのフレームワークです。
主に速い構築、拡張性、サーバーサイドアプリケーションをベースとした信頼性の高いネットワークを主に設計しています。
Indeed.comには現在、8,905つの求人があります。
Node開発者の平均的な給与はだいたい 10万5千ドル(約1,148万円)です。Vue.js
Vueもしくはvue.jsはユーザーインターフェースを構築するための革新的なフレームワークです。
ライブラリのコアは、表示のレイヤーだけに焦点を合わせている、簡単に習得できる、他のライブラリもしくは既存のプロジェクトとの統合といった部分です。
違う視点からみると、Vueはモダンなツールとサポートしているライブラリとを一緒に使った時に洗礼されたシングルページアプリケーションにすることができます。
webページの表示に焦点を当てたVueもフロントエンド界隈では人気のあるフレームワークとなります。Indeed.comには現在、1,316つの求人があります。
平均的な給与はおよそ10万ドル(約1,093万円)もしくはそれ以上です。Backbone.js
BackboneもしくはBackbone.jsはRESTful JSONインターフェースのフレームワークで、model–view–presenter (MVP)アプリケーションデザインパターンをベースとしているものです。
Backboneは2010年にJeremy Ashkenasによってつくられ、7年後には最も人気のあるフレームワークのひとつとなりました。
Indeed.comには現在、5,135つの求人があります。
backboneフロントエンドとbackboneフルスタックエンジニアの平均的な給与はそれぞれおよそ10万2千ドル(約1,115万円)と10万1千ドル(約1,104万円)です。React Vs Angular
ReactとAngularはweb開発者によって最も討論されることです。
これらを学習したプログラマーと開発者にとってどちらを学ぶべきなのかは悩むところです。
webサイトを開発する人にとってはどちらを使うべきかを悩みます。
どちらを採用するかを決定するのはいつも課題となります。Angularは完全なweb applicationの構築する機能を提供しており、Reactはインターフェースだけを構築するのに使われているライブラリです。
アーキテクチャーの概念によると、Angularはモデルと表示、制御の機能を処理しますが、Reactは表示のみを処理します。
AngularはTypescriptをベースとしていますが、ReactはJavascript上で動きます。
ReactはFacebookによって開発されましたが、AngularはGoogleによって開発されています。
AngularのIonicはモバイルアプリケーションの構築に使われますが、
Reactはモバイルアプリケーション開発のためにReact Nativeを提供します。













