Getting Started

更新 2020/06/24

Piece の動作部分 (Lambda編)

どうも、こんにちは!JellyWareです。

Getting Started!! Riiiver tutorialの第12弾となります。

このシリーズは、Riiiverやプログラミングを学び、初心者の方でもEco-Drive Riiiverを活用したオリジナルアプリを理解して作れるようになることを目指します。

今回は、Piece の構成要素の1つ「PieceCore」について説明します。

また、Riiiver が用意している Lambda を利用する場合について絞って解説をします。


目次


PieceCore とは

PieceJSON が Piece の仕様書であるのに対して、PieceCore は Piece の 本体と言え、どのような動作を行うのかを決定します。

具体的には、プログラミング言語を使用して記述された動作部を「PieceCore」と呼び、Riiiver が用意したモノとユーザーが作成するモノの2種類があります。

PieceCore はスマートフォンアプリ内に作成する必要があるため、アプリの開発者しか作れません。しかし、アプリの開発をしなくてもオリジナルな Piece を作成できる仕組みがRiiiverには用意されています。

Lambda を使用する場合の PieceCore

ここで、登場するのが AWS の Lambda となります。

「動作部である PieceCore は、スマートフォンアプリ内に作成する必要がある」と先ほど説明しましたが、Lambda にソースコードをアップロードすることで PieceCore のように機能させることができます。

流れとしては、
「Piece の実行」 → 「PieceCore の実行」 → 「Lambda 上のソースコードの実行」 となります。

では、この時に使用される PieceCore は何になるのでしょうか?
答えはRBCCommonWebServiceExecutorで、Riiiverが用意する PieceCore です。

以降、「Lambda 上のソースコード」を「PieceFunc」と便宜上呼びます。(Func は、Functionの省略です。)

簡単に図にすると、こういった流れになります。

つまり、皆さんは「PieceFunc」を作成することで Piece を作成することができます。

※Lambda を使用する場合、作成できるのは Service / Action Piece のみです。

Lambda を使用する場合の PieceJSON

Lambda を使用する場合、PieceJSON のフォーマットが一部固定となります。

前回説明していますが、以下のkeyについて改めて説明します。

  • deviceId
  • vendorId
  • executor
  • serviceProxy
"deviceId": "none"

対応するデバイスを指定する必要がない場合、"none"を指定できます。

"vendorId": "none"

"none"を指定します。

"executor": "RBCCommonWebServiceExecutor"

"RBCCommonWebServiceExecutor"を指定することで、Lambda上の Piece Func を実行できるようになります。

"serviceProxy" : {
  "service": "serviceSample"
}

"serviceProxy"内の"service"に、Lambda 上の PieceFunc となるファイル名を指定します。(拡張子は不要です。)

例えば、serviceSample.jsと言うファイルを実行する場合は、上記のように"serviceSample"と指定します。


PieceFunc

それでは、Lambda にアップロードする PieceFunc について解説していきます。

サンプル

以下、サンプルの PieceFunc です。(一部割愛しています。)

'use strict';
exports.handler = async (event) => {
  // 受け取った情報をログで出力しておく
  console.log('start: event = ' + JSON.stringify(event));
 
  const apiHost = "https://api.openweathermap.org";
  const apiPath = "/data/2.5/weather";
  const apikey = "98d2xxxxxxxxxxxxxxxxxxxxxxxxxc";    // ←ここに皆さんのAPI Keyを入れる
  let response = {
      status: 200,
      body: {
          celsiusTemperature: -1.0
      }
  };
 
  const cityName = event.properties.preferences.cityName;     // 自分のpieceのプリファレンスの値
  const urlString = `${apiHost}${apiPath}?q=${cityName},jp&APPID=${apikey}`;  //URLの文字列を完成させる。
  console.log(urlString);
 
  // 天気情報を取得
  let weatherData = await getWeatherInfo(urlString);
 
  // 天気情報から今日の情報を取得
  if (weatherData) {
      const tempK = weatherData.main.temp;           // REST APIから得られたJSON内の温度を保存。単位はケルビン。
      response.body.celsiusTemperature = tempK - 273.15;  // 次のブロックに返す値は摂氏で数値(Number)
  }
 
  // output情報をログで出力しておく
  console.log(JSON.stringify(response));
 
  // 結果を返却
  return response;
};

基本的には、今まで習った JavaScript の文法に従ってコードを記述すれば問題ありませんが、いくつか決まり事があります。

フォーマット

Lambda と Riiiver の仕様で、PieceFunc にもフォーマットがいくつかあります。
PieceJSON 同様、最低限必要となるコードを使用して解説します。

exports.handler = async (event) => {
  const response = {
      status: 200,
      body: {
          outputData: "成功"
      }
  };
 
  // 処理
 
  // 結果を返す
  return response;
};

上記が最低限記述したコードです。2つに分けて解説します。

exports.handler = async (event) => {
  // 処理
};

Piece が実行されると、このアロー関数が自動的に実行されます。

また、引数のeventは以下のようなJSONデータです。

{
    "serviceProxy": "samplePieceCode",
    "properties": {
        "preferences": {
            "prefrenceData": "samplePrefrenceData"
        },
        "input": {
            "inputData": "sampleInputData"
        },
        "parameters": {}
    }
}

"serviceProxy"は、使用する Lambda 上のコード名がvalueとなります。(.jsなどの拡張子は省かれます。)

"properties"は、以下の3つのデータがvalueとなります。

  • "preferences":アプリからユーザーが入力したデータ
  • "input":前の Piece から受け取るデータ
  • "parameters":端末の情報(位置情報など)

("parameters"に関しては、別途解説するため空にしています。気になる方はこちらを参照してください。)

また、"preferences""input"は、PieceJSON の"preferences""input"とそれぞれ紐づいています。

"preferences": {
  "type": "object",
  "properties": {
    "prefrenceData": {
      ~
    }
  }
}
"input": {
  "type": "object",
  "properties": {
    "inputData": {
      ~
    }
  }
}

今回の例で言うと、それぞれの"properties"内にある"prefrenceData" "inputData"が紐づいている箇所になります。(このkey名は、みなさんで自由につけられます。)

PieceJSON で指定したkey名がevent変数内のJSONデータで使用されることになります。

次に残りを解説します。

const response = {
  status: 200,
    body: {
    outputData: "成功"
    }
};
 
// 処理
 
// 結果を返す
return response;

Riiiver の仕様で、戻り値のフォーマットが固定となっています。

{
  status: 200,
  body: {
    outputData: "成功"
  }
}

型はオブジェクトで、statusbodyの2つのkeyが必要です。

status: 200

Piece が正しく動作したことの目印として、status200を指定します。(HTTPステータスコードと呼ばれるものです。)

例えば、try…catch文を利用して Piece が正しく動作しなかった場合には、status500等のエラーコードを指定することでRiiiverにエラー内容を伝えることができます。

body: {
  outputData: "成功"
}

bodyには、次の Piece に渡すデータを設定します。型はオブジェクトです。

この例だと、outputDataに指定している"成功"が次の Piece に渡されるデータです。

また、PieceJSON の"output"と紐づいています。

"output": {
  "type": "object",
  "properties": {
    "outputData": {
      "type": "string"
    }
  }
}

今回の例で言うと、"properties"内にある"outputData"が紐づいている箇所になります。(このkey名は、みなさんで自由につけられます。)

PieceJSON で指定したkey名を変数名として使用する必要があります。

※以下、2020年6月24日に追記

if (event.call === 'lambda') {
  if (requestPromise !== null) { }
  return;
}

Lambdaの動作を素早くするために、上記のようにウォームアップコードを追記する必要があります。詳細については、Riiiverのチュートリアルをご参照ください。

if (event.call === 'lambda') {
  if (xxxxxx !== null) { }
  return;
}

基本コードが上記となり、xxxxxxの箇所を読み込んだモジュールに変更するだけとなります。例えば、const requestPromise = require("request-promise");と読み込んでいるのでrequestPromiseに差し替えれば問題ありません。

複数のモジュールを扱う場合はif (xxxxxx !== null) { }を追加しましょう。

作成例

例えば、ユーザーがアプリから入力した「太郎」、前の Piece から「おはようございます」を受け取り、次の Piece に「おはようございます、太郎さん」を渡す PieceFunc を考えます。

以下の JavaScript のコードを埋める形で、PieceFunc を作成します。

exports.handler = async (event) => {
  // 処理
};

まずは、event変数のデータがどのようになるか確認しましょう。
(今回は、PieceJSON は考えないため一部のkey,valueは仮となります。)

{
    "serviceProxy": "serviceSample",
    "properties": {
        "preferences": {
            "name": "太郎"
        },
        "parameters": {},
        "input": {
            "greet": "おはようございます"
        }
    }
}

ユーザーがアプリから入力したデータは"preferences"、前の Piece から受け取ったデータは"input"に、それぞれ渡されます。
("name","greet"は、PieceJSON で指定したkey名となります。)

次に、PieceFunc の方でそれぞれのデータを抜き出しましょう。

exports.handler = async (event) => {
  const name = event["properties"]["preferences"]["name"]; // 追加
  
  const greet = event["properties"]["input"]["greet"]; // 追加
};

eventは、JSONデータのため、keyを指定してvalueを抜き出すことができます。

次にこの抜き出したデータを使って、次の Piece に渡しましょう。

exports.handler = async (event) => {
  const name = event["properties"]["preferences"]["name"];
  
  const greet = event["properties"]["input"]["greet"];
  
  const nameAndGreet = `${greet}${name}さん`; // 追加
};

文字連結を行い、戻り値となるresponseの形式を Riiiver用に整えています。

exports.handler = async (event) => {
  const name = event["properties"]["preferences"]["name"];
  
  const greet = event["properties"]["input"]["greet"];
  
  const nameAndGreet = `${greet}${name}さん`;
  
  const response = { // 追加
      status: 200,
      body: {
          output: nameAndGreet
      }
  }
};

今回は、シンプルに成功のみにしています。そのため、statusには200を指定します。

outputは、PieceJSON のoutputで定義したkey名と合わせる必要があるので注意してください。

exports.handler = async (event) => {
  const name = event["properties"]["preferences"]["name"];
  
  const greet = event["properties"]["input"]["greet"];
  
  const nameAndGreet = `${greet}${name}さん`;
  
  const response = {
      status: 200,
      body: {
          output: nameAndGreet
      }
  }
  
  return response; // 追加
};

最後にreturnで、次の Piece に渡すデータを返しています。

これで完成です。コツとしては、以下の3つになるかと思います。

  • event内のJSONデータを確認する
  • eventから必要なデータを抜き出す
  • 次の Piece に渡すデータを作成する

特にevent内のJSONデータについては、Riiiver の仕様で決定されるものです。形式などを忘れた際には、改めて本コンテンツか公式のドキュメントをご確認ください。


Lambda を使用する場合の Piece Core について理解できたでしょうか?

  • RBCCommonWebServiceExecutorの PieceCore を指定すること

  • PieceFunc を作成すること

Piece を作る上で重要なことは、この2点です。

次回は、今まで学んだことを活用して1つの Piece を作成します。

ご覧くださりありがとうございました。