20190226のNode.jsに関する記事は7件です。

[AWS Lambda Tips] 自作ライブラリの保管場所は Lambda Layer じゃなくて GitHub にホストした npm package でも良かった

はじめに

以下の記事で「Lambda の共通モジュールを配置する場所は Lambda Layer くらいしかない」と書いたのですが、Github 上で公開した npm package でも同様の要件が満たせることがわかりました(なんでこんな初歩的なことを見落としてたのだろう。。)。

要件

  • 複数のLambda関数から利用できる、共通のライブラリーを1箇所で管理したい

解決方法

  1. 共通のライブラリーを Lambda Layer にデプロイする
  2. 共通のライブラリーを npm package として GitHub 上で公開する

共通のライブラリーを Lambda Layer にデプロイする

共通のライブラリーを Lambda Layer にデプロイすれば、他の Lambda Function から参照することができます。

pros

  • デプロイ箇所が Lambda Layer 1箇所なので、個々のLambda Function のそれぞれにデプロイするよりも早くデプロイできる。

cons

  • 管理するインフラリソースが1つ増えるので、アーキテクチャの複雑さは少し上昇する
    • つまり、考えないといけないことが増える
      • Lambda Layer にデプロイする資産のビルドやデプロイをどのように行うか?
      • デプロイされた資産と手元の資産で差分が生じた場合に確実に新しいリソースがデプロイされるか?
      • etc.

共通のライブラリーを npm package として GitHub 上で公開する

GitHub リポジトリーを npm のリポジトリのように使える方法があることを最近知りました。
https://qiita.com/billthelizard/items/b9571a3f276fdf24597c

この方法を使うと、Lambda Layer を使わなくても、ビルドプロセスで npm install するさいに共通のライブラリーを lambda function に含めることができます。

pros

  • 管理するインフラ要素が1つ減るので、アーキテクチャの複雑度が減少する
  • それによって、考えるべきことが減る

cons

  • 個々の Lambda Function に対して同じ資材を配布するので、デプロイに時間がかかる
    • デプロイ時間は Lambda Function の数に比例して増えます
    • とくに、 dependency として含まれる npm modules のファイル数が多い場合には注意です

まとめ

Lambda Function の共通モジュールの保管場所として、 Lambda Layer は1つの選択肢ですが、
管理するインフラ要素が増えることで考えることも増えます。
少ない数の Lambda Function にデプロイする場合や、 dependency に含まれる資材のファイル数が少ない場合は、 npm package として GitHub リポジトリで配布することも検討に入れましょう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

やはり俺のnodeの理解は間違っている

tl;dr

やっはろー!!

ノンエンジニア、ノンプログラマな友人知人がプログラミングを始めるぞい!ってなったときに、
だいたいProgateったりudemyったりドットインストールったりするものの、
そもそもノンプログラマのファーストチャレンジ用に
"こいつらなんなん?"
的なことをまとめてあるドキュメントが(自分の観測範囲に)なかったので、まとめてしまおうと思う。
nodeの話じゃないこともするかもしれない(そもそもCLIとかエディタとかがよくわからないレベルの方向けなので)けど、
なんとなくファーストトライの時に「node?npm?うぉー!!わけわからん!!」って人向けの説明記事になればいいなと思っている。
あとはなんか小中高生とかがプログラミングするときの理解の端緒になればいいかな?的な。

そして上述の目的ゆえ、正確性よりも、こういうもんだよ。こう理解しとけばいいよ。的な不正確性を多分に孕む。
エンジニア的には間違った情報断固許さない。となりがちだけど、これを読む対象はもうすでにコード書いてる人じゃなくて、
これからコード書き始める人たちで、一週間もしたらこんな記事いらなくなる人たちなので、
そういう不正確性や雑な書き方、雑な説明も一旦腹落ちさせて先に進むためのものだと割り切って許してやってもらえれば嬉しい。

あと、タイトルとか小ネタは某ガガガ文庫のラノベから引っ張ってくる所存なので、
わかる人だけわかってもらえれば嬉しいです。

チャプタータイトルはタイトルの元ネタにしてる某ラノベ1巻の各章から取ってきたので内容とは必ずしも合っていない。すまんな。

1.とにかくnode.jsは流行っている

Q. node.jsないしnodeと呼ばれる謎のツール。ソフト。アプリ。なんでもいいけど、アレ。アレって一体なんなの?

A. JavaScript(ECMA Script)って呼ばれるプログラミング言語の実行環境。

Q. よくわかりません。

A. それな。

というわけで、雑にnodeとかJSとかESとかそういう話をしようと思う。

HTML,CSS,JSの最低限をProgateとかでやった人とか、Chromeのコンソールとかで
console.log('hello world!!');
とかした人は、
なんかChrome(またはその他のブラウザ)で、JavaScriptというプログラミング言語が動くっぽいぞ。
というのを体験したと思う。
そこで書いたconsole.log(hello world!!);がプログラムで、
その文法がJavaScriptというやつ。
ECMA Scriptというのは、JavaScriptの標準規格。

例えるなら、

自動車はメーカーが違っても車種が違っても自動車だし、
アクセル踏んだら加速するし、
ブレーキ踏んだら減速するし、
ハンドル切ったら曲がる

みたいな自動車ってなんだっけの決まりごとがECMA Scriptっていう規格で、
「ブラウザで動くJavaScriptはこういう風に書いたらこういう風に動くようにしてね。」
っていう決まりごとが定義されている。

そのECMA ScriptをもとにGoogleがChrome作ったり、AppleがSafari作ったり、MozillaがFirefox作ったりしている。
トヨタのアクアも日産のノートもホンダのフィットも別の車だけど、ハンドルがついてて、アクセルとブレーキがついてて、アクセルが右、ブレーキが左にあるのは一緒。みたいな感じで、
ECMA Script(のきまりごと)を守って作ってあれば基本的には同じようなJavaScriptが使える

けどアクアとノートとフィットが違うように、各ブラウザによって、必ずしも全部が全部同じではなかったりもするのがJavaScriptなので、
ECMA Scriptの決まりごと以外の部分ではそれぞれ違う機能があったり、
ECMA Scriptの決まりごとの、どのバージョンまで対応しているかとかもブラウザによって違ったりする。

そして、ブラウザの話に戻ると、ブラウザはそもそもウェブサイトを見るためのソフト(今時はアプリか?)なので、
JavaScriptを動かすだけじゃなくて、HTMLを表示させたり、それをCSSで装飾したりする機能など、たくさんの機能がついている。

そのブラウザのなかでも、GoogleのChromeというブラウザに使われているJavaScriptを動かす部分がV8というソフトで、
そのV8をブラウザから引っこ抜いてきて、ちょこちょこ機能を足すことで、
ChromeというブラウザのJavaScriptを動かす部分だけを取り出したアプリがnode.jsだと理解しておけば、
これからプログラミングを勉強する人たちにとっては十分正確な理解だと思う。
JavaScriptを動かす部分を取り出してきたものなので、できることはJavaScriptを動かすだけなので、
node.js(またはnode)はJavaScriptの実行環境ということになる。

node.jsはJavaScriptの実行環境なので、例えば

Screen Shot 2019-02-26 at 19.19.47.png

このHTMLをChromeで開いた時に実行される

index.js
console.log('hello world!!');

がこんな感じでブラウザで実行されるように

Screen Shot 2019-02-26 at 19.22.18.png

nodeで実行しても同じように"hello world!!"と表示される。

Screen Shot 2019-02-26 at 19.25.08.png

まとめ:node.js(とかnodeって言われてるやつ)は、ブラウザからJavaScriptを動かす機能だけを引っこ抜いてきてたやつ。

2.いつでもnpmは付属している

Q. nodeが"ブラウザからJavaScriptを動かす部分だけを抜き取ってきたやつ"だということを上で説明したけれど、それ入れるとくっついてくるnpmってなんなんですか。

A. node package manager のことです!

Q. nodeはともかく"package manager"ってなんですか。

A. スマートフォンのAppStoreみたいなもんだと思ってくれ!

つまり、npmはnodeで動くアプリのストアみたいなもの。npmからいろんなソフト(アプリ)をインストールしたりアンインストールしたりできる。

と、言い切ってしまうと誤解が爆発的に産まれそうなので、もっと詳しく解説してみる。

  • パッケージマネージャーって何?
  • npmは何ができるの?

の2点についてざっくり説明するので、npmがなんなのかふわっとわかっていただければと思います!!

パッケージマネージャーって何?

パッケージマネージャーは、各種ソフトウェアのインストール、アンインストール、依存関係管理を行うツール。

依存関係管理?

依存関係とは、ソフトAをインストールする際、ソフトAはソフトBがないと動かない。という時に"AはBに依存している"(Bがないと動かない)という関係。

つまり依存関係管理をしてくれるというのは、
「ソフトAをインストールしてね!」
と言うコマンドを実行した時に、
「AはB,C,Dに依存しているのでB,C,Dもインストールしますね!」
というのを自動でやってくれたり、Aのバージョンアップの時に、
「B,C,DだけじゃなくてEも追加しないと!」とか、「Bもバージョンアップしなきゃ!」
などを勝手に管理して、ダウンロードしてインストールしてくれる仕組みのこと。

また、逆の操作に対しても管理を行ってくれるため、Aをアンインストールすると、
一緒にB,C,Dもアンインストールしてくれたり、Bを削除しようとすると、
「AがBに依存しているのでBを削除するとAが動かなくなりますが削除しますか?」と言ったことを聞いてくれるなどして、
ソフトウェア同士の依存関係の管理をパッケージマネージャーがしておいてくれるというもの。

なので、例えばnpm install reactなどでreactをインストールすると、reactが依存している数多くのパッケージ(ソフト)がnode_modulesというところに自動でインストールされる。

もちろん、パッケージマネージャーで管理されるものはいろんなプログラムなので、コマンドラインで実行するコマンドのプログラムをインストールすることもできる。

そのため、npm install create-react-appとすると、create-react-appというコマンドがインストールされて、
create-react-app {appname}とコマンドを打つと、appnameという名前でreactのアプリの雛形が作られる。というようなこともできる。
この時も、create-react-appというプログラムを実行するために必要な依存関係にあるソフト群は自動でインストールされて、npmが管理してくれるため、create-react-appを使う時に、create-react-appが依存しているプログラムを意識したり、手動でインストールすることなく利用することができる。

npmは何ができるの?

上述のようなパッケージ管理に加えて、npmではnpm scriptというものを書くことで、コマンドを自作したりできる。
自作するコマンドはpackage.jsonというファイルに記述することで、npmがpackage.jsonを読み込んで、コマンドを実行できるようにしてくれる。

また、create-react-appのようなコマンドに関しては、npxというコマンドを使うことで、コンピューターにインストールすることなくインターネット上にあるコマンドを実行することができる。

そのため、create-react-appがインストールされていなくても、

Screen Shot 2019-02-26 at 21.09.22.png

このようにnpxコマンドを実行すると

Screen Shot 2019-02-26 at 21.09.45.png

こう言った感じでpackage.jsonが作られ、

Screen Shot 2019-02-26 at 21.10.36.png

reactのアプリケーションに必要な依存関係パッケージがnode_modulesに展開されて

Screen Shot 2019-02-26 at 21.11.33.png

必要なパッケージと、初期状態の雛形ファイルが自動的に作られる。

この時、package.json"scripts"以下に"start":"react-scripts start"というコマンドが作られるので、
ここで、npm startを実行すると、実際にはnode ./node_modules/react-scripts/bin/react-scripts.js startが実行される。

ezgif.com-video-to-gif.gif

といった具合に、$ npm {自分で作ったコマンドの名前}$ node {実行するJavaScriptのスクリプト}が実行されるようにできるというのがパッケージ管理以外のnpmにできることになる。

まとめ:npmはnodeで実行できるプログラム各種の依存関係を管理したり、インストールしたりアンインストールしたり、あとpackage.jsonに自作コマンドを書くことでnpmのコマンドを作ったりできるパッケージ管理ツール

3.つねにnodeとnpmは使われている

ここまでで、nodeはJavaScriptを実行する部分をブラウザから分離したものということとnpmはnodeの各種パッケージを管理したりスクリプト作ったりするものということを説明したけれど、

Q.じゃあそれがあるとプログラムを書く時に何が嬉しいの?

という疑問が出てくると思う。

なので、プログラムを作る時にnodeとnpmがあると何が嬉しくて、nodeとnpmが何に使われているのかを簡単に説明しようと思う。

nodeがあると何が嬉しいの?

nodeがあるとできること、それはブラウザの外側でJavaScriptが動かせること。
nodeはブラウザから取り出したJavaScriptの実行環境に、I/O(入出力)の仕組みを追加してあるので、
コンピュータの中にあるファイルを取り込んで、JavaScriptで書いたプログラムを使って処理を行って、
実行結果を出力することができる。

今まではブラウザの中でしか動かすことができず、できることが限られていたJavaScriptというプログラミング言語で、
ブラウザ関係なく動作するプログラムが作れることで、
コマンドラインツールや汎用性のあるプログラムなど、必ずしもブラウザというUIやユーザーによる操作を必要としないプログラムが書けて、実行できるようになったのがすごく大きい。

例えば、nodeで実行できるプログラムであれば、インターネットに繋がっていなくても、通信を必要としないプログラムが書けたり、
通信を必要とするけど操作を必要としない常駐型のプログラムを作ることができるようになる。

node.jsがサーバーサイドJavaScriptと言われるのは、ブラウザ上でのユーザー操作に対して反応するプログラムだけでなく、
サーバー(コンピューター)上で常時実行され続けるプログラムをJavaScriptで書くことができることで、サーバーサイドとクライアントサイドの開発言語を統一することができることが大きなメリットであり、
JavaScriptが基本的にはHTML,CSSと合わせて学ばれる"難しくない"プログラミング言語であるために、ウェブアプリケーション開発において、UI開発の必須項目であるJavaScriptさえある程度できれば、同じ言語でサーバーサイドが開発できることが大きい。

npmがあると何が嬉しいの?

パッケージマネージャがあることで依存関係の管理をしなくていい、
独自のコマンドを作れることで長いコマンドを短いコマンドにできる。
というすでにあげたメリットだけでなく、
パッケージ管理システムとインターネット、中央リポジトリを使うことで、他人が今までに作ってきたプログラム資産を自分のソフトウェア開発に使用できるというのがすごく嬉しい。
こういう機能が追加したいな。とか、こういう機能が欲しいな。という時に、0から自分で作ることなく既に誰かが作ってあるものを使えることで、
時間はすごい短縮できるし、ソフトウェアの品質は向上させられるし、自分が作りたいものを作ることに集中できる。
車を作る時に、タイヤとかホイールとか鉄板から自分で全部作っていると大変なので、
タイヤメーカーが作ったタイヤを使う、ホイールメーカーが作ったホイールを使う、鉄鋼メーカーが作った鉄板を使う。
それと同じようにソフトウェアを作る時に(ネットに繋がっていれば)外部から既に他の誰かが作ってあるものを取り込んで使うことができるのが、
エンジニアとしては嬉しいというのがある。

まとめ:nodeがあると、サーバーサイドとクライアントサイドのプログラムが両方JavaScriptで書けて、npmがあると自分が過去に作ったプログラムや他人が作ったプログラムを利用できるので新しく作るところに集中できて良い。

あとがき

雑かつ後半とか体力が尽きてだいぶ適当な感じになってしまったが、
nodeってなんだ!npmってなんだ!ウォォオオオーン!!という人たちがこれを読んで少しでもわかった気になってもらえたらいいなと思います。

エンジニア向け解説はもっと強くてJS詳しくて強い、つよつよエンジニアが書いたほうが正確性が高まって良いと思うので、
僕は右も左もわかりません!という方々が、どっちが右でどっちが左かを判別できるようになる程度の情報を提供できればなと思います。

p.s.

渡航先生、やはり俺の青春ラブコメはまちがっている14巻発売日決定&14巻で本編完結おめでとうございます。
発売日を心待ちにしてます。
あと14巻以降も短、中編集などで八幡や他のキャラ達の姿が楽しめることを期待してます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Xubuntuでpuppeteerがまともに動かなかったときの対処

ことの起こり

妻(プログラミングに興味はあるけど、特別ITに詳しいわけではない普通の主婦。どちらかというとむしろ理数系は苦手)がどこかで、スクレイピングなる単語を聞いてきたらしく、「puppeteerというソフトを使えば、Webサイトを自動的に定期取得したりということができるらしい」「面白そうだからやってみたい」と言われたことが始めたきっかけ。

それからどした

とりあえず、私がpuppeteerなるものを知らなかったので調べてみました。

  • node.js のモジュールで、chromeをプログラム的に操作するものらしい。
  • 使いこなせたらテストとか楽そう。
  • 自分も興味が出たから、まぁ、暇なときにやってみるか。

という流れで、ひとまず環境を作ってあげることに。
本格的な開発で使うわけではないので、古くてもいい代わりに、プライバシーをとかく気にしがちな妻が自由にできる環境が良いだろうということで、自宅で眠っていた13年前のLet's noteを引っ張り出してみました。
元々、すでに数年前にXubuntuに入れ替えて眠らせていた端末だったのでアップデートかけるくらいで使えたらめっけ物だなぁ〜と思って動かしてみたら、意外と動く。でも、リポジトリが古すぎてインストールできたnodeのバージョンが古く、肝心のpuppeteerが動かない。
ということで、まぁ、折角の機会なのでXubuntuのLTSでもある18.04.2をクリーンインストール。
まっさらな環境を立ち上げました。

詰まったこと

あとはドキュメントなり関連情報調べながら適当に入れていけばいいだろう〜と思って、qiitaなりを参考に、

npm i puppeteer

と実行してから、サンプルをカキカキ。

node example.js

と実行してみたら、もろにエラー。あれ?
しかもエラーメッセージが

node example.js 
(node:10017) UnhandledPromiseRejectionWarning: Error: Failed to launch chrome!
/home/tezuka/node/node_modules/puppeteer/.local-chromium/linux-624492/chrome-linux/chrome: 1: /home/tezuka/node/node_modules/puppeteer/.local-chromium/linux-624492/chrome-linux/chrome: Syntax error: "(" unexpected


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md

    at onClose (/home/tezuka/node/node_modules/puppeteer/lib/Launcher.js:360:14)
    at Interface.helper.addEventListener (/home/tezuka/node/node_modules/puppeteer/lib/Launcher.js:349:50)
    at emitNone (events.js:111:20)
    at Interface.emit (events.js:208:7)
    at Interface.close (readline.js:370:8)
    at Socket.onend (readline.js:149:10)
    at emitNone (events.js:111:20)
    at Socket.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)
(node:10017) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:10017) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

なんて感じでリンクされてるトラブルシューティングのドキュメント読んでも今ひとつわからない。
というより何より、一番最初のエラーメッセージ、明らかにlaunch関数で詰まっているし、エラーメッセージが出るにしてもこれ、構文エラーって明らかにおかしいよね?
当初npmの環境作った場所が日本語パスの下にあったので、そのへんが問題かと思って別のパスはいかに環境作り直してみたりもしたが状況は変わらず。
どうもpuppeteer配下に一緒にセットアップされているchromeが壊れてるんじゃないかなぁ〜ということで、なんとかこの下のchromeを使わずに動かす方法がないか調査。

で、どうした。

公式文書を見ると、どうもpuppeteerの他に、ブラウザをバンドルしないpuppeteer-coreというバージョンが提供されている様子。
これを使えばいけそうだが、connectしたりなんだかんだと、ちょっと使い方が複雑になりそう。
私はともかく、本を頼りになんとかしようとしている妻はきっと挫折するだろう。どうにかならないかと思ってもう公式ドキュメントをプラプラ見ていたらこんな一文を見つけた。

const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});

https://www.npmjs.com/package/puppeteer-core

お?これってもしかして任意の場所のChrome立ち上げられるんじゃ?
ということで、Xubuntuのaptでインストールできた、chromiume-browserを指定する以下の形のexample.jsを作成。

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium-browser'});

  const page = await browser.newPage();
  await page.goto('https://www.yahoo.co.jp');
  await page.screenshot({path: 'yahoo.png'});

  await browser.close();
})();

node example.js
を実行したところ、無事、yahoo.pngの画像が作成されました。

ひとまずこれで動くようになりましたので、もし同じようなことでお困りの方がいましたら参考にどうぞ。
結局、なんでバンドルされていたブラウザがエラーになったのかは不明なんですが、どなたかご存じの方がいましたら教えていただけますと幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

socket.io で Web空間に神社を建立

は?

兎にも角にも成果物です。
https://web-shrine.herokuapp.com/

はじめに

会社でWebSocket勉強会が開催されたので、試しに遊んでみようということで作成しました。
あまり時間もなく凝ったことはできないので、代わりにネタに走った結果がこれです。

使用技術は以下の通りです。

  • HTML5
  • CSS3(flexbox)
  • Node.js(express.js)
  • jQuery(Angularで書き直したい)
  • socket.io

作成したプロダクトの公開にHerokuを用いています。

コード

フロント側とバック側に分けて説明します。

フロントエンド

今どきjQuery使うのもどうかとは思いましたが、あまり時間がなかったので許してください。
そのうちAngular使って書き直したいです。

HTML部は特にこれといって解説する部分はないので、scriptタグ内に絞って解説していきます。

index.html
const socket = io();

socket.on('connect', () => {
    console.log('Server Connected')
})

socket.on('receive_money', (obj) => {
    console.log('現在のお賽銭額', obj.value)
    $('#money').animate(
        { opacity: "toggle" },
        0
    );
    $('#money').text(obj.value.toLocaleString())
    $('#money').animate(
        { opacity: "toggle" },
        200
    );
})

socket.on('receive_throw', (obj) => {
    if ($('#throw-money-list li').length >= 50) {
        $('#throw-money-list li:last-child').remove();
    }
    $('#throw-money-list').prepend(
        $('<li>').text(obj.value.toLocaleString() + '円が投げ込まれました!')
    )
})

socket.on('receive_money_list', (array) => {
    $('#money-list').empty();
    array.reverse();
    $.each(array, (index, element) => {
        $('#money-list').append(
            $('<li>').text(element.amount.toLocaleString() + '円 奉納'),
        )
    });
})

socket.on('receive_throw_time_by_amount', (obj) => {
    $('#time_1').text(obj['1'])
    $('#time_5').text(obj['5'])
    $('#time_10').text(obj['10'])
    $('#time_50').text(obj['50'])
    $('#time_100').text(obj['100'])
    $('#time_500').text(obj['500'])
    $('#time_1000').text(obj['1000'])
    $('#time_5000').text(obj['5000'])
    $('#time_10000').text(obj['10000'])
})

socket.on('receive_wish', (array) => {
    console.log('お願いを受信', array)
    $('#wish-list').empty();
    $.each(array, (index, element) => {
        $('#wish-list').prepend(
            $('<div style="margin: 12px;">').append(
                $('<dt>').text(element.name + ' さん - ' + new Date(element.time).toLocaleString()),
                $('<dd class="ema">').append(
                    $('<span class="ema-text">').text(element.wish)
                )
            )
        )
    });
})

socket.on('receive_worshipers', (num) => {
    $('#worshipers').text(num);
})


function throwMoney(num) {
    console.log('賽銭', num, '円')
    socket.emit('throw_money', { value: num });
    if ($('#throw-money-list li').length >= 50) {
        $('#throw-money-list li:last-child').remove();
    }
    $('#throw-money-list').prepend(
        $('<li>').text(num + '円を投げ込みました!')
    )
}

function sendWish() {
    console.log('お願いの送信')
    const name = $('#wish-form [name=name]').val();
    const wish = $('#wish-form [name=wish]').val();
    console.log(name, wish)
    if (!wish) {
        return;
    }
    socket.emit('send_wish', { name: name || '名無し', wish: wish, time: new Date() });
}

socket.ioはon関数で受信、emit関数で送信時の動作を定義することができます。
両関数は第一引数で名前を設定することができ、フロントエンドとバックエンドで同じ名前のon-emitの対を作ることで、双方向データ通信を実現しています。

receive_moneyは現在の合計賽銭額が変更された時に発火するon関数です。
#moneyをアニメーションさせながら書き換えているだけです。

receive_throw`は賽銭が投げ入れられた時に発火します。
こちらも#throw-money-listが50件までに収まるように追加された額をリストに追加しているだけ。

receive_money_listreceive_throwと一見同じように見えますが、こちらはサーバーが起動してから現在までの奉納の履歴を取得して発火します。throwは「ユーザーがサイトにアクセスした後に、だれかが賽銭を投げ入れたとき」に発火するので、自分がアクセスしたときにはリストは空の状態ですが、こちらはサーバー起動時からの履歴を表示することができます。

receive_throw_time_by_amountは、上記で用いた方法を応用して、サーバーが起動してから賽銭箱に何回何円を入れたのかを取得して発火します。

receive_wishもまた同様の方法で、ユーザーが入力したお願いを取得して発火します。

receive_worshipersは、現在サイトを閲覧しているユーザー数を取得して発火します。
人数を書き換えているだけです。

続いてemit関数ですが、全てボタンのクリックイベントに紐づけてあります。

throw_moneyは賽銭を投げ込む動作です。賽銭の額を引数にとり、バックエンド側にemit関数として送信しています。
送信履歴をフロント側で書き換えることで、「hoge円を投げ込みました!」と「hoge円が投げ込まれました!」の文書の差を作っています。
これはバックエンド側でemitする対象を選択することが出来る機能によるものです。後述します。

send_wishは単純にバックエンド側にお願いを送信するものです。

バックエンド

app.js
const express = require('express')
const app = express();
const http = require('http').Server(app);
const io = require('socket.io')(http);

let num = 0;
const moneyList = [];
const wishList = [];
const throwTimeByAmount = {
    '1': 0,
    '5': 0,
    '10': 0,
    '50': 0,
    '100': 0,
    '500': 0,
    '1000': 0,
    '5000': 0,
    '10000': 0
}

app.use('/', express.static('public'))

io.on('connection', (socket) => {
    console.log('socket connected', socket.client.conn.server.clientsCount);
    io.emit('receive_money', {value: num})
    io.emit('receive_wish', wishList)
    io.emit('receive_money_list', moneyList)
    io.emit('receive_worshipers', socket.client.conn.server.clientsCount)
    io.emit('receive_throw_time_by_amount', throwTimeByAmount)

    socket.on('throw_money', (obj) => {
        console.log('賽銭が投げられました', obj)
        moneyList.push({amount: obj.value});
        num += obj.value;
        throwTimeByAmount[obj.value]++; 
        io.emit('receive_money', {value: num})
        io.emit('receive_money_list', moneyList)
        io.emit('receive_throw_time_by_amount', throwTimeByAmount)
        socket.broadcast.emit('receive_throw', {value: obj.value})
    })

    socket.on('send_wish', (obj) => {
        console.log('お願いが送信されました', wishList, obj)
        wishList.push(obj);
        io.emit('receive_wish', wishList)
    })

    socket.on('disconnect', () => {
        io.emit('receive_worshipers', socket.client.conn.server.clientsCount)
    })

})

http.listen(process.env.PORT || 3000, () => console.log('Server Open'))

const io = require('socket.io')(http);とおまじないをかけることで、ioオブジェクトが使えます。

ただ、バックエンド側では最初にio.on('connection', (socket) => {})を定義して、接続が確立したことを確認する必要があります。
コールバック関数の中にon-emit関数を書くことで、フロント側と同様に使えます。

最初数行のemit関数では、単純にバックエンドで管理されている変数をフロント側に送信しています。

app.js
socket.on('throw_money', (obj) => {
    console.log('賽銭が投げられました', obj)
    if (moneyList.length >= 50) {
        moneyList.shift();
    }
    moneyList.push({ amount: obj.value });
    num += obj.value;
    throwTimeByAmount[obj.value]++;
    io.emit('receive_money', { value: num })
    io.emit('receive_money_list', moneyList)
    io.emit('receive_throw_time_by_amount', throwTimeByAmount)
    socket.broadcast.emit('receive_throw', { value: obj.value })
})

先ほど「バックエンド側でemitする対象を選択することが出来る機能」があると記しましたが、さっそくthrow_moneyで用いています。
単純にsocket.emit()としてしまうと、自分が送信した賽銭履歴も自分に返ってきてしまい、「投げ入れました」と「投げ込まれました」が同時に出力されてしまうのですが、socket.broadcast.emitとすることで、自分以外にemitすることができます。
この機能にはほかにもバリエーションがあるので、公式ドキュメントで探してみてください。

その他のemit関数は説明するまでもないと思いますので割愛します。

感想

小学生の時に、Java appletで実装されていたチャットでインターネットに触れた私としては、こんなに簡単にリアルタイム双方向通信が実装できるとは思わず、なんとも隔世の感を禁じえません。

socket.io、すっげー(小並感)

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Paizaのチュートリアル問題をTDDしてみるメモ

概要

Paizaのスキルチェックテストのチュートリアルを、JavaScript(Node.js)とテスト駆動開発(TDD)でやってみたよ、ってメモ書き。

スキルチェックテストの実際の問題を解く上でも、「これで動くかな?」を確認するのは「テストで」やりたいよね、と思ったのでやってみた。
なお、ESLINT+VSCode環境をお勧めする。文法エラーで詰まるとかアホらしいので。 by 詰まった人

※スキルチェックテストの設問自体を公開するのはルール違反だが、チュートリアルなら良かろう。ほぼ「標準入出力の扱い方」でしかないし。

取り上げる設問

各問題とも「提出いただいたコードの実行(標準入力による値の取得、計算処理)→標準出力→正誤の判定」

とのことである。

チュートリアルでJavaScript言語を選択すると、次のようにデフォルトコードが出力される。
これは、標準入出力の扱い方のサンプルコード。
https://paiza.jp/challenges/practice

tutorial.js
process.stdin.resume();
process.stdin.setEncoding('utf8');
// 自分の得意な言語で
// スキルチェックの基本となる、標準入力で値を取得し、
// 出力するコードを書いてみよう!

var lines = [];
var reader = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
reader.on('line', (line) => {
  lines.push(line);
});
reader.on('close', () => {
  console.log(lines[0]);
});

これを「標準入出力から入力された複数行の文字列のうち、終了時(Ctrl+C押下時)に最初の1行の文字列を標準出力へ出力する」と言う「機能を実装する」と捉えて、話を進める。

今後を見据えて、これをテスト駆動開発してみる。

機能をテストする枠組み

まず、次のような基本クラスを作った。

paiza_stdio.js
// var PAIZA_STANDARD_IO = require("paiza_stdio.js").PAIZA_STANDARD_IO;
/**
 * Readlineオブジェクトのイベント"line"と"close"の発火時に
 * これを呼び出すように実装する。
 * 標準入出力に対する機能実装は、このクラスを継承して行う。
 * 
 * @param {Object} Consoleオブジェクトのインスタンスを指定。通常は"console"を書けばよい。
 */
var PAIZA_STANDARD_IO = function ( consoleInstance ) {
  this._lines = [];
  this._consoleInstancce = consoleInstance;
};
PAIZA_STANDARD_IO.prototype.onInputLine = function (lineStr) {
  // ↑呼び出し元のインスタンスをthisに欲しいので、アロー関数は使わない。
  this._lines.push( lineStr );
};
PAIZA_STANDARD_IO.prototype.onClose = function () {
};
PAIZA_STANDARD_IO.prototype.writeConsole = function (value) {
    var str = value.toString();
    this._consoleInstancce.log( str );
}

これを、サンプル―コードに倣って入出力から呼び出す。

index.js
/**
 * [index.js]
 */
process.stdin.resume();
process.stdin.setEncoding('utf8');
// 自分の得意な言語で
// スキルチェックの基本となる、標準入力で値を取得し、
// 出力するコードを書いてみよう!


// IO for target environment.
var reader = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
reader.on('line', (line) => {
  instance.onInputLine( line );
});
reader.on('close', () => {
  instance.onClose();
});


// Paizaへの提出時は、忘れずに【以下を】ロード先のコードに置き換えること!
var TUTORIAL_SHOW_1ST_LINE = require("./src/01_tutorial.js").TUTORIAL_SHOW_1ST_LINE;
var instance = new TUTORIAL_SHOW_1ST_LINE( console );

機能の実装、今回のサンプルコードであれば「標準入力の最初の1行目を、終了後に標準出力へ表示」は、PAIZA_STANDARD_IOを継承して、例えば次のように行う。なお、現時点では敢えて出力は「期待値に対して未実装」にしてある。

01_tutorial.js
// Implement
var TUTORIAL_SHOW_1ST_LINE = function ( consoleInstance ) {
  PAIZA_STANDARD_IO.call(this, consoleInstance);
};
TUTORIAL_SHOW_1ST_LINE.prototype = Object.create( PAIZA_STANDARD_IO.prototype );
TUTORIAL_SHOW_1ST_LINE.prototype.onClose = function () {
  this.writeConsole( "終了時に出力する文字列" );
};
exports.TUTORIAL_SHOW_1ST_LINE = TUTORIAL_SHOW_1ST_LINE;


作りたい機能を検証(テスト)するコードを作成

続いてテストコード。
Mocha+Chai+Sinon環境が好きなので、次コマンドを叩いてサクッとインストールする。cross-envは「テスト用の環境変数の取り扱いをクロスOSで容易にする」モジュールで、今は使わないかもしれないが後々を考えて入れておく。npm initは特にこだわりなければ全部デフォルトで「Yes」でOK。

npm init
npm install --save-dev mocha chai cross-env sinon

package.jsonの "script" を次のように編集する。

package.json
  "scripts": {
    "start" : "node index.js",
    "test": "cross-env NODE_ENV=development mocha --recursive"
  },

今回の「標準入出力から入力された複数行の文字列のうち、終了時(Ctrl+C押下時)に最初の1行の文字列を標準出力へ出力する」機能を検証(テスト)するテストコードは、次のように書く。

01_tutorial_test.js
/**
 * [01_tutorial_test.js]
 * encoding=utf-8
 */

var chai = require("chai");
var expect = chai.expect;
var sinon = require("sinon");


describe( "01_tutorial.js", function(){
    var target = require("../src/01_tutorial.js");

    describe("instance.",function () {
        var instance = new target.TUTORIAL_SHOW_1ST_LINE( console );


        it("After input 'hoge', 'fuga' & ctrl+c`, output `hoge`.", function () {
            var consoleStubLog = sinon.stub( console, "log" );
            var INPUT1 = "hoge", INPUT2 = "fuga";

            instance.onInputLine(INPUT1);
            instance.onInputLine(INPUT2);
            instance.onClose();

            consoleStubLog.restore();
            expect( consoleStubLog.getCall(0).args[0] ).to.equal( INPUT1 );
        });
    });
});

これを実行すると次のようになる。現時点では、「失敗」が期待値。ここから開始。

npm test

qiita_tdd_on_tutorial_of_paiza_scrshot1_failed.png
https://gyazo.com/dd93e4c42955d519d4dc1bfc0249daa6

機能を実装してテストする。

改めて実装する。

01_tutorial.js
// Implement
var TUTORIAL_SHOW_1ST_LINE = function ( consoleInstance ) {
  PAIZA_STANDARD_IO.call(this, consoleInstance);
};
TUTORIAL_SHOW_1ST_LINE.prototype = Object.create( PAIZA_STANDARD_IO.prototype );
TUTORIAL_SHOW_1ST_LINE.prototype.onClose = function () {
  this.writeConsole( this._lines[0] );
};
exports.TUTORIAL_SHOW_1ST_LINE = TUTORIAL_SHOW_1ST_LINE;

テストを実行すると、成功する。

npm test

qiita_tdd_on_tutorial_of_paiza_scrshot2_ok.png
https://gyazo.com/8e14f457c2545a5af96d4b594c9abf40

実際の動作も確認してみる。

実際のコマンドプロンプトからの入力をしてみる。

npm start
hoge
fuga
ctrl + c

qiita_tdd_on_tutorial_of_paiza_scrshot3_index.png
https://gyazo.com/5ff81418690a1ad89c3cc667bc8198cd

実際に、Paizaに以下のコードを提出してみる。
「成功」になる。

/**
 * [index.js]
 */
process.stdin.resume();
process.stdin.setEncoding('utf8');
// 自分の得意な言語で
// スキルチェックの基本となる、標準入力で値を取得し、
// 出力するコードを書いてみよう!




// IO for target environment.
var reader = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
reader.on('line', (line) => {
  instance.onInputLine( line );
});
reader.on('close', () => {
  instance.onClose();
});


// Paizaへの提出時は、忘れずに【以下を】ロード先のコードに置き換えること!
// var TUTORIAL_SHOW_1ST_LINE = require("./src/01_tutorial.js").TUTORIAL_SHOW_1ST_LINE;
/**
 * [01_tutorial.js]
 */
// var PAIZA_STANDARD_IO = require("paiza_stdio.js").PAIZA_STANDARD_IO;
var PAIZA_STANDARD_IO = function ( consoleInstance ) {
  this._lines = [];
  this._consoleInstancce = consoleInstance;
};
PAIZA_STANDARD_IO.prototype.onInputLine = function (lineStr) {
  this._lines.push( lineStr );
};
PAIZA_STANDARD_IO.prototype.onClose = function () {
};
PAIZA_STANDARD_IO.prototype.writeConsole = function (str) {
  this._consoleInstancce.log( str )
}


// Implement
var TUTORIAL_SHOW_1ST_LINE = function ( consoleInstance ) {
  PAIZA_STANDARD_IO.call(this, consoleInstance);
};
TUTORIAL_SHOW_1ST_LINE.prototype = Object.create( PAIZA_STANDARD_IO.prototype );
TUTORIAL_SHOW_1ST_LINE.prototype.onClose = function () {
  this.writeConsole( this._lines[0] );
};
exports.TUTORIAL_SHOW_1ST_LINE = TUTORIAL_SHOW_1ST_LINE;


var instance = new TUTORIAL_SHOW_1ST_LINE( console );
exports.instance = instance;

以上ー。

雑感

今回に、次のコードを書いたときに、「あぁ、そうか!Sinon::stub()って、この用途がスタート地点なんだ!」て妙に腑に落ちたの印象的。実際にどうかは知らんが、私にはしっくりきた。

var instance = new target.TUTORIAL_SHOW_1ST_LINE( console );
// (中略)
var consoleStubLog = sinon.stub( console, "log" );
// (中略)
consoleStubLog.restore();

これ、sinon.stub()をしないと当然ながら、そのままコンソールに console.log() した値が出力される。でも、上記のようにstub()ってから実行すると、出力されない。そして、restore() した以降は、元のように出力されるようになる。

あぁ、そういうことなのね。シノンのスタブ、完全に理解した()。

※なお、sinon.stub()してると、デバッグ目的でのconsole.log()は使えなくなります、当然ながら。その場合は sinon.spy()にすれば解決するかな? いや、そもそもconsole.log()じゃなくて、.writeConsole()sinon.stub()するのが適切なのかな?w

補足

Googleで「paiza javascript テスト駆動」を検索したけど、javvascriptに関して、これってのはヒットせず。

Ruby向けだと見つかった。

エッ、今さら!?練習問題と具体的コード例によるTDD超入門。

C#版だと、こんなのもあった。
【C#】paizaの煩わしいテスト(入力処理)を自動化したい

最初は、ReadLineをフックすることを考えた。
https://nodejs.org/api/readline.html#readline_event_line

次に、Readable Streamsをフックするも検討してみた。
https://nodejs.org/api/stream.html#stream_readable_streams

でも、どっちもピンとこなかった。

そこまで考えて、「呼び出しのところだけ切り離せばよいのでは?標準入出力のところを律義にエミュレートする必要は無い」と考え直した。そして書いたコードが上記である。

これでいいのかは分からないが、とりあえずしっくりは来た。
「それは違う」とか「こうした方が良い」とかのコメント歓迎。

補足の補足(サンプルで実施例)

コーディングサンプル問題として「入力された整数がグレゴリオ暦でうるう年であるか判定する」があったので、やってみた。
https://paiza.jp/challenges/practice

書いたテストコードは以下。実装は省略する。

02_tutorial_practice_test.js
/**
 * [02_tutorial_practice_test.js]
 * encoding=utf-8
 */

var chai = require("chai");
var assert = chai.assert;
var expect = chai.expect;
var sinon = require("sinon");

describe( "02_tutorial_practice.js", function(){
    var target = require("../src/02_tutorial_practice.js");

    describe("instance.",function () {
        var instance = new target.IS_LEAP_YEAR( console );

        it("入力された整数がグレゴリオ暦でうるう年であるか判定する", function () {
            var consoleStubLog = sinon.stub( console, "log" );

            // 【コーディングサンプル問題】
            // 1行目には、入力される行数Tが入ります。
            // 1回のテストケースは、1行に1つずつ整数Nが入っている複数行の標準入力(stdin)による入力になります。 
            // https://paiza.jp/challenges/practice
            instance.onInputLine("4");
            instance.onInputLine("1000");
            instance.onInputLine("1992");
            instance.onInputLine("2000");
            instance.onInputLine("2001");
            instance.onClose();

            consoleStubLog.restore();
            expect( consoleStubLog.callCount ).to.equal( 4 );
            expect( consoleStubLog.getCall(0).args[0] ).to.equal( "1000 is not a leap year" )            
            expect( consoleStubLog.getCall(1).args[0] ).to.equal( "1992 is a leap year" )            
            expect( consoleStubLog.getCall(2).args[0] ).to.equal( "2000 is a leap year" )            
            expect( consoleStubLog.getCall(3).args[0] ).to.equal( "2001 is not a leap year" )            
        });
    });
});

実装しながらのユニットテストの「失敗と成功」は次のように進んだ(実装例は省略)。

とりあえず枠だけ実装してテスト。もちろん失敗。

qiita_tdd_on_practice_scrshot1.png
https://gyazo.com/efad38f2c9fdd263a6ee4f4adc4afbb8

判定無しで、とりあえず文字列出力まで実装。意図通りの失敗。

qiita_tdd_on_practice_scrshot2.png
https://gyazo.com/dceda4eccc304408ad0bc790b4012d2f

実装終わって、無事にテストも成功。
ついでなので、実際のコンソール入力からの(システム)テストもやってみた。成功。

qiita_tdd_on_practice_scrshot3.png
https://gyazo.com/d59dca0df97f140dbe22f38b42a78f26

今度こそ、以上ー。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slackの絵文字をimport/exportする方法

概要

Slackのカスタム絵文字(emoji)にまつわる問題を解決します。

  • ある人が作った絵文字を一括ダウンロードしたい
  • 絵文字をまとめてアップロードしたい
  • あるワークスペースの絵文字を別の場所でも使いたい

背景

ある人の退職に伴い、絵文字の引継ぎをしたいと思ったのが背景です。
Slackのカスタム絵文字はアカウントと紐づいているため、それを作成したアカウントが退会するとただの文字として表示されるようになってしまいます。そこで絵文字をバックアップできるツールを探していました。しかし、"特定ユーザの作成した絵文字"ではなく、ワークスペースにある絵文字全部を取得してしまうツールがほとんどで、6000個余りのカスタム絵文字が存在する弊社では不要な絵文字が多すぎ、役に立ちませんでした。

そこで特定ユーザの作成した絵文字をバックアップできるツールであるemojmeを見つけたので紹介します。

emojme

https://github.com/jackellenberger/emojme

何をするツール?

ユーザ別に絵文字を取得したり、取得した絵文字をアップロードしたりするツールです。

なぜ動くの?

Slackの公開APIである emoji.listではなく、非公開のAPIであるemoji.adminListを叩いて絵文字リストを取得するため、ユーザ別で取得できるようになっています。
したがって動作にはUser tokenが必須になっており、bot用のトークンでは動作しません。

動かし方

導入

npm install -g emojme

動作に必要なAPIトークンの取得方法(https://github.com/jackellenberger/emojme#slack-for-web)

${SUBDOMAIN}にSlackのワークスペースのサブドメイン名が入っているものとします。
1. https://${SUBDOMAIN}.slack.com/messages にアクセスします。
2. 右クリックして「検証」→上のタブの「Console」→下の文字列を入力しEnter
window.prompt("your api token is: ",/api_token: "(.*)"/.exec(document.body.innerHTML)[1])
3. APIトークンが表示されるので、これをコピーします。

ある人が作った絵文字を一括ダウンロードしたい

emojme download --subdomain $SUBDOMAIN --token $TOKEN --save $USER
* $SUBDOMAIN: Slackのワークスペースのサブドメイン名
* $TOKEN: APIトークン
* $USER: ユーザのスクリーンネーム(@〜 ではなく表示される名前)
カレントディレクトリ以下にbuild/$subdomain/$userというディレクトリが作られ、その中に絵文字がダウンロードされます。

絵文字をまとめてアップロードしたい

emojme add --subdomain $SUBDOMAIN --token $TOKEN --src $DIR
* $SUBDOMAIN: Slackのワークスペースのサブドメイン名
* $TOKEN: APIトークン
* アップロードしたいディレクトリ名

これ以上の使い方についてはREADMEを参照してください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS LambdaでPuppeteerを動かす

はじめに

AWS LambdaでPuppeteerを動かすためのメモです。
npm の @serverless-chrome/lambda と chrome-remote-interface を使用した例はいくつかありますが、開発停止の影響のためか、一部のPuppeteer APIが動作しなかったので、chrome-aws-lambda パッケージを使用します。

環境

  • AWS Lambda
    • ランタイム:Node.js 8.10
  • ローカル
    • macOS Mojave(10.14.3)
    • npm 6.8.0

手順

Lambda Layerに登録するパッケージの作成

$ mkdir nodejs && cd !$
$ npm i chrome-aws-lambda puppeteer-core
$ cd ..
$ zip -r modules.zip nodejs

Lambda Layer登録

AWSのコンソールからLambdaを開く。

Layer設定画面を開く

スクリーンショット_2019-02-25_13_41_43.png

Layerの作成

スクリーンショット_2019-02-25_13_46_37.png

Layerのアップロード

  • 名前: 任意のLayer名
  • 説明: 任意の説明文
  • コードエントリタイプ: .zipファイルをアップロード
  • アップロード: 作成したzipファイルを選択
  • 互換性のあるランタイム: Node.js 8.10
  • 「作成」をクリック スクリーンショット 2019-02-25 13.52.25.png

Lambda関数の作成

関数設定画面を開く

スクリーンショット_2019-02-25_21_09_18.png

関数の作成

スクリーンショット_2019-02-25_21_13_08.png
スクリーンショット 2019-02-25 21.19.02.png

  • 名前: 任意の関数名
  • ランタイム: Node.js 8.10
  • ロール: 1つ以上のテンプレートから新しいロールを作成
  • ロール名: 任意のロール名
  • ポリシーテンプレート: 未選択で可
  • 「関数の作成」をクリック

Layerの追加

スクリーンショット_2019-02-25_21_22_48.png
スクリーンショット 2019-02-25 21.26.59.png

  • ランタイムと互換性のあるレイヤーのリストから選択
  • レイヤー: 登録したLayer名を選択
  • バージョン: 登録したLayerの任意のバージョンを選択
  • 「追加」をクリック

関数コードを作成

スクリーンショット_2019-02-25_21_41_57.png

node.js
const chromium = require('chrome-aws-lambda');
const puppeteer = require('puppeteer-core');

exports.handler = async (event, context) => {
  let result = null;
  let browser = null;

  try {
    browser = await puppeteer.launch({
      args: chromium.args,
      defaultViewport: chromium.defaultViewport,
      executablePath: await chromium.executablePath,
      headless: chromium.headless,
    });

    let page = await browser.newPage();

    await page.goto(event.url || 'https://yahoo.co.jp/');

    result = await page.title();
  } catch (error) {
    return context.fail(error);
  } finally {
    if (browser !== null) {
      await browser.close();
    }
  }

  return context.succeed(result);
};

基本設定を調整

スクリーンショット 2019-02-25 21.45.06.png

  • メモリ: 512MB〜1600MB(chrome-aws-lambdaパッケージ推奨値)
  • タイムアウト:任意の時間

保存および実行

スクリーンショット_2019-02-25_21_53_40.png
スクリーンショット 2019-02-25 21.57.34.png

  • イベントテンプレート: Hello World
  • イベント名: 任意のイベント名
  • 「作成」をクリック

再度、画面右上テストをクリック

実行結果

スクリーンショット_2019-02-25_22_00_44.png

まとめ

ブラウザ操作を自動化できるPuppeteerをLambdaで動かせると、API Gateway(API化)やCloudWatch Events(定期バッチ処理)など、他のAWSサービスとの連携が容易になります。
ただでさえ便利なPuppeteerがさらに便利になるので、よければ試してみてください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む