LoginSignup
1
1

More than 5 years have passed since last update.

OpenWhisk シーケンスのソース

Last updated at Posted at 2017-01-01

OpenWhiskは、IBMが提供するサーバレスアーキテクチャーです。
IBM Bluemix、またはOSSとして利用できます。
「トリガー」「アクション」「ルール」「シーケンス」の4つから構成されます。
概要図は以下を参照ください。
https://github.com/openwhisk/openwhisk/blob/master/docs/about.md

↓の自動化でトリガー、
 シーケンスへのリンクでシーケンス、
 をアクションと関連付けできます。

secence action.png

1.アクション例

AWS Lambda , Azure Function , Google Function も近いアーキテクチャーですが、
Docker、Swift対応やWatson連携だけではなく、シーケンス設定の分かりやすさに
とソースが見えることだと思います。
そのソースコードを見てみましょう。

Node.js

function main(params) {
return { "message": "you sent me " + params.message };
}

Python

import sys

def main(dict):
if 'message' in dict:
name = dict['message']
else:
name = 'stranger'
greeting = 'Hello ' + name + '!'
print(greeting)
return {'greeting':greeting}

Swift

func main(args: [String:Any]) -> [String:Any] {
if let message = args["message"] as? String {
return [ "greeting" : "Hello (message)!" ]
} else {
return [ "greeting" : "Hello stranger!" ]
}
}

C inside Docker

Dockerの場合は、Dockerhub内に記載します。

イメージを使用して
が dockerhub
に接続されました: openwhisk/example

*request parameter
{
"arg": "3"
}

*response
{
"args": {
"arg": "3"
},
"msg": "Hello from arbitrary C program!"
}

2.シーケンス例

シーケンスはNode.jsで実装されます。

Websocket

Websocket send sample です。
ws.send(payload ・・・ 部分がメッセージ送信となります。

/**
* Sends a payload message to the designated WebSocket URI
*
* @param uri String representation of the WebSocket uri
* @param payload Message to send to the WebSocket
* @return Standard OpenWhisk success/error response
*/
function main(params) {
if (!params.uri) {
return whisk.error('You must specify a uri parameter.');
}
var uri = params.uri;
console.log("URI param is " + params.uri);

if (!params.payload) {
    return whisk.error('You must specify a payload parameter.');
}
var payload = params.payload;

console.log("Payload param is " + params.payload);

var WebSocket = require('ws');

var connectionEstablished = false;
var ws = new WebSocket(uri);

var connectionTimeout = 30 * 1000; // 30 seconds

var promise = new Promise(function(resolve, reject) {
    setTimeout(function () {
        if (!connectionEstablished) {
            reject('Did not establish websocket connection to ' + uri + ' in a timely manner.');
        }
    }, connectionTimeout);

    ws.on('open', function () {
        connectionEstablished = true;

        console.log("Sending payload: " + payload);
        ws.send(payload, function (error) {
            if (error) {
                console.log("Error received communicating with websocket: " + error);
                ws.close();
                reject(error);
            } else {
                console.log("Send was successful.");
                ws.close();
                resolve({
                    'payload': payload
                });
            }
        });
    });

    ws.on('error', function (error) {
        console.log("Error communicating with websocket: " + error);
        ws.close();
        reject(error);
    });
});

return promise;

}

Slack

Slackへの連携です。Slackの基本認証とメッセージが必須パラメータです。

var request = require('request');

/**
* Action to post to slack
* @param {string} url - Slack webhook url
* @param {string} channel - Slack channel to post the message to
* @param {string} username - name to post the message as
* @param {string} text - message to post
* @param {string} icon_emoji - (optional) emoji to use as the icon for the message
* @param {boolean} as_user - (optional) when the token belongs to a bot, whether to post as the bot itself
* @param {object} attachments - (optional) message attachments (see Slack documentation for format)
* @return {object} whisk async
*/
function main(params) {

if (checkParams(params)) {

var body = {
  channel: params.channel,
  username: params.username || 'Simple Message Bot',
  text: params.text
};

if (params.icon_emoji) {
  // guard against sending icon_emoji: undefined
  body.icon_emoji = params.icon_emoji;
}

if (params.token) {
  //
  // this allows us to support /api/chat.postMessage
  // e.g. users can pass params.url = https://slack.com/api/chat.postMessage
  //                 and params.token = <their auth token>
  //
  body.token = params.token;
} else {
  //
  // the webhook api expects a nested payload
  //
  // notice that we need to stringify; this is due to limitations
  // of the formData npm: it does not handle nested objects
  //
  console.log(body);
  console.log("to: " + params.url);

  body = {
    payload: JSON.stringify(body)
  };
}

if (params.as_user === true) {
    body.as_user = true;
}

if (params.attachments) {
  body.attachments = params.attachments;
}

var promise = new Promise(function (resolve, reject) {
  request.post({
    url: params.url,
    formData: body
  }, function (err, res, body) {
    if (err) {
      console.log('error: ', err, body);
      reject(err);
    } else {
      console.log('success: ', params.text, 'successfully sent');
      resolve();
    }
  });
});

return promise;

}
}

/**
Checks if all required params are set
*/
function checkParams(params) {
if (params.text === undefined) {
whisk.error('No text provided');
return false;
}
if (params.url === undefined) {
whisk.error('No Webhook URL provided');
return false;
}
if (params.channel === undefined) {
whisk.error('No channel provided');
return false;
}
return true;
}

Cloudant

ドキュメントNoSQLCloudantが変更された場合です。

/**
* Create and update attachment for document in Cloudant database:
* https://docs.cloudant.com/attachments.html#create-/-update
**/

function main(message) {
var cloudantOrError = getCloudantAccount(message);
if (typeof cloudantOrError !== 'object') {
return whisk.error('getCloudantAccount returned an unexpected object type.');
}
var cloudant = cloudantOrError;
var dbName = message.dbname;
var docId = message.docid;
var attName = message.attachmentname;
var att = message.attachment;
var contentType = message.contenttype;
var params = {};

if(!dbName) {
return whisk.error('dbname is required.');
}
if(!docId) {
return whisk.error('docid is required.');
}
if(!attName) {
return whisk.error('attachmentname is required.');
}
if(!att) {
return whisk.error('attachment is required.');
}
if(!contentType) {
return whisk.error('contenttype is required.');
}
//Add document revision to query if it exists
if(typeof message.docrev !== 'undefined') {
params.rev = message.docrev;
}
var cloudantDb = cloudant.use(dbName);

if (typeof message.params === 'object') {
params = message.params;
} else if (typeof message.params === 'string') {
try {
params = JSON.parse(message.params);
} catch (e) {
return whisk.error('params field cannot be parsed. Ensure it is valid JSON.');
}
}

return insert(cloudantDb, docId, attName, att, contentType, params);
}

/**
* Insert attachment for document in database.
*/
function insert(cloudantDb, docId, attName, att, contentType, params) {
return new Promise(function(resolve, reject) {
cloudantDb.attachment.insert(docId, attName, att, contentType, params, function(error, response) {
if (!error) {
console.log("success", response);
resolve(response);
} else {
console.log("error", error)
reject(error);
}
});
});
}

function getCloudantAccount(message) {
// full cloudant URL - Cloudant NPM package has issues creating valid URLs
// when the username contains dashes (common in Bluemix scenarios)
var cloudantUrl;

if (message.url) {
// use bluemix binding
cloudantUrl = message.url;
} else {
if (!message.host) {
whisk.error('cloudant account host is required.');
return;
}
if (!message.username) {
whisk.error('cloudant account username is required.');
return;
}
if (!message.password) {
whisk.error('cloudant account password is required.');
return;
}

cloudantUrl = "https://" + message.username + ":" + message.password + "@" + message.host;

}

return require('cloudant')({
url: cloudantUrl
});
}

Watson Text to speech

watson-developer-cloudを呼び出すため、認証情報、文字情報等を送ります。

var watson = require('watson-developer-cloud');

function isValidEncoding(encoding) {
return encoding === 'ascii' ||
encoding === 'utf8' ||
encoding === 'utf16le' ||
encoding === 'ucs2' ||
encoding === 'base64' ||
encoding === 'binary' ||
encoding === 'hex';
}

/**
* Synthesizes text to spoken audio.
* See https://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/text-to-speech/api/v1/
*
* @param voice The voice to be used for synthesis. Example: en-US_MichaelVoice
* @param accept The requested MIME type of the audio. Example: audio/wav
* @param payload The text to synthesized. Required.
* @param encoding The encoding of the speech binary data. Defaults to base64.
* @param username The Watson service username.
* @param password The Watson service password.
*
* @return {
* "payload": "",
* "encoding": "",
* "content_type": ""
* }
*/
function main(params) {
var voice = params.voice;
var accept = params.accept;
var payload = params.payload;
var encoding = isValidEncoding(params.encoding) ? params.encoding : 'base64';
var username = params.username;
var password = params.password;

console.log('params:', params);

var textToSpeech = watson.text_to_speech({
username: username,
password: password,
version: 'v1'
});

var promise = new Promise(function(resolve, reject) {
textToSpeech.synthesize({
voice: voice,
accept: accept,
text: payload,
}, function (err, res) {
if (err) {
reject(err);
} else {
resolve({
payload: res.toString(encoding),
encoding: encoding,
mimetype: accept
});
}
}, function (err) {
reject(err);
});
});

return promise;
}

Weather

Bluemix上の
Forcast 情報との連動です。

var request = require('request');

/**
* Get hourly weather forecast for a lat/long from the Weather API service.
*
* Must specify one of zipCode or latitude/longitude.
*
* @param username The Weather service API account username.
* @param username The Weather service API account password.
* @param latitude Latitude of coordinate to get forecast.
* @param longitude Longitude of coordinate to get forecast.
* @param zipCode ZIP code of desired forecast.
* @return The hourly forecast for the lat/long.
*/
function main(params) {
console.log('input params:', params);
var username = params.username;
var password = params.password;
var lat = params.latitude || '0';
var lon = params.longitude || '0';
var language = params.language || 'en-US';
var units = params.units || 'm';
var timePeriod = params.timePeriod || '10day';
var url = 'https://twcservice.mybluemix.net/api/weather/v1/geocode/' + lat + '/' + lon;
var qs = {language: language, units: units};

switch(timePeriod) {
    case '48hour':
        url += '/forecast/hourly/48hour.json';
        break;
    case 'current':
        url += '/observations.json';
        break;
    case 'timeseries':
        url += '/observations/timeseries.json';
        qs.hours = '23';
        break;
    case '10day':
    default:
        url += '/forecast/daily/10day.json';
        break;
}

console.log('url:', url);

var promise = new Promise(function(resolve, reject) {
    request({
        url: url,
        qs: qs,
        auth: {username: username, password: password},
        timeout: 30000
    }, function (error, response, body) {
        if (!error && response.statusCode === 200) {
            var j = JSON.parse(body);
            resolve(j);
        } else {
            console.log('error getting forecast');
            console.log('http status code:', (response || {}).statusCode);
            console.log('error:', error);
            console.log('body:', body);
            reject({
                error: error,
                response: response,
                body: body
            });
        }
    });
});

return promise;

}

シーケンス関連付

以下は、CloudandのDocumentDBに変更があった場合にアクションが起動する例です。

relation.png

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