LoginSignup
4
1

More than 1 year has passed since last update.

スマートデバイスとクラウドサービスとの連携を容易にするiPaaSの活用① ~AlexaからAPI呼び出しでDatabaseを参照してみた~

Last updated at Posted at 2022-09-05

はじめに

近年、スマートデバイスの種類や用途は広がってきており、身近なものになりつつあります。今後は、さらに活用のパターンは広がっていくと思われますが、その際にはデバイス間の連携や、クラウドサービスとの連携のニーズも高まっていくと考えられます。
様々なデバイスでデータ取得のためのAPIが提供されていたり、各種プログラミング言語ではサービス連携のためのライブラリーも提供されていると思います。しかし、この方法だけでは、将来的にシステム連携が増えていくと、各クラウドサービス用の連携開発や認証情報の管理など、効率性が損なわれてしまう恐れがあります。
このような課題に対応するために、近年、iPaaS(integration Platform as a Service)と呼ばれるサービスに注目が集まっています。クラウドサービス種別としてPaaSはよく知られていますが、その中でも特にintegration(システム連携)に特化したプラットフォームサービスとなります。このソリューションでは、様々なクラウドサービスと連携するためのアダプターが提供されており、システム連携の課題を解決することができます。

今回は、スマートデバイスとクラウドサービスとの連携をiPaaSを活用して実装した例をご紹介します。
実装のアーキテクチャーは以下のような概要となります。クラウド側はIBM Cloudの無料サービスで実現しています。

image.png

1. IBM Cloud 実装の流れ

1.1 Cloudant

CloudantはIBM Cloudの無料枠でインスタンスを作成しています。

image.png

Cloudantのデータベース内にはユーザーの健康状態(statusフィールド)が含まれています。

image.png

1.2 App Connect

IBMでは、iPaaSソリューションとしてApp Connect on Cloud機能を提供しています。
こちらもIBM Cloudの無料枠でインスタンスを作成しています。

image.png

インスタンス作成後は、コンソールにアクセスでき、フローを開発することができます。

image.png

今回は、1.1で作成したCloudantに接続するためのAPIを登録したいので、「Create flows for an API」を選択します。

<データモデルの登録>
APIの呼び出し、応答で使用するデータモデルを設定します。
今回は、Databaseにあるnameとstatusを設定しています。

image.png

<操作の登録>
APIの操作を登録します。
今回は、Databaseを参照しますので、Retrieve(GETメソッド)を登録します。

image.png

<フロー開発>
API操作に対して具体的な処理を登録していきます。
全体の流れは以下のようになります。APIが呼び出されれるとフローが開始し、

  1. 「IBM Cloudant」でCloudantからデータ取得
  2. 「JSON Parser」で取得したJSONデータをパース
  3. 「Response」でJSONデータを応答

image.png

以下、もう少し詳しく、各設定値を見ていきます。

1. 「IBM Cloudant」でCloudantからデータ取得

+ボタンで「IBM Cloudant」アプリケーションを登録します。
接続先として以下のように「ホスト」と「API Key」を設定して接続します。

image.png

一度、アカウント登録すると認証情報が保管され、次からは再利用することができます。
今回はCloudantのDocumentを取得したいので「Retrieve documents」を選択します。

image.png

無事に接続できるとDatabase情報が取得されます。
取得条件としてIDに設定している「name」を指定します。

image.png

2. 「JSON Parser」で取得したJSONデータをパース

この後の処理として、Toolboxの「JSON Parser」を登録します。
この機能のInputとして「IBM Cloudant」機能で取得した「Document data」を設定します。
これにより、IBM Cloudantから取得したローデータをフローで参照可能なJSON形式に変換することができます。

image.png

また、後続の処理でJSONの構造を参照できるように「Output Schema」を設定します。
今回は、CloudantのサンプルデータからJSON Schemaを生成しています。

image.png

3. 「Response」でJSONデータを応答

先ほどのOutput Schemaを参照して、応答データに「name」と「status」を設定していきます。

image.png

以上で設定は完了です。
実際にこのフローをテストしてみましょう。
テストの際は「Request」から具体的なデータを設定できます。
今回は要求のIDとして「yamada」を設定しています。次に操作名の隣にあるテストボタンを押下します。

image.png

テストに問題がなければ、Cloudantから情報が取得できていることが確認できます。

image.png

動作は問題ありませんので、これでAPIを開始します。

image.png

Runningになりましたら「Manage」画面のルート情報からAPIのエンドポイントを確認できます。
今回は、簡易的なテストのためこのAPIにセキュリティー設定はしていません。

image.png

2. Smart Device 実装の流れ

今回は、1で公開したAPIをAlexaのスキルから呼び出していきます。

2.1 Alexa Console

alexa developer consoleからスキルを作成します。

image.png

スキルを作成します。今回はカスタムモデル、Alexa-hosted(Node.js)を選択します。

image.png

image.png

スキルが作成されましたら、「コードエディタ」タブからAPIを呼び出す処理を実装します。
特にAlexaとのやり取りは設定せず、静的API呼び出しでyamadaさんの健康状態を取得しています。

image.png

index.js
const Alexa = require('ask-sdk-core');
const http = require('http');
const https = require('https');

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  async handle(handlerInput) {
 
       // CreateMessage関数からAPIを呼び出し結果を取得
        try {
            const response = await CreateMessage();
            const speakOutput = response;
            handlerInput.responseBuilder
                .speak(speakOutput)   
        } catch (error) {
            handlerInput.responseBuilder
                .speak("すみません、健康状態が取得できませんでした")
        }
        return handlerInput.responseBuilder
            .getResponse();
  }
};

const HelloIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'HelloIntent';
  },
  handle(handlerInput) { 
    const speechText = handlerInput;
    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard()
      .getResponse();
  },
};

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
    const speechText = '「ヘルプ」と話しかけてください。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .reprompt(speechText)
      .withSimpleCard()
      .getResponse();
  },
};

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {
    const speechText = 'さようなら。';

    return handlerInput.responseBuilder
      .speak(speechText)
      .withSimpleCard('こんにちは。', speechText)
      .getResponse();
  },
};

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
    console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);

    return handlerInput.responseBuilder.getResponse();
  },
};

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
    console.log(`Error handled: ${error.message}`);

    return handlerInput.responseBuilder
      .speak('すみません。理解できませんでした。もう一度お願いします。')
      .reprompt('すみません。理解できませんでした。もう一度お願いします。')
      .getResponse();
  },
};

const skillBuilder = Alexa.SkillBuilders.custom();

exports.handler = skillBuilder
  .addRequestHandlers(
    LaunchRequestHandler,
    HelloIntentHandler,
    HelpIntentHandler,
    CancelAndStopIntentHandler,
    SessionEndedRequestHandler
  )
  .addErrorHandlers(ErrorHandler)
  .lambda();



//CreateMessage関数
const CreateMessage = function() {
  
    // App Connect on cloudのAPIエンドポイント
    var url = "https://***.***.apigw.appdomain.cloud/xdBXrU/user_status/yamada" ;

    return new Promise((resolve, reject) => {
        const request = https.get(url, response => {
            response.setEncoding('utf8');

            let returnData = '';
            if (response.statusCode < 200 || response.statusCode >= 300) {
                return reject(new Error(`${response.statusCode}: ${response.req.getHeader('host')} ${response.req.path}`));
            }

            response.on('data', chunk => {
                returnData += chunk;
            });
            response.on('end', () => {

                let res = JSON.parse(returnData);
                let name = res.name;
                let status = res.status;
                let speakOutput = name + "さんの健康状態は" + status + "です";
                resolve(speakOutput);
            });
            response.on('error', error => {
                console.log(error);
                let errorSpeakOutput = "すみません、健康状態を取得できませんでした。";
                reject(errorSpeakOutput);
            });
        });
        request.end();
    });
};

実際にテストしてみると以下のようになります。

image.png

以上です。

4
1
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
1