Azure Cosmos DB の REST API を叩く

# 目的
Cosmos DB サーバーへのHTTPリクエストの方法を整理。

- HTTPリクエストで基本的な CRUD 操作が可能。
Cosmos DB 中のユーザーやアクセス許可を容易に確認できるのが利点。
その際アクセストークンを自前で生成し、HTTPリクエストのヘッダーに使用する必要がある。

- ここではトークンの生成に Node.js を使用。

# 準備物  
- [Node.js] (https://nodejs.org/ja/)
- Cosmos DB サーバーにHTTPリクエストを飛ばせるアプリ
    - ここでは Postman に頑張ってもらった

# 手順の概略
1. HTTPリクエストの内容決め
2. スクリプトでのトークン生成
3. HTTPリクエストぶん投げ

## 1. HTTPリクエストの内容決め
そもそもの操作によって、トークン生成スクリプトのパラメータに渡す引数が変わる。

- どのリソースに（= データベース, コレクション, ドキュメント, ユーザー 等）
- 何を（= ユーザー, アクセス許可, ドキュメント 等）
- どうする（= CRUD操作）

ここではデータベースに存在するユーザーの一覧を取得する。
= **特定のデータベース** に対して **全ユーザー** を **GET**

## 2.トークン生成

以下のようなスクリプトによりトークンを生成する。

```javascript:token.js
var crypto = require("crypto");

// Cosmos DB アカウントのプライマリ/セカンダリキー
var inputKey = "{アカウントキー}";

// RFC 1123 の日付形式で現在時刻を取得
var today = new Date().toUTCString();

// x-ms-date ヘッダとして使用
console.log(today);

// HTTP リクエストに使用するトークンを生成

// authorization ヘッダとして使用
console.log(getAuthorizationTokenUsingMasterKey("GET", "users", "dbs/{データベース名}", today, inputKey))

function getAuthorizationTokenUsingMasterKey(method, resourceType, resourceId, date, masterKey)
{
    var key = new Buffer.from(masterKey, "base64");

    var text = method.toLowerCase() + "\n" +
               resourceType.toLowerCase() + "\n" +
               resourceId + "\n" +
               date.toLowerCase() + "\n" +
               "" + "\n";

    var body = new Buffer.from(text, "utf8");

    var signature = crypto.createHmac("sha256", key).update(body).digest("base64");

    var MasterToken = "master";

    var TokenVersion = "1.0";

    return encodeURIComponent("type=" + MasterToken + "&ver=" + TokenVersion + "&sig=" + signature);
}
```

- https://docs.microsoft.com/en-us/rest/api/cosmos-db/access-control-on-cosmosdb-resources#constructing-the-hashed-token-signature-for-a-master-token

**inputKey** で使えるアカウントキーは以下の通り。

| メソッド | アカウントキーの種類 |
| :-- | :-- |
| GET (dbs, colls, docs etc.)| 読み取り/書き込みキー or 読み取り専用キー |
| **GET (users, permissions)** | **読み取り/書き込みキー** |
| POST, PUT, DELETE etc. | 読み取り/書き込みキー |

- users, permissions は GET でも **読み取り/書き込みキー** を使う必要がある点に注意。

---
**スクリプトの引数は実行するHTTPリクエストによって異なる。**


例1: データベース下にユーザーを作成

| パラメータ | 引数 |
| :--- | :--- |
| method | POST |
| resourceType | users |
| resourceId | dbs/{データベース名} |

- https://docs.microsoft.com/en-us/rest/api/cosmos-db/create-a-user

---
例2: データベース下に作成されたユーザーの一覧を取得

| パラメータ | 引数 |
| :--- | :--- |
| method | GET |
| resourceType | users |
| resourceId | dbs/{データベース名} |

- https://docs.microsoft.com/en-us/rest/api/cosmos-db/list-users

---
例3: データベース下に作成されたユーザーにアクセス許可を付与

| パラメータ | 引数 |
| :--- | :--- |
| method | POST |
| resourceType | permissions |
| resourceId | dbs/{データベース名}/users/{ユーザー名} |

- https://docs.microsoft.com/en-us/rest/api/cosmos-db/create-a-permission

---

Node.js 上でスクリプトを実行し、生成時刻とトークンを取得。

![CosmosDB_token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701198/b829041f-e5d7-11f2-df9b-c92cddfb8922.png)

- 1行目: 生成日時（ヘッダーで使う）
- 2行目: トークン（ヘッダーで使う）

## 3. HTTPリクエストぶん投げ
Postman で HTTPリクエストを実行。

メソッド: **GET**
URL: https://{Cosmos DB アカウント名}.documents.azure.com/dbs/{データベース名}/users

![CosmosDB_REST1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701198/2094e26e-5154-eba6-c48c-6143cd1be3c2.png)

設定するヘッダーは以下の通り。

| キー | 値 |
| :--- | :--- |
| x-ms-version | APIのバージョン（**2018-12-31** でOK）|
| x-ms-date | トークンの生成日時 |
| authorization | 生成したトークン |

- **x-ms-version** で指定できる REST API のバージョンは以下で確認可能
    - https://docs.microsoft.com/en-us/rest/api/cosmos-db/#supported-rest-api-versions

---

# HTTPレスポンス
ユーザーの情報が JSON で返ってくる。

- HTTPステータス: 200 Ok であれば成功

![CosmosDB_REST2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/701198/8a303161-3766-7840-cdac-ee02fff45282.png)

---

### ＊POST, PUT する場合は Body を追加する。

- （例）データベース下に作成されたユーザーにアクセス許可を付与する場合
    - アクセス許可の例: 特定のドキュメントに対する読み取り権限

メソッド: POST
URL: https://{Cosmos DB アカウント名}.documents.azure.com/dbs/{データベース名}/users/{ユーザー名}/permissions

``` body.json
{  
    "id": "アクセス許可の名前",  
    "permissionMode": "Read か All",  
    "resource": "dbs/{データベース名}/colls/{コレクション名}/docs/{ドキュメントのID}"  
}
```