Mac で、Azure Functions をいじろうと思って、C# もかけるけど、まずは、node + Azure Functions CLI の組み合わせで、書いてみようと思った。簡単な、 CosmosDB に接続するサンプル。
噂の Callback hell
一番簡単そうな、CosmosDB の node からのチュートリアルをやってみた。
コードが見当たらなかったので、自分で書いたのを GitHub に上げておいた。
ちなみに、こんな感じのが3ファイルぐらい必要だった。単なる ToDo に、、、orz
taskDao.js
var DocumentDBClient = require('documentdb').DocumentClient;
var docdbUtils = require('./docdbUtils');
function TaskDao(documentDBClient, databaseId, collectionId) {
this.client = documentDBClient;
this.databaseId = databaseId;
this.collectionId = collectionId;
this.database = null;
this.collection = null;
}
TaskDao.prototype = {
init: function (callback) {
var self = this;
docdbUtils.getOrCreateDatabase(self.client, self.databaseId, function(err, db) {
if (err) {
callback(err);
} else {
self.database = db;
docdbUtils.getOrCreateCollection(self.client, self.database._self, self.collectionId, function (err, coll) {
if (err) {
callback(err);
} else {
self.collection = coll;
}
});
}
});
},
find: function (querySpec, callback) {
var self = this;
self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
callback(null, results);
}
});
},
addItem: function (item, callback) {
var self = this;
item.date = Date.now();
item.completed = false;
self.client.createDocument(self.collection._self, item, function (err, results) {
if (err) {
callback(err);
} else {
callback(null, results);
}
});
},
updateItem: function (itemId, callback) {
var self = this;
self.getItem(itemId, function (err, doc) {
if (err) {
callback(err);
} else {
doc.completed = true;
self.client.replaceDocument(doc._self, doc, function(err, replaced) {
if (err) {
callback(err);
} else {
callback(null, replaced);
}
});
}
});
},
getItem: function (itemId, callback) {
var self = this;
var querySpec = {
query: 'SELECT * FROM root r WHERE r.id = @id',
parameters: [{
name: '@id',
value: itemId
}]
};
self.client.queryDocuments(self.collection._self, querySpec).toArray(function (err, results) {
if (err) {
callback(err);
} else {
callback(null, results[0])
}
});
}
};
module.exports = TaskDao;
私の感想は、、、これが、コールバックヘルと言うやつか、、、こんなしょーもないことするのに、こんなコードが要るとは、、、辛すぎる。だった。そうだ、TypeScript だったらどうだろう。楽じゃないかな?
ライブラリを調べてみるとあった!
こ、これだ、これしかない! これを使って、Functions を書いてみよう。
Azure Functions を TypeScript 対応にしてみる
とりあえず、HttpTrigger を TypeScript に変えてみる。ちなみに、Azure Functions の TypeScript は、現在 Preview になって要るが、Azure Functions CLI の、2.0はまだ対応していない。自力だ。
Function App を作成
$ func init
Writing .gitignore
Writing host.json
Writing local.settings.json
Created launch.json
Initialized empty Git repository in /Users/ushio/Codes/AzureFunctions/some/.git/
Functions を作成
$ func new
Select a language:
1. C#
2. JavaScript
Choose option: 2
JavaScript
Select a template:
1. BlobTrigger
2. HttpTrigger
3. QueueTrigger
4. TimerTrigger
Choose option: 2
HttpTrigger
Function name: [HttpTriggerJS]
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/index.js
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/sample.dat
Writing /Users/ushio/Codes/AzureFunctions/some/HttpTriggerJS/function.json
テンプレートが生成された
TypeScript 環境を作る
cd HttpTriggerJS
npm init
tsc --init
cp index.js index.ts
index.ts
を次のように変える
index.ts
export async function run (context: any, req: any) {
typeconfig.json
を次のように変える。これをしないと、async メソッドは、promise を返さないとエラーになる。ところが、最初の1つ目のfunction では返しても仕方がないケースがあるので、新しいバージョンの TypeScript ではエラーにならなくなっている。
{
"compilerOptions": {
/* Basic Options */
"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
"lib": [ "es2015"],
can't find process
error
もしかすると、こんなエラーに出会うかもしれない。
index.ts(7,34): error TS2304: Cannot find name 'process'.
index.ts(7,65): error TS2304: Cannot find name 'process'.
このケースは、これで解消する。そもそも process
見つからんとかおかしいよね。これはバグ。
npm install --save-dev @types/node
下記の議論をみてみると詳細が書いてありJます。
下記のエラーは先ほどのtypeconfig.json
の設定で解消されます。
error TS2705: An async function or method in ES5/ES3 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your `--lib` option.
ついに Azure Functions
ここまで設定すると、無事、CosmosDB にアクセスするファンクションが動きました。簡単!
import * as DB from "documentdb-typescript";
export async function run (context: any, req: any) {
context.log('JavaScript HTTP trigger function processed a request.');
const client = new DB.Client(process.env["COSMOS_DB_HOST"], process.env["COSMOS_DB_KEY"]);
client.enableConsoleLog = true;
await client.openAsync();
console.log(await client.getAccountInfoAsync());
var dbs = await client.listDatabasesAsync();
if (req.query.name || (req.body && req.body.name)) {
context.res = {
// status: 200, /* Defaults to 200 */
body: "Hello! " + (req.query.name || req.body.name) + dbs.map(db => db.id)
};
}
else {
context.res = {
status: 400,
body: "Please pass a name on the query string or in the request body"
};
}
context.done();
};
ちなみに、上記のは、documentdb-typescript
のサンプルそのままだが、最初は、サンプルのままだと、エラーだった。Issue を書いて、解決したので、プルリクエストを書いておいた。
TypeError: Cannot read property 'getDatabaseAccount' of undefined
次のコミットが参考になります。 commit.
ToDo
残念ながら、TypeScript のローカルデバッグはできません。(生成されたjsはできると思うけど)。どうやったらできるか考えてみます。
Resource
実は、このサンプルを GitHub においています。