LoginSignup
4
3

Code EngineのFunctionsでPackageを導入する

Last updated at Posted at 2023-11-08

はじめに

前回の記事では、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で応答する

開発手順

対象のコードを開発する手順自体は普通です

  1. npm initで開発環境のディレクトリに package.jsonを生成する
  2. npm install xxx で必要なパッケージモジュールを導入する
    今回は dotenvrss-parserをインストールしています
  3. main.jsを作成し、必要な機能を実装する
  4. テスト呼び出し用の driver.jsを作成し動作検証する
  5. Code EngineのFunctionsに登録する
  6. Rest Clientで動作検証する

1.npm initで開発環境のディレクトリに package.jsonを生成する

npm initで、(index.js)? と聞かれる部分だけ main.jsを指定しています。いろいろ指定したい方は自由に設定ください。
もうちょっと書くべきところを書いても良いのですが、今回は簡易に進めたいと思います。
こんな感じのpackage.jsonになっていれば大丈夫です。

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の情報を取得したいため、 dotenvrss-parserをインストールしています。

  • npm install dotenv
  • npm install rss-parser
    image.png

3. main.jsを作成し、必要な機能を実装する

image.png

やりたいことだけであれば、末尾10行ちょっとくらいで良いのですが、インターネット上にこのサービスを公開する場合に、URL知っていると誰でも呼び出されると困る、ということもあると思います。なので、簡易に Bearer の認証を入れるようにしています。
環境変数として定義された BEARER_VALUE の値と同じものがリクエストヘッダーの Authorization に設定されていないと弾く仕組みにしています。あと、 POST でリクエストされていない場合も弾いています。機能内容的には GET で良いと思いますが一応。

main.js
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は今回キーボードを適当にグシャグシャして設定しました。他の人に類推できなければ何でも良いと思います。

.env
BEARER_VALUE=3zawx4se5drfv6t7b6numxsECRVt7by8nu9imO9oi8UumZYT5cRVaTByjIxrcvbK

4. テスト呼び出し用の driver.jsを作成し動作検証する

さて、Code EngineのFunctionsに登録するためのモジュールは main()を外から実行するようにコーディングする関係で、このコード単体では動作しません。
なので、このコードを呼び出すための driver.jsを作成します。

呼び出す際のパラメータに以下を設定しています

  • __ce_method : Code EngineのFunctionsにどのメソッドで呼び出されたか識別するもの
  • __ce_headers : Code EngineのFunctionsが呼び出された際のヘッダーを識別するもの
driver.js
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><もくじ>  見えないことは不便なだけで、不幸ではない  「もうこれなしの生活には戻れない」 &#124; みずいろクリップがヒット商品に  「SYN+(シンプラス)」 &#124; 視覚に代わる感覚を与える次世代型デバイス  障がいの有無 [&#8230;]</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と同じような感じです。

.ceignore
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ファイルを作成して、中に以下のような感じで記述します。

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形式で取得できていますね!
image.png

例えば、Bearerで設定している値を違うものにすると Unauthorizedで応答されます。
image.png

というわけで無事に登録したFunctionをローカルPCから動作検証することが出来ました。

さいごに

いかがでしたでしょうか?Code EngineのFunctionsに任意のPackageを導入したアプリを公開することができるので、アプリの実装の幅が無限大に増えましたね。次の記事ではこのFunctionsを使って安価に使えるLINE Botの作り方でも公開できればと考えています。

また次回のCode Engineの記事をお楽しみに!

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3