エッジコンピューティングを実現するCouchbase Mobile紹介：Sync Gatewayによるデータ同期におけるルーティングの柔軟性（Sync関数概要）

## はじめに

Couchbase Mobileは、エッジコンピューティングを実現するデータプラットフォームであり、AndroidやiPhone(Swift, Objective-C)のようなスマートフォンやエッジデバイス用の組込データベースであるCouchbase Liteと、Couchbase Server、そしてそれらの間のデータの同期を実現する、Sync Gatewayから構成されます。

[エッジコンピューティングを実現するCouchbase Mobile紹介：Sync Gatewayによるデータ同期におけるアクセス制御モデル](https://qiita.com/yoshiyuki_kono/items/b9eb6ddae93fc98f4f7b)にも書いた通り、Sync Gatewayでは、ユーザーとロールとチャネルによって、データ（ドキュメント）のルーティングを構成します。

ここで紹介するSync関数(Sync Function)を定義することによって、アプリケーションは、データルーティングとアクセス制御を実装することができます。

[Couchbase Mobileアプリケーション開発へのロードマップ](https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417)もご覧ください。

## Sync関数概要

Sync関数は、ドキュメントに対して新しいリビジョン/更新が行われるたびに呼び出されます。

Sync関数は、データルーティングとアクセス制御のため、以下のような処理を管理することができます。

- ドキュメントの検証
- 変更の承認
- チャネルへのユーザーアクセスの許可
- チャネルへのドキュメントの割り当て

また、アクセス制御の粒度として、

- ドキュメントレベルの読み取り許可

のみではなく、

- フィールド レベルの書き込み許可

を実現することができます。



## Sync関数機能の基本

### ヘルパー関数

Sync Gatewayは、Sync関数の中で利用することのできる、アクセスを制御するための多数のヘルパー関数を提供しています（詳しくは、Sync Function APIリファレンスを参照してください）。

たとえば、ユーザーにチャネルへのアクセスを許可するには`access（）`ヘルパー関数を使用します。

この`access()`関数は、ロールを操作することもできます。ユーザー名の文字列が`role：`で始まる場合、文字列の残りの部分は役割名として解釈されます(「：」はユーザー名またはロール名の不正な文字であるため、ここにあいまいさはありません)。

また、匿名リクエストはユーザー「GUEST」として認証されるため、`GUEST`というユーザー名で`access()`を呼び出すことにより、チャネルとそのドキュメントを公開できます。

### 削除ドキュメント

削除されたドキュメントのチェックを含め、検証時にこれらを異なる方法で処理する必要がある可能性があります。削除は、`_deleted：true`プロパティを持つ単なるリビジョンです。このリビジョンには、他のプロパティは存在しません。
プロパティがないために検証チェックが失敗する可能性があるため、`doc._deleted == true`のチェックを組み込みます。

## Sync関数利用の基本


```JAVASCRIPT
function (doc, oldDoc, meta) { 
   channel(doc.channels); 

}
```

Sync Gateway 3.0ベータから、XATTR（拡張属性）の内容を使用してアクセス制御を駆動できるようになりました。これをサポートするために、追加のオプションの引数`meta`が追加されています。

### 引数
Sync関数のそれぞれの引数について解説します。


#### doc

このオブジェクトは、保存されているドキュメントのコンテンツを参照します。これは、Couchbase Liteアプリケーションによって保存され、Sync Gatewayに複製されたJSONと一致します。

ドキュメントの`_id`プロパティにはドキュメントIDが含まれていますドキュメントの`_rev`プロパティは新しいリビジョンIDです。ドキュメントが削除される場合、値が`true`の`_deleted`プロパティがあります。

#### oldDoc

ドキュメントが以前に保存されている場合、このオブジェクトは置き換えられるリビジョンを参照します。それ以外の場合はnullです。

同期関数の実装では、`oldDoc`パラメーターが必要ない場合は省略できます（Java Scriptは、関数に渡された余分なパラメーターを無視します）。

#### meta

この引数は、アクセス許可データを保持するために使用しているユーザー定義のXATTRを参照します。

参照されるオブジェクトには、チャネルやロールなどのアイテムを含めることができます。したがって、チャネル情報をドキュメント本文に直接埋め込む代わりに、ユーザーはドキュメントに関連付けられているこのユーザー定義のXATTRを指定できます。

## 構成方法
Sync関数を定義しない場合、SyncGatewayはデフォルトのSync関数を使用します。


ここでは、REST　APIを使用してSync関数をプロビジョニングします。

この例ではcurlコマンドを使用します（他の方法を使用することも可能です）。

```BASH
curl --location --request PUT 'http://localhost:4985/getting-started-db/_config' \
--header 'accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
    "sync": ` /* sync function code */ `  
        }'
```

Sync関数はバッククォートで囲まれていることに注意してください。

## 定義例

Sync関数を構築するときは、アクセス制御とドキュメント配布の要件を決定する必要があります。例えば：

- 処理するドキュメントタイプ
- 提供するユーザー
- どのユーザーがどのドキュメントタイプにアクセスする必要があるか
- ドキュメントの作成、更新、および/または削除にどのような制約を課すか

この例の要件は次のとおりです。

- ドキュメントに次のプロパティがあること： `title `、`title `、`channels ` 、`writers `
- `editor`ロールを持つユーザーへのアクセスの作成および/または削除のみを許可
- ドキュメントの`writers`プロパティで識別されたユーザーのみが削除を含む変更を行うことを許可
- ドキュメントのメタデータ内で設定されたチャネルにドキュメントを割り当てる


```JAVASCRIPT
// Note the new (3.0), optional, argument `meta`
function (doc, oldDoc, meta) {
  if (doc._deleted) {
    // Only editors with write access can delete documents:
    requireRole("role:editor"); 
    requireUser(oldDoc.writers); 
    // Skip other validation because a deletion has no other properties:
    return;
  }
  // Required properties:
  if (!doc.title || !doc.creator ||
        !doc.channels || !doc.writers) { 
    throw({forbidden: "Missing required properties"});
  } else if (doc.writers.length == 0) {
    throw({forbidden: "No writers"});
  }
  if (oldDoc == null) {
    // Only editors can create documents:
    requireRole("role:editor"); 
    // The 'creator' property must match the user creating the document:
    requireUser(doc.creator)
  } else {
    // Only users in the existing doc's writers list can change a document:
    requireUser(oldDoc.writers); 
    // The "creator" property is immutable:
    if (doc.creator != oldDoc.creator) {
            throw({forbidden: "Can't change creator"}); 
    }
  }
  // Finally, assign the document to the channels in the list:
  channel(meta.XattrChannels.channels); 
}
```

## 関連情報

https://docs.couchbase.com/sync-gateway/3.0/sync-function-overview.html