はじめに
前回の記事では、Code EngineのFunctionsに非同期処理を実装する方法を紹介しましたが、今回は npm
でパッケージのモジュールを導入したアプリを公開する方法を紹介します。
ただ、この内容だけだと少ないので、私が作った際に用意ソースコードや、テスト用のコード等を紹介します。
開発環境
- OS : Windows 11 Enterprise
- Node : 18.17.0
- npm : 9.6.7
- Editor : Visual Studio Code (拡張機能で Rest Client を利用)
アプリ
IBMソリューションブログのRSSからランダムで選んだ1件の記事の情報をjsonで応答する
開発手順
対象のコードを開発する手順自体は普通です
-
npm init
で開発環境のディレクトリにpackage.json
を生成する -
npm install xxx
で必要なパッケージモジュールを導入する
今回はdotenv
、rss-parser
をインストールしています -
main.js
を作成し、必要な機能を実装する - テスト呼び出し用の
driver.js
を作成し動作検証する - Code EngineのFunctionsに登録する
- Rest Clientで動作検証する
1.npm init
で開発環境のディレクトリに package.json
を生成する
npm init
で、(index.js)? と聞かれる部分だけ main.js
を指定しています。いろいろ指定したい方は自由に設定ください。
もうちょっと書くべきところを書いても良いのですが、今回は簡易に進めたいと思います。
こんな感じのpackage.json
になっていれば大丈夫です。
{
"name": "qiita-solution-blog-function",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
}
}
2. npm install xxx
で必要なパッケージモジュールを導入する
今回紹介するコードでは、 .env
から環境変数を取得するのと、IBMソリューションブログのRSSの情報を取得したいため、 dotenv
、rss-parser
をインストールしています。
3. main.js
を作成し、必要な機能を実装する
やりたいことだけであれば、末尾10行ちょっとくらいで良いのですが、インターネット上にこのサービスを公開する場合に、URL知っていると誰でも呼び出されると困る、ということもあると思います。なので、簡易に Bearer
の認証を入れるようにしています。
環境変数として定義された BEARER_VALUE
の値と同じものがリクエストヘッダーの Authorization
に設定されていないと弾く仕組みにしています。あと、 POST
でリクエストされていない場合も弾いています。機能内容的には GET
で良いと思いますが一応。
require('dotenv').config();
const Parser = require('rss-parser');
const parser = new Parser();
const bearerValue = process.env.BEARER_VALUE;
const main = async (args) => {
if(args.__ce_method !== "POST" ||
!args.__ce_headers.Authorization){
return {
statusCode: 400,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer error="invalid_request""'
},
body: {
text:"Bad Request"
},
};
}
const reqAuthorization = args.__ce_headers.Authorization;
if(!reqAuthorization.match(/^[bB]earer /)){
return {
statusCode: 401,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer realm="token_required"'
},
body: {
text:"Unauthorized"
},
};
}
const reqBearerToken = reqAuthorization.replace(/^[bB]earer /,'');
if(reqBearerToken !== bearerValue){
return {
statusCode: 401,
headers: {
'Content-Type': 'application/json',
'WWW-Authenticate': 'Bearer error="invalid_token"'
},
body: {
text:"Unauthorized"
},
};
}
const feed = await parser.parseURL("https://www.ibm.com/blogs/solutions/jp-ja/rss");
const items = feed.items.map((data) => {
return data;
});
const targetNum = Math.floor(Math.random() * (items.length));
const body = items[targetNum];
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body,
};
}
module.exports.main = main;
また、環境変数用の .env
ファイルも作成します。
BEARER_VALUE
は今回キーボードを適当にグシャグシャして設定しました。他の人に類推できなければ何でも良いと思います。
BEARER_VALUE=3zawx4se5drfv6t7b6numxsECRVt7by8nu9imO9oi8UumZYT5cRVaTByjIxrcvbK
4. テスト呼び出し用の driver.js
を作成し動作検証する
さて、Code EngineのFunctionsに登録するためのモジュールは main()
を外から実行するようにコーディングする関係で、このコード単体では動作しません。
なので、このコードを呼び出すための driver.js
を作成します。
呼び出す際のパラメータに以下を設定しています
-
__ce_method
: Code EngineのFunctionsにどのメソッドで呼び出されたか識別するもの -
__ce_headers
: Code EngineのFunctionsが呼び出された際のヘッダーを識別するもの
const mainJs = require('./main');
const main = async () => {
const returnMain = await mainJs.main({
"__ce_method": "POST",
"__ce_headers": {"Authorization":"Bearer 3zawx4se5drfv6t7b6numxsECRVt7by8nu9imO9oi8UumZYT5cRVaTByjIxrcvbK"}
});
console.log(returnMain);
}
main()
試しに driver.js
を呼び出すとRSSからランダムで1つの情報を返してくれることがわかります。
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>node driver.js
{
statusCode: 200,
headers: { 'Content-Type': 'application/json' },
body: {
creator: 'masayayagihashi',
title: 'ダイバーシティーハッカソン勉強会 with 株式会社Raise the Flag. | PwDA+クロス3',
pubDate: 'Thu, 02 Nov 2023 12:25:52 +0000',
'dc:creator': 'masayayagihashi',
content: '<p><もくじ> 見えないことは不便なだけで、不幸ではない 「もうこれなしの生活には戻れない」 | みずいろクリップがヒット商品に 「SYN+(シンプラス)」 | 視覚に代わる感覚を与える次世代型デバイス 障がいの有無 […]</p>\n' +
'<p>The post <a rel="nofollow" href="https://admin02.prod.blogs.cis.ibm.net/blogs/solutions/jp-ja/iot-pwda9/">ダイバーシティーハッカソ ン勉強会 with 株式会社Raise the Flag. | PwDA+クロス3</a> appeared first on <a rel="nofollow" href="https://admin02.prod.blogs.cis.ibm.net/blogs/solutions/jp-ja">IBM ソリューション ブログ</a>.</p>\n',
contentSnippet: '<もくじ> 見えないことは不便なだけで、不幸ではない 「もうこれなしの生活には戻れない」 | みずいろクリップがヒット商品 に 「SYN+(シンプラス)」 | 視覚に代わる感覚を与える次世代型デバイス 障がいの有無 […]\n' +
'The post ダイバーシティーハッカソン勉強会 with 株式会社Raise the Flag. | PwDA+クロス3 appeared first on IBM ソリューション ブログ.',
guid: 'https://admin02.prod.blogs.cis.ibm.net/blogs/solutions/jp-ja/?p=225250',
categories: [
'IBM Sustainability Software',
'ソサエティ5.0',
'ソーシャル・アジェンダ',
'社会課題解決'
],
isoDate: '2023-11-02T12:25:52.000Z'
}
}
5. Code EngineのFunctionsに登録する
前回の記事では、モジュール1つを指定して登録しましたが、今回はこのコードが置いてあるディレクトリ全体を登録したいと思います。
ただ、Code Engineに必要な情報だけ登録できれば良いので、Code Engineには不要だと宣言する .ceignore
ファイルを作成します。.gitignore
や.dockerignore
と同じような感じです。
node_modules
driver.js
.env
*.rest
node_modules
は言わずもがな、環境変数を定義する .env
も不要です。Code Engineでの環境変数の定義はこの後紹介します。
先ほど作成した driver.js
もテスト用なので不要です。
*.rest
は、Code EngineでFunctionsを作成した後にテスト用に作成するので、それをあらかじめ登録しています。想定外の動きでソースコード修正して、再度Functionsを登録する際に、誤ってテスト用のモジュールが登録されないようにするためです。
まず、今 .env
に定義している環境変数をCode EngineのSecretに登録しましょう。
IBM Cloud CLIで登録します。事前に以下を満たすようにしてください。
-
ibmcloud login
で事前にIBM Cloudにログインしている -
ibmcloud target -r [Code Engineのインスタンスが存在するリージョン] -g [Code Engineのインスタンスが紐づくリソースグループ]
の操作でターゲットを選んでいる -
ibmcloud ce project select --name [Code Engineのインスタンス名]
の操作で対象のCode Engineを操作可能にしている - ターミナルのカレントディレクトリをこれらのモジュールがあるディレクトリにしていること
ibmcloud ce secret create --name qiita-bearer-env -e .env
-
--name
: 作成するSecretの名前 -
-e
:.env
形式のファイルから生成する場合のオプション
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>ibmcloud ce secret create --name qiita-bearer-env -e .env
Creating generic secret 'qiita-bearer-env'...
OK
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>
作成されたSecretは以下のコマンドで確認できます
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>ibmcloud ce secret get --name qiita-bearer-env
シークレット「qiita-bearer-env」を取得しています...
OK
名前: qiita-bearer-env
ID: dd2a3c42-80f1-48b6-91f0-bf2c668f0373
フォーマット: generic
プロジェクト名: xxxxxxxxx
プロジェクト ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx
経過時間: 4m43s
作成日時: 2023-11-08 04:10:14 +0000 UTC
データ:
---
BEARER_VALUE: M3phd3g0c2U1ZHJmdjZ0N2I2bnVteHNFQ1JWdDdieThudTlpbU85b2k4VXVtWllUNWNSVmFUQnlqSXhyY3ZiSw==
表示される値はBase64でEncodeされています。コマンドに --decode
のオプションを入れるとデコードして表示してくれます。
では、このSecretを使うようにFunctionsを作成します。
ibmcloud ce fn create --name qiita-solution-blog --runtime nodejs-18 --cpu 0.25 --memory 1G --env-sec qiita-bearer-env --build-source .
-
--env-sec
: 事前に作成されたSecretを指定して環境に組み込む -
--build-source .
: カレントディレクトリにあるリソースのうち.ceignore
で指定されていないものを対象とする
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>ibmcloud ce fn create --name qiita-solution-blog --runtime nodejs-18 --cpu 0.25 --memory 1G --env-sec qiita-bearer-env --build-source .
ビルド・プッシュ用に関数「qiita-solution-blog」を準備しています ...
機能「qiita-solution-blog」を作成しています...
パス「.」からアップロードするファイルのパッケージ化...
ビルド実行 'qiita-solution-blog-run-231108-140127591'を送信中...
イメージ 'private.jp2.icr.io/ce--xxxxx-xxxxxxxxxx/function-qiita-solution-blog:231108-0501-gkq44' を作成しています ...
ビルド実行が完了するのを待機しています...
ビルド実行状況: '実行中'
ビルド実行が正常に完了しました。
ビルド実行の状況を確認するには、'ibmcloud ce buildrun get -n qiita-solution-blog-run-231108-140127591' を実行してください。
関数「qiita-solution-blog」が作動可能になるのを待っています ...
関数「qiita-solution-blog」は作動可能です。
OK
詳細を表示するには、'ibmcloud ce function get -n qiita-solution-blog' を実行してください。
https://qiita-solution-blog.xxxxxxxxxx.jp-osa.codeengine.appdomain.cloud
C:\node-apps\node-apps\code-engine\qiita-solution-blog-function>
公開できないものはマスキングしていますが、無事にFunctionを作成することが出来ました。
6. Rest Clientで動作検証する
test.rest
ファイルを作成して、中に以下のような感じで記述します。
@url=https://qiita-solution-blog.xxxxxxxxxx.jp-osa.codeengine.appdomain.cloud/
@token=3zawx4se5drfv6t7b6numxsECRVt7by8nu9imO9oi8UumZYT5cRVaTByjIxrcvbK
POST {{url}}
Authorization: Bearer {{token}}
Accept: application/json
Content-Type: application/json
POST
以下を選択してリクエストを送ってみましょう。
無事にRSSからランダムで1件、その情報をjson形式で取得できていますね!
例えば、Bearerで設定している値を違うものにすると Unauthorized
で応答されます。
というわけで無事に登録したFunctionをローカルPCから動作検証することが出来ました。
さいごに
いかがでしたでしょうか?Code EngineのFunctionsに任意のPackageを導入したアプリを公開することができるので、アプリの実装の幅が無限大に増えましたね。次の記事ではこのFunctionsを使って安価に使えるLINE Botの作り方でも公開できればと考えています。
また次回のCode Engineの記事をお楽しみに!