- 投稿日:2019-05-29T22:18:47+09:00
Node.jsとtwilioとwindows10で あそぼ
5月31日(金)
本日の目標
課題1)韓国、台湾、香港へダイヤルできるようにする。
課題2)dial.jsの処理時間を計測する。
課題3)dial.jsを別のファイルから読み込み、?分おきに実行できるようにする。課題1
香港の電話番号は固定も携帯も8桁。携帯に絞ると国番号で分岐が必要になるので、めんどくさいから固定、携帯の区別はしない。
結果)香港へダイヤルできた。
注意点)twilioの設定を確認。通話⇒地理による許可⇒Low-Risk香港にチェック// 架電先 国+頭番号リスト(アジア圏) const CallToHeadNumList = { "台湾00": "+8869", //携帯 "韓国00": "+8210", //携帯 "香港00": "+852", //固定,携帯,IPなどall }5月30日(木)
ダイヤル処理無視して
あほみたいにループばかりするので、
同期処理するようにソースを書き直そう一億ループさせるので
ダイヤル後にメモリ解放させましょう本人の頭がパンクしてきた。
架電先、下8桁の生成に
乱数を使ってみる。// 架電先 下半身番号(8桁)を生成 let CallToBodyNum = String(Math.random()).slice(-8);二万件の発信処理に
少なくとも5時間30分はかかるので
頭番号21種✖️1億回ループさせても
無事に終了できるわけがない。架電先を乱数で指定し、ループ減らして
終了後に再度実行させよう午後0時25分
Twilioの仕様により
海外のスマホへ発信できないことが判明
固定電話でやり直します午後19時20分
午後9時54分
韓国と台湾へはダイヤルできた
折り返し確率、1/1024・参考サイト
メモリ解放方法
http://blog.livedoor.jp/aki_mana/archives/2587785.htmlnodeで同期処理例
https://qiita.com/rawHam/items/838eefc80bc35a90e49a乱数
http://ysklog.net/java/4-15.html5月29日(水)
Twilioで発信してみよう
エラーが出ました。
発信先21件✖️7570ループ目で。
Ineffective heap なんちゃらエラーWait入れて、発信キューが捌けるのを待った方がよいのでしょうか?
対策を考えます。
対策1:下記の呪文を入力
--max-old-space-size=8192
結果1
26000ループあたりで
Windows落ちる
- 投稿日:2019-05-29T22:18:47+09:00
Node.jsとtwilioとwindows10で 国際電話
5月31日(金)
本日の目標
課題1)韓国、台湾、香港へダイヤルできるようにする。
課題2)dial.jsの処理時間を計測する。
課題3)dial.jsを別のファイルから読み込み、?分おきに実行できるようにする。課題1
香港の電話番号は固定も携帯も8桁。携帯に絞ると国番号で分岐が必要になるので、めんどくさいから固定、携帯の区別はしない。
結果)香港へダイヤルできた。
注意点)twilioの設定を確認。通話⇒地理による許可⇒Low-Risk香港にチェック// 架電先 国+頭番号リスト(アジア圏) const CallToHeadNumList = { "台湾00": "+8869", //携帯 "韓国00": "+8210", //携帯 "香港00": "+852", //固定,携帯,IPなどall }19時56分
Twilio運営から英語で
どんな使い方をしているのか
説明しろとメールが。急遽、英語の勉強することになりました。
Oh!my gosh!こちらがアカウント停止中の実行結果です。
認証エラーが出ています。
課題2
・処理時間が出るようにしました。(定期実行させる時、時間間隔設定に使うため)
こちらを参考にしました。
https://blog.roundrop.jp/66/
https://ameblo.jp/rasenbagel/entry-11558847169.html(躓いた点)
関数名の頭に数字を使ったらエラーになりました
追加モジュールperf_hooksをインストールしました。
ミリ秒表示されるので、1000で割りました。(問題点)
表示される処理時間がAPIを叩くまでの時間になっている。
STATUS CALL BACK の やり方を覚えないといけない。//処理時間の測定(前処理) const startTime = performance.now(); // 開始時間 //メイン処理 main(); function main(){ //処理内容記入 } //処理時間の測定(後処理) const endTime = performance.now(); // 終了時間 console.log('処理時間:'+(endTime - startTime)/1000+'秒'); // 秒表示する課題3
外部ファイルのfunctionを呼び出す方法はわかったが
jsファイルまるごと実行させる方法がわからないのであきらめて
main関数を cronで挟みました。(参考サイト)
https://qiita.com/n0bisuke/items/66abf6ca1c12f495aa04//定期実行タイマーセット cron.schedule('*/10 * * * * *', () => { //10秒おきに実行 //処理時間の測定(前処理) const startTime = performance.now(); // 開始時間 //メイン処理 main(); function main(){ //ダイヤル処理 } //処理時間の測定(後処理) const endTime = performance.now(); // 終了時間 console.log('処理時間:'+(endTime - startTime)/1000+'秒'); // 秒表示する console.log('10秒ごとに実行') });5月30日(木)
ダイヤル処理無視して
あほみたいにループばかりするので、
同期処理するようにソースを書き直そう一億ループさせるので
ダイヤル後にメモリ解放させましょう本人の頭がパンクしてきた。
架電先、下8桁の生成に
乱数を使ってみる。// 架電先 下半身番号(8桁)を生成 let CallToBodyNum = String(Math.random()).slice(-8);二万件の発信処理に
少なくとも5時間30分はかかるので
頭番号21種✖️1億回ループさせても
無事に終了できるわけがない。架電先を乱数で指定し、ループ減らして
終了後に再度実行させよう午後0時25分
Twilioの仕様により
海外のスマホへ発信できないことが判明
固定電話でやり直します午後19時20分
午後9時54分
韓国と台湾へはダイヤルできた
折り返し確率、1/1024・参考サイト
メモリ解放方法
http://blog.livedoor.jp/aki_mana/archives/2587785.htmlnodeで同期処理例
https://qiita.com/rawHam/items/838eefc80bc35a90e49a乱数
http://ysklog.net/java/4-15.html5月29日(水)
Twilioで発信してみよう
エラーが出ました。
発信先21件✖️7570ループ目で。
Ineffective heap なんちゃらエラーWait入れて、発信キューが捌けるのを待った方がよいのでしょうか?
対策を考えます。
対策1:下記の呪文を入力
--max-old-space-size=8192
結果1
26000ループあたりで
Windows落ちる+815035037584
トヨタ紡織株式会社 とは 環形ありません
toyota-bosyoku.com
- 投稿日:2019-05-29T20:58:54+09:00
Node.jsで環境変数(AppDataの場所、PATH、その他)を取得したい
結論
process.env
を使います。node.js - how to get the OS platforms user data folder - Stack Overflow
実践
ターミナルで
node
を叩くと始まるInteractive Rubyみたいなアレで実行してみましょう。{ ALLUSERPROFILE : "C:\\ProgramData", APPDATA : "C:\\Users\\jotaro\\AppData\\Roaming", (省略 }こんなObjectが返ってきました。これはもう要するに、
process.env.APPDATA
と書けば、"C:\\Users\\jotaro\\AppData\\Roaming"
が返ってくるというわけですね。あとはprocess.env.PATH
からPATHの編集も…は…できないようです。制限
Process - Node.js Documentation によれば、
It is possible to modify this object, but such modifications will not be reflected outside the Node.js process. In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo「値を変更することはできるけど、Node.jsのプロセス内だけでしか変わらないよ」(超適当訳)とのこと。PATHを通したり環境変数をいじることは不可能なようです。うっかりPATH破壊でもしたらと思うと、納得がいきます。
もしかしたら環境変数の変更方法というのもあるのかもしれませんが、適当にググった結果ないと判断しました。応用
スタートメニュー
応用して、Windowsのユーザーのスタートメニューにアクセスしてみます。
test.jsconsole.log(`${process.env.APPDATA}\\Microsoft\\Windows\\Start Menu\\Programs\\`);スタートメニューにショートカットを作るのも楽ちんですね。おしまい。
- 投稿日:2019-05-29T17:41:34+09:00
IonicとFirebaseを使って3日で作る写真共有アプリ - 画面処理の作成(Typescript)
「IonicとFirebaseを使って3日で作る写真共有アプリ」シリーズの「画面処理の作成(Typescript)」編です。
まとめ記事はコチラ↓
https://qiita.com/kazuki502/items/585aef0d79ed1235bec0前回までで見た目の部分は出来てきたので、今回は画面内の処理を作っていこうと思います。サーバーとの通信の部分は後程やるので、今回はアップロードする写真をプレビュー表示する部分を作っていきます。
写真をアップロードする前にプレビュー表示させる
前回の画面を作った段階ではこのような画面になっていると思います。
…もっとカッコよくしたいですよね。
画面のデザインを整える
こういう時は、他のサイトを参考にして画面をデザインしていきましょう!とりあえず出てきたのはこんな感じの画面ですね。
破線の枠がある感じです。ではまずこれを作っていきましょう。ここでは"label"タグをいじっていきます。
modal-upload.component.html<label class="photo" for="photo-upload">画像を選択</label> <input type="file" name="photo" id="photo-upload"> <ion-button>送信</ion-button>余計な文言を消して、"label"にcssクラスを追加しました。
そしてscssファイルを以下のように編集します(コピペでOKです)。
modal-upload.component.scss.photo { width: 90%; height: 200px; border: 1px #000 dashed; position: absolute; left: 50%; transform: translateX(-50%); top: 30px; }そうすると、先ほどのモーダル画面がこんな風になります。
それっぽくなってきました。もうひと頑張りしたい方は、枠の中に写真のロゴなんか入れてみるとさらにそれっぽくなりますよ!Ionicにはロゴも用意されているので、目的にあるロゴを選びましょう。
Ionic logo
https://ionicons.com/アイコンをクリックして下に出てきた"WEB COMPONENT CODE"をクリックしてコピーして自分のソースにペーストしましょう。それから少し調整を加えるとこのようになります。
modal-upload.component.html<label class="photo" for="photo-upload"> <ion-icon name="image"></ion-icon><br> 画像を選択 </label> <input type="file" name="photo" id="photo-upload"> <ion-button>送信</ion-button>modal-upload.component.scss.photo { width: 90%; height: 200px; border: 1px #000 dashed; position: absolute; left: 50%; transform: translateX(-50%); top: 30px; text-align: center; vertical-align: middle; & > ion-icon { font-size: 4em; } }それっぽくなりましたね。
"ファイルを選択ボタン"を消す
ファイルを選択ボタンはなんかいらない気がしますよね。枠線の中のエリアをクリックするとファイル選択画面が開くようにしたいですよね。はい、やりましょう。とても簡単です。ボタンの表示設定で非表示に設定すればいいんです。
modal-upload.component.scss.photo { width: 90%; height: 200px; border: 1px #000 dashed; position: absolute; left: 50%; transform: translateX(-50%); top: 30px; text-align: center; vertical-align: middle; & > ion-icon { font-size: 4em; } } // 送信ボタンスタイル .send { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: 90%; } // 画像選択input #photo-upload { display: none; // ファイルをアップロードボタンを非表示に設定 }そうすると、このような画面になります。(送信ボタンなど一部スタイルをして調整しています。
選択した写真を表示させる。
このままだと、写真を選択しても画面には何も反映されません。選択した写真を画面に反映させるにはどうすればいいでしょうか。それには、"FileReader"を使えばうまくいきます。まずは完成イメージと、完成ソースコードをご覧ください。説明はその後にします。
modal-upload.component.html<label class="photo" for="photo-upload"> <div [ngClass]="{'inactive': isSelected}"> <ion-icon name="image"></ion-icon><br> 画像を選択 </div> <ion-img [ngClass]="{'inactive': !isSelected}" [src]="imageSrc"></ion-img> </label> <input type="file" name="photo" id="photo-upload" accept="image/*" (change)="previewPhoto($event)"> <ion-button class="send">送信</ion-button>modal-upload.component.scss.photo { width: 90%; height: 200px; border: 1px #000 dashed; position: absolute; left: 50%; transform: translateX(-50%); top: 30px; text-align: center; vertical-align: middle; ion-icon { font-size: 4em; } } .inactive { display: none; } // 送信ボタンスタイル .send { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); width: 90%; } // 画像選択input #photo-upload { display: none; }modal-upload.component.tsimport { Component, OnInit, Input } from '@angular/core'; // Inputを追加 import { NavParams } from '@ionic/angular'; // 追加 const RESULT = 'result'; @Component({ selector: 'app-modal-upload', templateUrl: './modal-upload.component.html', styleUrls: ['./modal-upload.component.scss'], }) export class ModalUploadComponent implements OnInit { // "value" passed in componentProps public imageSrc: any; public isSelected: boolean; @Input() value: number; private reader = new FileReader(); constructor( navParams: NavParams // 追加 ) { } ngOnInit() {} public previewPhoto(event) { const file = event.target.files[0]; this.reader.onload = ((e) => { this.imageSrc = e.target[RESULT]; this.isSelected = true; }); this.reader.readAsDataURL(file); } }このコードをコピペすればとりあえず動くと思います。それでは、それぞれで何をしているのか順を追って説明していきましょう。
FileReaderを設定する
まずは"modal-upload.component.ts"をご覧ください。今回はFileReaderをつかうので、内部オブジェクトとして宣言しています。
private reader = new FileReader();そして、写真を選択したときの処理を定義します。
public previewPhoto(event) { const file = event.target.files[0]; // パラメータからファイルを抽出 this.reader.onload = ((e) => { this.imageSrc = e.target[RESULT]; // 予め用意しておいた変数に画像データを格納する this.isSelected = true; // 写真選択フラグを切り替える }); this.reader.readAsDataURL(file); }これで、選択した写真が
this.imageSrc
に格納されます。FileReaderに関してはこれだけです。previewを設定する
選択した写真をアプリに保持出来るようになったので、写真をどのように表示させるのかを説明していきます。先ほど"previewPhoto"という関数を定義しましたが、定義しただけでは関数を実行させることはできません。画面の要素と関連付ける必要があります。今回関連付けるべき要素は、"input"タグですね。画面要素と処理の紐づけはこのようにして書くことが出来ます。
modal-upload.component.html<input type="file" name="photo" id="photo-upload" accept="image/*" (change)="previewPhoto($event)">
(change)="previewPhoto($event)"
が重要な部分です。"(change)"はangularのイベントバインディングと呼ばれるもので、"change"というのは、要素に変更があったときなので写真が選択されたときですね。Angularのバインディングについて詳しく説明しているサイトが合ったので、もっと知りたい方はこちらをご覧ください。これで、「写真が選択された」というイベントと、「選択された写真をアプリの中に保持する」という処理がつながりました。画像を表示するためにはこのように書けばよいです。
<ion-img [ngClass]="{'inactive': !isSelected}" [src]="imageSrc"></ion-img>ion-imgタグのsrc要素に画像情報が保持されている変数を割り当てるだけです。"src"ではなく、"[src]"と括弧が付いているのも、Angularのバインディングの一種です。
[ngClass]="{'inactive': !isSelected}"
は、条件によってCSSクラスを切り替えるための記述です。写真を設定したときに、初期表示で出ていた文字と写真アイコンを消して、画像を表示するためのものです。これが無い場合は、初期表示・写真選択時にこんな表示の仕方になります。イメージと違うというか、すこぶるダサいですよね笑。今回は、CSSクラスで表示・非表示を切り替えましたが、"*ngIf=..."というのを使っても同じことが出来ます。
- 投稿日:2019-05-29T13:05:59+09:00
discord.jsのfilterを使ってみたメモ
こんにちは、ほぼ毎日discord.jsでbot作ってる人です。(?)
前置き
今回は、discord.jsでfilterを使ってみたのを記事にしてます
(説明が下手なのは許してください)
discord.js: https://discord.js.org/
Collection: https://discord.js.org/#/docs/main/stable/class/Collection使い方
テキストチャンネルだけの数
const textChannels = message.guild.channels.filter(t => t.type == "text").size message.channel.send(textChannels);これでテキストチャンネルのみの数が表示されます。
説明すると
message.guild.channels.filter
(メッセージが送信されたギルドのチャンネルをフィルターする)
[以下略].filter.(t => t.type == 'text')
(t
はmessage.guild.channels
の省略版です(多分))
(t.type == 'text'
はチャンネルのタイプです。)
'text'
に入るのは、https://discord.js.org/#/docs/main/stable/class/Channel?scrollTo=type をご覧下さい。説明はこれで終わりです
終わりに
他にもfindとかあるのでいつかだそうかなと思ってます。
- 投稿日:2019-05-29T13:05:59+09:00
JavaScriptのfilterをdiscord.jsで使ってみたメモ
こんにちは、ほぼ毎日discord.jsでbot作ってる人です。(?)
前置き
今回は、discord.jsでJavaScriptのfilterを使ってみたのを記事にしてます
(説明が下手なのは許してください)
(まだ無知なことがいっぱい)
discord.js: https://discord.js.org/
Collection: https://discord.js.org/#/docs/main/stable/class/Collection使い方
テキストチャンネルだけの数
const textChannels = message.guild.channels.filter(t => t.type == "text").size message.channel.send(textChannels);これでテキストチャンネルのみの数が表示されます。
説明すると
message.guild.channels.filter
(メッセージが送信されたギルドのチャンネルをフィルターする)
[以下略].filter.(t => t.type == 'text')
(t
はmessage.guild.channels
の省略版です(多分))
(t.type == 'text'
はチャンネルのタイプです。)
'text'
に入るのは、https://discord.js.org/#/docs/main/stable/class/Channel?scrollTo=type をご覧下さい。追記
.size
は○○の数です。説明はこれで終わりです
終わりに
他にもfindとかあるのでいつかだそうかなと思ってます。
- 投稿日:2019-05-29T12:48:30+09:00
【日記】技術書どおりにやって上手くいかなかったwinstonを中途半端に上手くいかせた
最近、こちらの本を読んでWebアプリ開発を勉強しています。
JavaScriptでのWeb開発 ~ Node.js + Express + MongoDB + ReactでWebアプリを開発しよう ~ その2(iOS対応版)
途中、winstonというロギングアプリを導入する方法が解説されていたところで詰まりました。本書では次のようにwinstonの設定ファイルを定義するよう書いてありました。
lib/logger.jsvar winston = require('winston') function Logger(){ return winston.add(winston.transports.File, ( filename: "log/warning.log", maxsize: 1048576, level: "warn" }); } module.exports = new Logger();この通りにファイルを作成してアプリを起動しようとすると、次のようなエラーが発生。
$ node app.js /Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/node_modules/winston-transport/legacy.js:18 throw new Error('Invalid transport, must be an object with a log method.'); ^ Error: Invalid transport, must be an object with a log method. at new LegacyTransportStream (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/node_modules/winston-transport/legacy.js:18:11) at DerivedLogger.add (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/node_modules/winston/lib/winston/logger.js:345:11) at Object.winston.<computed> [as add] (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/node_modules/winston/lib/winston.js:110:68) at new Logger (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/lib/logger.js:4:20) at Object.<anonymous> (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/lib/logger.js:12:18) at Module._compile (internal/modules/cjs/loader.js:759:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:770:10) at Module.load (internal/modules/cjs/loader.js:628:32) at Function.Module._load (internal/modules/cjs/loader.js:555:12) at Module.require (internal/modules/cjs/loader.js:666:19) at require (internal/modules/cjs/helpers.js:16:16) at Object.<anonymous> (/Users/anaakikutsushita/Desktop/code/learn-nodejs/hoge/app.js:12:11) at Module._compile (internal/modules/cjs/loader.js:759:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:770:10) at Module.load (internal/modules/cjs/loader.js:628:32) at Function.Module._load (internal/modules/cjs/loader.js:555:12)ググったところ、次のような解決法を見つけました。
Invalid transport, must be an object with a log method winston mongodb loggingこれに従って、前述のfunction Logger()を次のように修正。
lib/logger.jsfunction Logger(){ return winston.add(new winston.transports.File({ filename: "log/error.log", maxsize: 1048576, level: "error" })); }こうすることで正常にアプリが起動し、ログファイルも生成されるようになりました。
しかし問題はここからで、技術書にあるような詳細なログが出力されませんでした。
出力されたログはと言うと、次のようなちょっとしたもの。error.log{"level":"error"}この一行で全文です。
明らかに正常な動作とは思えず、しかしどこがどう間違っているのかもよくわからず、現時点ではひとまずこの問題は諦めるということで切り上げました。
- 投稿日:2019-05-29T11:55:45+09:00
Next.js ApplicationにPassport+Auth0で認証機能を追加する
はじめに
この記事はNext.jsのサンプルアプリケーションにPassport+Auth0を利用して認証・認可機能を追加する手順で、こちらの原文を元に作成しています。Node.jsとnpmのインストール、Auth0の無料アカウントの取得とテナントの作成が完了していることが前提となっています。まだの方はこちらの記事を参照の上ご準備をお願いします。
完成版のソースコードはここで公開しています。環境
- OS :
macOS Mojave 10.14.5
- Node.js :
10.15.3
- npm :
6.4.1
手順
準備と基本プロジェクトの作成
NPM Project Directoryを作成して初期化します。
$ mkdir nextjs-passport $ cd nextjs-passport $ npm init -y必要なパッケージをインストールします。
$ npm i body-parser bootstrap dotenv \ dotenv-webpack express isomorphic-fetch \ next react react-bootstrap \ react-dom styled-components.babelrcを作成してコンパイルパラメータを設定します。
.babelrc// ./.babelrc { "presets": ["next/babel"], "plugins": [["styled-components", { "ssr": true }]] }.envを作成して環境変数を設定します。このタイミングではPORTだけで問題ありません。
.env# ./.env PORT=3000next.config.jsを作成して.envファイルのパスを指定します。
next.config.js// ./next.config.js require("dotenv").config(); const path = require("path"); const Dotenv = require("dotenv-webpack"); module.exports = { webpack: config => { config.plugins = config.plugins || []; config.plugins = [ ...config.plugins, // Read the .env file new Dotenv({ path: path.join(__dirname, ".env"), systemvars: true }) ]; return config; } };ソースコードを保管するディレクトリを作成します。
$ mkdir -p src/components $ mkdir src/pages $ mkdir src/statesrc/pages配下にindex.jsを作成します。
index.js// ./src/pages/index.js import styled from "styled-components"; const Rocket = styled.div` text-align: center; img { width: 630px; } `; function Index() { return ( <Rocket> <img src="https://media.giphy.com/media/QbumCX9HFFDQA/giphy.gif" /> </Rocket> ); } export default Index;src/pages配下に_document.jsを作成します。
_document.js// ./src/pages/_document.js import Document, { Head, Html, Main, NextScript } from "next/document"; import { ServerStyleSheet } from "styled-components"; export default class MyDocument extends Document { static async getInitialProps(ctx) { const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; try { ctx.renderPage = () => originalRenderPage({ enhanceApp: App => props => sheet.collectStyles(<App {...props} />) }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, styles: ( <> {initialProps.styles} {sheet.getStyleElement()} </> ) }; } finally { sheet.seal(); } } render() { return ( <Html> <Head> <link rel="stylesheet" href="https://bootswatch.com/4/darkly/bootstrap.min.css" /> </Head> <body> <Main /> <NextScript /> </body> </Html> ); } }package.jsonのscriptsプロパティを下記のように修正します。
package.json// ./package.json "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "next ./src" },npm run devを実行してアプリケーションを起動しChromeで
http://localhost:3000
にアクセスします。画面が正常に表示されれば成功です。$ npm run dev
Applicationの作成
src配下にthoughts-api.jsを作成してAPI Endpointを定義します。
thoughts-api.js// ./src/thoughts-api.js const bodyParser = require("body-parser"); const express = require("express"); const router = express.Router(); router.use(bodyParser.json()); const thoughts = [ { _id: 123, message: "I love pepperoni pizza!", author: "unknown" }, { _id: 456, message: "I'm watching Netflix.", author: "unknown" } ]; router.get("/api/thoughts", (req, res) => { const orderedThoughts = thoughts.sort((t1, t2) => t2._id - t1._id); res.send(orderedThoughts); }); router.post("/api/thoughts", (req, res) => { const { message } = req.body; const newThought = { _id: new Date().getTime(), message, author: "unknown" }; thoughts.push(newThought); res.send({ message: "Thanks!" }); });src配下にserver.jsを作成してhttpリクエストをlistenします。
server.js// ./src/server.js require("dotenv").config(); const express = require("express"); const http = require("http"); const next = require("next"); const thoughtsAPI = require("./thoughts-api"); const dev = process.env.NODE_ENV !== "production"; const app = next({ dev, dir: "./src" }); const handle = app.getRequestHandler(); app.prepare().then(() => { const server = express(); server.use(thoughtsAPI); // handling everything else with Next.js server.get("*", handle); http.createServer(server).listen(process.env.PORT, () => { console.log(`listening on port ${process.env.PORT}`); }); });package.jsonのscriptsプロパティを下記のように修正します。
package.json// ./package.json "scripts": { "dev": "node ./src/server.js", "build": "next build ./src", "start": "NODE_ENV=production node ./src/server.js" },npm run devを実行してアプリケーションを起動しChromeで
http://localhost:3000/api/thoughts
にアクセスします。画面が正常に表示されれば成功です。$ npm run dev
src/components配下にThought.js, Thoughts.jsを作成してAPIを呼び出します。
Thought.js// ./src/components/Thought.js import Card from "react-bootstrap/Card" export default function Thought({ thought }) { const cardStyle = { marginTop: "15px" }; return ( <Card bg="secondary" text="white" style={cardStyle}> <Card.Body> <Card.Title>{thought.message}</Card.Title> <Card.Text>by {thought.author}</Card.Text> </Card.Body> </Card> ); }Thoughts.js// ./src/components/Thoughts.js import Col from "react-bootstrap/Col"; import Row from "react-bootstrap/Row"; import Thought from "./Thought"; export default function Thoughts(props) { return ( <Row> <Col xs={12}> <h2>Latest Thoughts</h2> </Col> {props.thoughts && props.thoughts.map(thought => ( <Col key={thought._id} xs={12} sm={6} md={4} lg={3}> <Thought thought={thought} /> </Col> ))} {!props.thoughts && <Col xs={12}>Loading...</Col>} </Row> ); }src/pages/index.jsを修正してAPI経由で取得するデータを表示します。
index.js// ./src/pages/index.js import Container from "react-bootstrap/Container"; import fetch from "isomorphic-fetch"; import Thoughts from "../components/Thoughts"; function Index(props) { return ( <Container> <Thoughts thoughts={props.thoughts} /> </Container> ); } Index.getInitialProps = async ({ req }) => { const baseURL = req ? `${req.protocol}://${req.get("Host")}` : ""; const res = await fetch(`${baseURL}/api/thoughts`); return { thoughts: await res.json() }; }; export default Index;npm run devを実行してアプリケーションを起動しChromeで
http://localhost:3000
にアクセスします。画面が正常に表示されれば成功です。src/components配下にNavbar.jsを、src/pages配下に_app.jsを作成してナビゲーションバーを追加します。
Navbar.js// ./src/components/Navbar.js import Link from "next/link"; import Container from "react-bootstrap/Container"; import Navbar from "react-bootstrap/Navbar"; import Nav from "react-bootstrap/Nav"; export default function AppNavbar() { const navbarStyle = { marginBottom: "25px" }; return ( <Navbar bg="light" expand="lg" style={navbarStyle}> <Container> <Navbar.Brand> <Link href="/"> <a>Thoughts!</a> </Link> </Navbar.Brand> <Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Collapse id="basic-navbar-nav"> <Nav className="mr-auto"> <Link href="/share-thought"> <a className="nav-link">New Thought</a> </Link> </Nav> </Navbar.Collapse> </Container> </Navbar> ); }_app.js// ./src/pages/_app.js import React from "react"; import App, { Container as NextContainer } from "next/app"; import Head from "next/head"; import Container from "react-bootstrap/Container"; import Jumbotron from "react-bootstrap/Jumbotron"; import Navbar from "../components/Navbar"; class MyApp extends App { render() { const { Component, pageProps } = this.props; return ( <NextContainer> <Head> <title>Thoughts!</title> </Head> <Navbar /> <Container> <Jumbotron> <Component {...pageProps} /> </Jumbotron> </Container> </NextContainer> ); } } export default MyApp;src/pages配下にshare-thought.jsを作成して新しいルートを定義します。
share-thought.js// ./src/pages/share-thought.js import Form from "react-bootstrap/Form"; import Router from "next/router"; import Button from "react-bootstrap/Button"; import Container from "react-bootstrap/Container"; const { useState } = require("react"); export default function ShareThought() { const [message, setMessage] = useState(""); async function submit(event) { event.preventDefault(); await fetch("/api/thoughts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message }) }); Router.push("/"); } return ( <Container> <Form onSubmit={submit}> <Form.Group> <Form.Label>What is in your mind?</Form.Label> <Form.Control type="text" placeholder="Say something" onChange={e => setMessage(e.target.value)} value={message} /> </Form.Group> <Button variant="primary" type="submit"> Share </Button> </Form> </Container> ); }npm run devを実行してアプリケーションを起動しChromeで
http://localhost:3000/api/thoughts
にアクセスします。画面が正常に表示されれば成功です。
認証機能の組み込み
Auth0にログインしてテナントを作成します。この記事では詳細な手順は割愛しています。こちらをご参照お願いします。
作成したApplicationをAuth0に登録します。左のペインからApplicationsをクリックして+CREATE APPLICATIONを押します。
Nameに任意の名前を入力、Choose an application typeでRegular Web Applicationsを選択してCREATEを押します。
Login/Logout後にリダイレクトさせるURLを指定するため、Settingsタブを選択してAllowed Callback URLs, Allowed Logout URLsに下記の値を入力してSAVE CHANGESを押します。
- Allowed Callback URLs :
http://localhost:3000/callback
- Allowed Logout URLs :
http://localhost:3000
必要なパッケージをインストールします。
$ npm install passport passport-auth0 express-session uid-safesrc配下のserver.jsを修正します。
server.js// ./src/server.js require("dotenv").config(); const express = require("express"); const http = require("http"); const next = require("next"); const session = require("express-session"); // 1 - importing dependencies const passport = require("passport"); const Auth0Strategy = require("passport-auth0"); const uid = require('uid-safe'); const authRoutes = require("./auth-routes"); const thoughtsAPI = require("./thoughts-api"); const dev = process.env.NODE_ENV !== "production"; const app = next({ dev, dir: "./src" }); const handle = app.getRequestHandler(); app.prepare().then(() => { const server = express(); // 2 - add session management to Express const sessionConfig = { secret: uid.sync(18), cookie: { maxAge: 86400 * 1000 // 24 hours in milliseconds }, resave: false, saveUninitialized: true }; server.use(session(sessionConfig)); // 3 - configuring Auth0Strategy const auth0Strategy = new Auth0Strategy( { domain: process.env.AUTH0_DOMAIN, clientID: process.env.AUTH0_CLIENT_ID, clientSecret: process.env.AUTH0_CLIENT_SECRET, callbackURL: process.env.AUTH0_CALLBACK_URL }, function(accessToken, refreshToken, extraParams, profile, done) { return done(null, profile); } ); // 4 - configuring Passport passport.use(auth0Strategy); passport.serializeUser((user, done) => done(null, user)); passport.deserializeUser((user, done) => done(null, user)); // 5 - adding Passport and authentication routes server.use(passport.initialize()); server.use(passport.session()); server.use(authRoutes); server.use(thoughtsAPI); // 6 - you are restricting access to some routes const restrictAccess = (req, res, next) => { if (!req.isAuthenticated()) return res.redirect("/login"); next(); }; server.use("/profile", restrictAccess); server.use("/share-thought", restrictAccess); // handling everything else with Next.js server.get("*", handle); http.createServer(server).listen(process.env.PORT, () => { console.log(`listening on port ${process.env.PORT}`); }); });src配下にauth-routes.jsを作成して認証ルートを定義します。
auth-routes.js// ./src/auth-routes.js const express = require("express"); const passport = require("passport"); const router = express.Router(); router.get("/login", passport.authenticate("auth0", { scope: "openid email profile" }), (req, res) => res.redirect("/")); router.get("/callback", (req, res, next) => { passport.authenticate("auth0", (err, user) => { if (err) return next(err); if (!user) return res.redirect("/login"); req.logIn(user, (err) => { if (err) return next(err); res.redirect("/"); }); })(req, res, next); }); router.get("/logout", (req, res) => { req.logout(); const {AUTH0_DOMAIN, AUTH0_CLIENT_ID, BASE_URL} = process.env; res.redirect(`https://${AUTH0_DOMAIN}/logout?client_id=${AUTH0_CLIENT_ID}&returnTo=${BASE_URL}`); }); module.exports = router;.envに必要な環境変数を追加します。
.envPORT=3000 AUTH0_DOMAIN=mokomoko.auth0.com AUTH0_CLIENT_ID=o4u8CRD1roZEN2t96AZZnfrO3NeegeN7 AUTH0_CLIENT_SECRET=... AUTH0_CALLBACK_URL=http://localhost:3000/callback BASE_URL=http://localhost:3000AUTH0_DOMAIN, AUTH0_CLIENR_ID, AUTH0_CLIENT_SECRETはApplications->Settingsから確認できます。
src配下のthoughts-api.jsを修正します。
thoughts-api.jsconst bodyParser = require("body-parser"); const express = require("express"); const router = express.Router(); router.use(bodyParser.json()); const thoughts = [ { _id: 123, message: "I love pepperoni pizza!", author: "unknown" }, { _id: 456, message: "I'm watching Netflix.", author: "unknown" } ]; router.get("/api/thoughts", (req, res) => { const orderedThoughts = thoughts.sort((t1, t2) => t2._id - t1._id); res.send(orderedThoughts); }); function ensureAuthenticated(req, res, next) { if (req.isAuthenticated()) return next(); res.send(401); } router.post("/api/thoughts", ensureAuthenticated, (req, res) => { const { message } = req.body; const newThougth = { _id: new Date().getTime(), message, author: req.user.displayName }; thoughts.push(newThougth); res.send({ message: "Thanks!" }); }); module.exports = router;src/components配下のNavbar.jsを修正します。
Navbar.js// ./src/components/Navbar.js import Link from "next/link"; import Container from "react-bootstrap/Container"; import Navbar from "react-bootstrap/Navbar"; import Nav from "react-bootstrap/Nav"; export default function AppNavbar({ user }) { const navbarStyle = { marginBottom: "25px" }; return ( <Navbar bg="light" expand="lg" style={navbarStyle}> <Container> <Navbar.Brand> <Link href="/"> <a>Thoughts!</a> </Link> </Navbar.Brand> <Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Collapse id="basic-navbar-nav"> <Nav className="mr-auto"> {user && ( <> <Link href="/share-thought"> <a className="nav-link">New Thought</a> </Link> <Link href="/profile"> <a className="nav-link">Profile</a> </Link> <Link href="/logout"> <a className="nav-link">Log Out</a> </Link> </> )} {!user && ( <Link href="/login"> <a className="nav-link">Log In</a> </Link> )} </Nav> </Navbar.Collapse> </Container> </Navbar> ); }src/pages配下にprofile.jsを作成してユーザプロファイル画面を表示します。
profile.js// ./src/pages/profile.js import styled from "styled-components"; const Picture = styled.img` border-radius: 50%; border: 3px solid white; width: 100px; `; function Profile({ user }) { return ( <div> <h2> <Picture src={user.picture} alt={user.displayName} /> Hello, {user.displayName} </h2> <p>This is what we know about you:</p> <ul> { Object.keys(user).map(key => ( <li key={key}>{key}: {user[key].toString()}</li> ))} </ul> </div> ); } export default Profile;src/pages配下の_app.jsを修正します。
_app.js// ./src/pages/_app.js import React from "react"; import App, { Container as NextContainer } from "next/app"; import Head from "next/head"; import Container from "react-bootstrap/Container"; import Jumbotron from "react-bootstrap/Jumbotron"; import Navbar from "../components/Navbar"; class MyApp extends App { static async getInitialProps({ Component, ctx }) { let pageProps = {}; if (Component.getInitialProps) { pageProps = await Component.getInitialProps(ctx); } if (ctx.req && ctx.req.session.passport) { pageProps.user = ctx.req.session.passport.user; } return { pageProps }; } constructor(props) { super(props); this.state = { user: props.pageProps.user }; } render() { const { Component, pageProps } = this.props; const props = { ...pageProps, user: this.state.user, }; return ( <NextContainer> <Head> <title>Thoughts!</title> </Head> <Navbar user={this.state.user} /> <Container> <Jumbotron> <Component {...props} /> </Jumbotron> </Container> </NextContainer> ); } } export default MyApp;npm run devを実行してアプリケーションを起動しChromeで
http://localhost:3000
にアクセスします。Auth0の組み込みLogin画面が表示されて、ログイン後Profileタブをクリックしてユーザプロファイル正常に表示されてれば成功です。
おわりです。
- 投稿日:2019-05-29T11:30:44+09:00
ask-sdkでuserIdを取得する方法のワークアラウンド
コアにパッチを送ってマージしてもらったのですが、リリースまでもうしばらく時間がかかりそうなのでワークアラウンドとしてメモ。
2019/05/30追記
ask-sdk / ask-sdk-coreがリリースされました。
https://github.com/alexa/alexa-skills-kit-sdk-for-nodejs/blob/2.0.x/CHANGELOG.md
getUserId
はversion2.6.0以降で利用できます。要点
- ask-sdk-coreのutilityに
getUserId
がないため、自力で取得する必要がある。- リリースブランチにはマージされているが、リリースされていないので、まだ使えない。
- マージしてもらったコードを貼るので、待てない人はこれを使ってね
コード
/** * Retrieves the user ID from the request. * * The method retrieves the userId property from the input request. This value uniquely identifies the user * and is generally used as input for some Alexa-specific API calls. More information about this can be found here: * https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#system-object * * @param {RequestEnvelope} requestEnvelope * @return {string} */ export function getUserId(requestEnvelope : RequestEnvelope) : string { return requestEnvelope.context.System.user ? requestEnvelope.context.System.user.userId : null; }余談
getDeviceId()
はすでにあるので、device idを取りたい場合は、import { getDeviceId } from 'ask-sdk-core'
で良いと思います。
- 投稿日:2019-05-29T09:00:15+09:00
AWS LambdaとHyperledger Fabric SDK for Node.jsを利用してAmazon Managed Blockchainのブロックチェーンネットワークにアクセスする
Amazon Managed BlockchainのブロックチェーンネットワークはVPC内に構築されるため、VPC外からブロックチェーンネットワークへアクセスするにはクライアントとなるアプリなりサービスを開発して経由する必要があります。
AWS LambdaでVPC内に関数を配置するとアクセス可能になるはずなので試してみました。Amazon VPC 内のリソースにアクセスできるように Lambda 関数を構成する - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.html前提
Dockerを利用して開発環境を構築します。
AWS Lambdaへのデプロイにはserverlessを利用します。
Serverless - The Serverless Application Framework powered by AWS Lambda, API Gateway, and more
https://serverless.com/AWS Lambdaを利用するのでAWSアカウントや権限も必要となります。
> docker --version Docker version 18.09.2, build 6247962 > docker-compose --version docker-compose version 1.23.2, build 1110ad01 > sls --version 1.43.0Amazon Managed Blockchainでブロックチェーンネットワークが構築済み
下記の2記事の手順でブロックチェーンネットワークが構築済みでfabcarのサンプルが動作する環境がある前提です。
Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7eAmazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる - Qiita
https://qiita.com/kai_kou/items/5a6b0fc148f04857a878開発環境を構築する
Hyperledger FabricのSDKをAWS Lambda上で利用するにはLinuxで
npm install
する必要があったのでDockerを利用して開発環境を構築します。Dockerコンテナの立ち上げ
> mkdir 任意のディレクトリ > cd 任意のディレクトリ > touch Dockerfile > touch docker-compose.ymlAWS Lambdaで利用できるNode.jsのバージョンは
8.10
と10.x
となります。
Hyperledger Fabric SDK for Node.jsは8.x系で動作するのでDockerにも8.x
をインストールします。AWS Lambda ランタイム - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.htmlDockerfileFROM amazonlinux RUN yum update && \ curl -sL https://rpm.nodesource.com/setup_8.x | bash - && \ yum install -y gcc-c++ make nodejs && \ npm i -g serverlessdocker-compose.ymlversion: '3' services: app: build: . volumes: - ./:/src working_dir: /src tty: true> docker-compose build > docker-compose run app bashNode.jsのプロジェクト作成
コンテナが立ち上がったらserverlessでNode.jsのテンプレートでプロジェクトを作成します。
コンテナ内$ sls create \ --template aws-nodejs \ --path fablic-app $ cd fablic-app $ npm initHyperledger Fabric SDK for Node.jsが利用できるように
package.json
を編集してnpm install
を実行します。package.json{ "name": "fabcar", "version": "1.0.0", "description": "Hyperledger Fabric Car Sample Application", "main": "fabcar.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "fabric-ca-client": "~1.2.0", "fabric-client": "~1.2.0", "fs-extra": "^8.0.1", "grpc": "^1.6.0" }, "author": "", "license": "Apache-2.0", "keywords": [ ] }コンテナ内$ npm install証明書の用意
Hyperledger Fabric SDK for Node.jsでブロックチェーンネットワークへアクセスするのに各種証明書が必要となるためプロジェクトに含めます。こちらはDockerコンテナ外で行います。
コンテナ外> cd fablic-app > aws s3 cp s3://us-east-1.managedblockchain-preview/etc/managedblockchain-tls-chain.pem ./managedblockchain-tls-chain.pem # EC2インスタンスからhfc-key-storeフォルダを取得 > scp -r -i [EC2インスタンス用のpemファイルパス] ec2-user@xxx.xxx.xxx.xxx:/home/ec2-user/fabric-samples/fabcar/hfc-key-store ./hfc-key-store実装
今回はブロックチェーンネットワークのステートDBから情報を取得する実装を行います。
下記記事でも利用しているquery.js
をAWS Lambdaで実行できるように編集しました。Amazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる - Qiita
https://qiita.com/kai_kou/items/5a6b0fc148f04857a878
handler.js
はquery.js
のメソッドを呼び出し結果を返す実装にしました。> cd fabric-app > tree -F -L 1 . . ├── handler.js ├── hfc-key-store/ ├── managedblockchain-tls-chain.pem ├── node_modules/ ├── package-lock.json ├── package.json ├── query.js └── serverless.ymlhandler.jsvar query = require('./query'); module.exports.hello = async (event) => { var result = await query.run(); return { statusCode: 200, body: JSON.stringify({ message: JSON.parse(result), input: event, }, null, 2), }; };実装のポイントは下記となります。
- 証明書フォルダ
hfc-key-store
を/tmp
にコピーして利用module.exports.run = async () => {}
でhandler.js
から呼び出し可能にするasync/await
で同期的に実行するquery.js'use strict'; /* * Copyright IBM Corp All Rights Reserved * * SPDX-License-Identifier: Apache-2.0 */ /* * Chaincode query */ var Fabric_Client = require('fabric-client'); var path = require('path'); var util = require('util'); var os = require('os'); var fs = require('fs-extra'); // var fabric_client = new Fabric_Client(); // setup the fabric network var channel = fabric_client.newChannel('mychannel'); var peer = fabric_client.newPeer('grpcs://nd-xxxxxxxxxxxxxxxxxxxxxxxxxx.m-xxxxxxxxxxxxxxxxxxxxxxxxxx.n-xxxxxxxxxxxxxxxxxxxxxxxxxx.managedblockchain.us-east-1.amazonaws.com:30003', { pem: fs.readFileSync('./managedblockchain-tls-chain.pem').toString(), 'ssl-target-name-override': null}); channel.addPeer(peer); // var member_user = null; var store_base_path = path.join(__dirname, 'hfc-key-store'); var store_path = path.join('/tmp', 'hfc-key-store'); console.log('Store path:'+store_path); var tx_id = null; // create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting module.exports.run = async () => { // 証明書ファイルを/tmp ディレクトリにコピーして利用する fs.copySync(store_base_path, store_path); console.log('Store copied!'); return await Fabric_Client.newDefaultKeyValueStore({ path: store_path }).then((state_store) => { // assign the store to the fabric client fabric_client.setStateStore(state_store); var crypto_suite = Fabric_Client.newCryptoSuite(); // use the same location for the state store (where the users' certificate are kept) // and the crypto store (where the users' keys are kept) var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path}); crypto_suite.setCryptoKeyStore(crypto_store); fabric_client.setCryptoSuite(crypto_suite); // get the enrolled user from persistence, this user will sign all requests return fabric_client.getUserContext('user1', true); }).then((user_from_store) => { if (user_from_store && user_from_store.isEnrolled()) { console.log('Successfully loaded user1 from persistence'); member_user = user_from_store; } else { throw new Error('Failed to get user1.... run registerUser.js'); } // queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'], // queryAllCars chaincode function - requires no arguments , ex: args: [''], const request = { //targets : --- letting this default to the peers assigned to the channel chaincodeId: 'fabcar', fcn: 'queryAllCars', args: [''] }; // send the query proposal to the peer return channel.queryByChaincode(request); }).then((query_responses) => { console.log("Query has completed, checking results"); // query_responses could have more than one results if there multiple peers were used as targets if (query_responses && query_responses.length == 1) { if (query_responses[0] instanceof Error) { console.error("error from query = ", query_responses[0]); } else { console.log("Response is ", query_responses[0].toString()); return query_responses[0].toString(); } } else { console.log("No payloads were returned from query"); } }).catch((err) => { console.error('Failed to query successfully :: ' + err); }); };serverlessの設定
AWS LambdaでVPC内配置されるように
serverless.yml
を編集します。
vpc
やiamRoleStatements
の定義については下記が参考になりました。
セキュリティグループとサブネットはAmazon Managed Blockchainで構築したブロックチェーンネットワークと同じものを指定します。ServerlessでLambdaをVPC内にデプロイする - Qiita
https://qiita.com/70_10/items/ae22a7a9bca62c273495serverless.ymlservice: fabric-app provider: name: aws runtime: nodejs8.10 iamRoleStatements: - Effect: "Allow" Action: - "ec2:CreateNetworkInterface" - "ec2:DescribeNetworkInterfaces" - "ec2:DeleteNetworkInterface" Resource: - "*" vpc: securityGroupIds: - sg-xxxxxxxxxxxxxxxxx subnetIds: - subnet-xxxxxxxx - subnet-yyyyyyyy functions: hello: handler: handler.hello events: - http: path: hello method: getAWS Lambdaにデプロイする
> sls deploy Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service fabric-app.zip file to S3 (40.12 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .............. Serverless: Stack update finished... Service Information service: fabric-app stage: dev region: us-east-1 stack: fabric-app-dev resources: 10 api keys: None endpoints: GET - https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello functions: hello: fabric-app-dev-hello layers: None Serverless: Removing old service artifacts from S3...デプロイができたらエンドポイントにアクセスしてみます。
> curl https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/hello { "message": [ { "Key": "CAR0", "Record": { "make": "Toyota", "model": "Prius", "colour": "blue", "owner": "Tomoko" } }, { "Key": "CAR1", "Record": { "make": "Ford", "model": "Mustang", "colour": "red", "owner": "Brad" } }, (略) ], "input": { "resource": "/hello", "path": "/hello", "httpMethod": "GET", (略) } }はい。
無事にAWS Lambda関数からHyperledger Fabric SDK for Node.jsを利用してブロックチェーンネットワークにアクセスすることができました。VPC内にLamnbda関数を配置する必要があるため、ENI(仮想ネットワークインターフェース)の利用に伴う制限や起動速度に課題が発生するかもしれませんので、実際に利用する際には負荷検証などしっかりと行う必要がありそうです。
AWS LambdaをVPC内に配置する際の注意点 | そるでぶろぐ
https://devlog.arksystems.co.jp/2018/04/04/4807/参考
Amazon VPC 内のリソースにアクセスできるように Lambda 関数を構成する - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/vpc.htmlServerless - The Serverless Application Framework powered by AWS Lambda, API Gateway, and more
https://serverless.com/Amazon Managed BlockchainでHyperledger Fabricのブロックチェーンネットワークを構築してみた - Qiita
https://qiita.com/kai_kou/items/e02e34dd9abb26219a7eAmazon Managed Blockchainで作成したブロックチェーンネットワークにHyperledger Fabric SDK for Node.jsでアクセスしてみる - Qiita
https://qiita.com/kai_kou/items/5a6b0fc148f04857a878AWS Lambda ランタイム - AWS Lambda
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.htmlServerlessでLambdaをVPC内にデプロイする - Qiita
https://qiita.com/70_10/items/ae22a7a9bca62c273495AWS LambdaをVPC内に配置する際の注意点 | そるでぶろぐ
https://devlog.arksystems.co.jp/2018/04/04/4807/