はじめに
社内のとある取り組みにおいて、AWS の Lambda から Salesforce のカスタムオブジェクトにデータを入れる仕組みが必要になったので、その仕組みを実装するための覚え書きです。
本稿では、Node.js、AWS、Salesforce を使用するので、前提としてこの辺りの知識が必要になります。
が、筆者は上記の知識ゼロだったので、なくてもなんとかなる。はず。(すばらしい記事を残してくれている方々に感謝
手順
- 作業環境・用意するもの
- Node.js で Salesforce へ接続
- AWS の準備
- Lambda関数の作成
- Lambda から Salesforce に接続
- Salesforce カスタムオブジェクトにレコードを追加する
作業環境・用意するもの
【作業環境】
- Windows7、Node.js 7.1.0(本稿作業時)
【用意するもの】
- AWS と Salesforce アカウント
- AWS S3、IAMロール、ポリシーなどの設定
- データを入れるための Salesfoce オブジェクト
- Salesforce 接続アプリケーション
Node.js で Salesforce へ接続
こちらの記事が大変参考に。
OAuth2 JWT Bearer Token フローを使ってSalesforceへアクセスする
必要に応じてモジュールをインストールする。上記の記事では下記のモジュールを使用する。
- fs
- jsonwebtoken
- request
- jsforce
AWS の準備
前準備として今回使用する IAM ロール、ポリシー、S3 バケットなどの作成、設定をしておく。
Lambda 関数の作成
AWS で Lambda 関数を作成する。
本稿では下記設定で作成した。
- 「Lambda」ページの 「Lambda 関数の作成」をクリックすると設計図の選択画面に遷移する。
- フィルターに「S3」と入れ、「s3-get-object」を選択。
- 「バケット」項目は 3 で作成したバケットを選択
- 「イベントタイプ」は「put」を選択
- プレフィックス、サフィックスは空欄で OK
- 「トリガーの有効化」にチェックを入れて「次へ」
関数の設定
関数の名前と、必要に応じて説明を入力。ランタイムは Node.js。
Lambda 関数のコード
とりあえずデフォルトのままで OK。
Lambda 関数ハンドラおよびロール
- ハンドラ:デフォルト( index.handler )で OK。
- ロール:「既存のロールを選択」で 3 で作成したロールを選択
ここまで入力して関数を作成する。
作成した関数をテストして動作を確認
- 事前に S3 になんかファイルを入れておく
- 「アクション」から「テストイベントの設定」を選択
- サンプルイベントテンプレートから「S3 Put」を選択。
- サンプルコード 14 行目辺りにある「"key": "HappyFace.jpg"」の値を先ほど入れておいたファイル名に
- サンプルコード 19 行目辺りにある「"name": "sourcebucket"」の値を準備したバケット名に変更
- 「保存してテスト」をクリックしてテスト。
Lambda から salesforce に接続
手順4で作成したLambda関数のコードをコピーし、手順2で作成したJSファイルと合体させる。
Lambda や Salesforce 接続に必要なモジュールなどは最初に読み込んでおいてOK。
const...
const...
exports.handler = (event, context, callback) => {
// 2 で作成の salesforce に接続するプログラムをここにぶち込む
};
とするだけ。
これで S3 にファイルがアップロードされた時、2 で作成した salesforce 接続処理が走る。
ここで合体した JS ファイル(仮に index.js)を関数パッケージとして Lambda にアップロードする。
- index.js
- pemファイル
- node_module
これらをまとめて ZIP にする。
Lambda 関数画面のコードタブ、コード エントリ タイプから「ZIP ファイルをアップロード」を選び、
作成した ZIP ファイルをアップロードする。
(pem ファイルもまとめてここでアップロードするため、JS 内で呼び出す PEM ファイルのパスに注意)
ZIP をアップロードしたら「テスト」をクリックし、ログを確認して 2 の動作ログが出ていれば OK。
Salesforce カスタムオブジェクトにレコードを追加する
Salesforce にカスタムオブジェクトを作成し、手順2で作成したコードの
conn.query('SELECT Id, Name FROM Account LIMIT 5').then(function(qr) {
console.log('Done:', qr.done);
console.log('Fetched Records:', qr.records);
});
の部分を
conn.sobject(" オブジェクト名 ").create({
" 項目名 ": 追加する値
});
とすることでカスタムオブジェクトにレコードを追加することができる。
最後に、ここまでの内容を元に、S3に保存した画像を Amazon Rekognition(DetectFaces)に送り、
解析結果を Salesforce のオブジェクトに保存する実装例をメモしておく。
'use strict';
const aws = require('aws-sdk');
const fs = require('fs');
const s3 = new aws.S3({apiVersion: '2006-03-01'});
const jwt = require('jsonwebtoken');
const request = require('request');
const jsforce = require('jsforce');
const rekognition = new aws.Rekognition();
const TOKEN_ENDPOINT_URL = 'https://login.salesforce.com/services/oauth2/token';
const ISSUER = '********'; // Salesforce 接続アプリのコンシューマ鍵(client_id)
const AUDIENCE = 'https://login.salesforce.com'; // 固定
const cert = fs.readFileSync('./key/myapp.pem'); // 秘密鍵の読み込み
exports.handler = (event, context, callback) =>{
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
const params = {
Bucket: bucket,
Key: key,
};
// rekognition に送るデータ
const rekogParams = {
Image: {
S3Object: {
Bucket: bucket,
Name: key
}
},
Attributes: ["ALL"]
};
rekognition.detectFaces(rekogParams, function (err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
} else {
// JWTに記載されるメッセージの内容
const claim = {
iss: ISSUER,
aud: AUDIENCE,
sub: '**********', // 接続するSalesforceのユーザアカウント名
exp: String(Date.now() + 3 * 60 * 1000) //現在時刻から3分間のみ有効
};
// JWTの生成と署名
var token = jwt.sign(claim, cert, {algorithm: 'RS256'});
// JWT Bearer Token フローによるアクセストークンのリクエスト
request({
method: 'POST',
url: TOKEN_ENDPOINT_URL,
form: {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: token
}
}, function (err, response, body) {
if (err) {
return console.error(err);
}
var ret = JSON.parse(body);
console.log(ret)
var conn = new jsforce.Connection({
accessToken: ret.access_token,
instanceUrl: ret.instance_url
});
// Salesforce オブジェクト「rekognitionData」に Rekognition の解析結果を追加
for (var i in data.FaceDetails) {
conn.sobject("rekognitionData__c").create({
"AgeRangeLow__c": data.FaceDetails[i].AgeRange.Low,
"AgeRangeHigh__c": data.FaceDetails[i].AgeRange.High,
"Gender__c": data.FaceDetails[i].Gender.Value,
"Smile__c": data.FaceDetails[i].Smile.Value,
"Eyeglasses__c": data.FaceDetails[i].Eyeglasses.Value,
"Sunglasses__c": data.FaceDetails[i].Sunglasses.Value,
"Beard__c": data.FaceDetails[i].Beard.Value,
"Mustache__c": data.FaceDetails[i].Mustache.Value,
"EyesOpen__c": data.FaceDetails[i].EyesOpen.Value,
"Emotions1__c": data.FaceDetails[i].Emotions[0].Type,
"Emotions2__c": data.FaceDetails[i].Emotions[1].Type,
"Emotions3__c": data.FaceDetails[i].Emotions[2].Type,
}, function (err, ret) {
if (err || !ret.success) {
return console.error(err, ret);
}
});
}
});
callback(null, data.FaceDetails);
}
});
}
;
上記コードとオブジェクトの項目が少し違いますが、こんな感じでレコードが追加されます。