Sync Gateway機能解説： Sync関数APIリファレンス

## はじめに

以下の記事では、Couchbase Mobileにおいて、Sync関数のはたす役割を開設しました。

https://qiita.com/yoshiyuki_kono/items/c418475ab46bf9505938

ここでは、Sync関数で何を行うことができるかをより具体的に理解できるよう、ここでは、Sync関数を定義する際に、開発者が利用することのできるヘルパー関数に関するリファレンスを提供します。

なお、Couchbase Mobileについては、[Couchbase Mobileアプリケーション開発へのロードマップ](https://qiita.com/yoshiyuki_kono/items/6a5bbad1b02196a25417)に記事をまとめている他、（これらの記事を元に構成した）以下の電子書籍を無償で頒布しています。

https://techbookfest.org/product/5074954942939136?productVariantID=5042865933647872

また、Couchbase Mobileは、Couchbase LiteとCouchbase Serverとのデータ同期機能を提供します。Couchbase Serverの存在意義、機能詳細、利用方法等については、拙著[NoSQLドキュメント指向データベースCouchbase Serverファーストステップガイド(インプレスR&D刊)](https://www.amazon.co.jp/-/en/%E6%B2%B3%E9%87%8E-%E6%B3%B0%E5%B9%B8/dp/429560030X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=1636588872&sr=1-1)や、[NoSQL/JSONデータベースCouchbase Server理解・活用へのロードマップ](https://qiita.com/yoshiyuki_kono/items/4c9cd1115efc5807b49f)にまとめてある記事をご参考ください。


## Sync関数APIリファレンス

Sync Gatewayでは、開発者が、Sync関数を定義することによって、データルーティングとアクセス制御を実装します。
Sync関数は、データの同期とレプリケーションのセキュリティにとって非常に重要です。ここでは、Sync関数を定義する際に、開発者が利用することのできるヘルパー関数に関するリファレンスを提供します。

### 注意

上述の記事で扱っているように、Sync関数は、以下のような引数を取るJavaScript関数として実装します。

```JavaScript
function (doc, oldDoc, meta) { 
...
}
```

Sync関数のなかで、更新されるドキュメントを検証するときは、関数はドキュメントに対する更新が常に破壊的なものでないことを前提とするべきではないため、`doc`パラメータのすべてのプロパティを信頼できないものとして扱うべきです。これは当たり前のように聞こえるかもしれませんが、`requireUser(oldDoc.owners)`ではなく、`requireUser(doc.owners)`を代わりに呼び出すなど、間違いを犯しやすい場合があります。
ドキュメントプロパティを使用して、ドキュメントを検証する場合は、`doc`ではなく、`oldDoc`のそのプロパティを利用することが往々にして適当だと考えられます。

### access

#### 目的
ユーザーにチャネルへのアクセスを許可します。

#### 引数

以下の二つの引数を指定します。

- ユーザーやを識別する文字列、またはその配列
- チャンネルを識別する文字列、またはその配列

この関数をユーザーではなくロールに適用するには、値の前に`role:`を付けます。これにより、そのロールが割り当てられたすべてのユーザーに、指定されたチャネルへのアクセスが許可されます。

すべてのチャネルを示すワイルドカード（`'*'`）を使用して、すべてのチャネルのすべてのドキュメントへのアクセスをユーザーに許可することができます。

#### コンテクスト
Sync関数内からこの関数を複数回呼び出すことができます。

すべてのアクティブなドキュメントによるすべてのアクセス呼び出しの効果は、効果的に結合されて結合されるため、いずれかのドキュメントがユーザーにチャネルへのアクセスを許可すると、そのユーザーはチャネルにアクセスできます。


#### 使用例

以下、`access()`の利用例を示します。

シングルユーザーにシングルチャネルのアクセスを許可する

```JAVASCRIPT
access ("jchris", "mtv"); 
```

シングルユーザーに複数のチャネルへのアクセスを許可する

```JAVASCRIPT
access ("jchris", ["mtv", "mtv2", "vh1"]); 
```
ロール指定を含む複数のユーザーに単一のチャネルのアクセスを許可する

```JAVASCRIPT
access (["snej", "jchris", "role:admin"], "vh1"); ③
```



### channel

#### 目的

ドキュメントを指定されたチャネルにルーティングします。

#### 引数

##### channels

- チャネル名を識別する文字列、またはその配列

指定するチャネルは必ずしも事前に定義されている必要はありません。
ドキュメントがチャネルにルーティングされると、そのチャネルが存在しない場合、暗黙的に作成されます。

#### コンテクスト

`channel()`関数は、どのドキュメントでも、Sync関数から0回以上呼び出すことができます。

ルーティングの変更は、ドキュメントが実際にデータベースに保存されるまで効果がないため、Sync関数が最初に`channel`または`access`を呼び出した後、ドキュメントの更新が（更新の拒否などによって）行われなかった場合、チャネルとアクセスの変更は発生しません。

利便性のため、`null`、または`undefined`として解決される引数で`channel`を呼び出すことが可能です。その場合、単に何もしません。
これにより`channel(doc.channels)`において、最初に`doc.channels`が存在するかどうかを確認しなくても、利用することが可能です。

#### 使用例

すべての「published」ドキュメントを「public」チャネルにルーティングします。

```JAVASCRIPT
function (doc, oldDoc, meta) {
   if (doc.published) {
      channel("public");
   }
}
```

### expiry

#### 目的
ドキュメントの有効期限値（TTL）を設定します。

#### 引数

有効期限は、次の2通りの方法で指定することができます。

- ISO-8601形式の日付文字列
- Couchbase Serverドキュメントの有効期限値

Couchbase Serverドキュメント有効期限はUnix TIMEとして指定します。あるいは、目的のTTLが30日未満の場合は、現在の時刻からの秒単位の間隔として表すこともできます（たとえば、値5を指定すると、ドキュメントが書き込まれてから5秒後に削除されます。

#### コンテクスト
内部的には、有効期限はCouchbase Serverドキュメントで設定および管理されます。

#### 影響

有効期限の値に達したときの結果のドキュメントへの影響は、以下のように`shared-bucket-access`の設定によって異なります。

##### `enabled`

ドキュメントのアクティブなリビジョンは破棄されます。

##### `disabled`

ドキュメントはCouchbae Liteデータベースから削除されますが、パージメカニズムと同様に、ローカルデータベースにのみ適用されます。

#### 使用例

以下は、有効期限を2022年6月23日の午前5時に設定します。

```JAVASCRIPT
expiry("2022-06-23T05:00:00+01:00") 
```

### requireAccess

#### 目的

指定されたチャネルの少なくとも1つにアクセスできるユーザーによって行われていないドキュメントの更新を拒否します。

#### 引数

チャネル名を識別する文字列、またはその配列

`requireAccess`は、チャネル名を使用して明示的に行われた許可のみを認識し、ワイルドカードを認識しないことにご注意ください。ユーザーがワイルドカード（`*`）のみにより、すべてのチャネルに対するアクセスを許可されている場合、そのユーザーは`requireAccess`の引数として指定される、具体的なチャネルへのアクセスを明示的に許可されていないため、この関数呼び出しは、失敗し（ドキュメントの更新が拒否され）ます。

#### コンテクスト

この関数は例外をスローすることで拒否を通知するため、Sync関数のそれ以降のコードは実行されません。



#### 使用例

次の例は、ユーザーが「events」チャネルを読み取るためのアクセス権を持っていない限り、例外をスローします。
さらに、ユーザーが前のリビジョンのchannelsプロパティのチャネル（のうちどれか1つ）のアクセスが無い場合、例外をスローします。

```JAVASCRIPT
requireAccess("events"); 

if (oldDoc) {
    requireAccess(oldDoc.channels); 
}
```

### requireAdmin

#### 目的
Sync Gateway Admin REST APIによって行われていないドキュメントの更新を拒否します。

#### 引数
なし

#### 使用例

次の例は、 このリクエストがAdmin REST APIに送信されない限り、例外をスローします。

```JAVASCRIPT
requireAdmin();
```


### requireRole

#### 目的
指定されたロールの少なくとも1つを持っているユーザーによって行われていないドキュメントの更新を拒否します。

#### 引数

ロールを識別する文字列、またはその配列

ロール名の前には常に`role:`を付ける必要があります。ロール名がこのルールに準拠していない場合、例外がスローされます。

#### コンテクスト
この関数は例外をスローすることで拒否を通知するため、Sync関数のそれ以降のコードは実行されません。

#### 使用例

ユーザーが「admin」ロールを持っていない限り、エラーをスローします。

```JAVASCRIPT
requireRole("admin"); 
```

ユーザーがこれらのロールを1つ以上持っていない限り、エラーをスローします。

```JAVASCRIPT
requireRole(["admin", "old-timer"]); 
```


### requireUser

#### 目的

指定したユーザーによって行われていないドキュメントの更新を拒否します。

#### 引数

##### ユーザー名

ユーザーを識別する文字列、またはその配列


#### コンテクスト
この関数は例外をスローすることで拒否を通知するため、残りのSync関数は実行されません。



#### 使用例

ユーザーが「snej」でない場合はエラーをスローします。

```JAVASCRIPT
requireUser("snej"); 
```
ユーザーの名前がリストにない場合はエラーをスローします。

```JAVASCRIPT
requireUser(["snej", "jchris", "tleyden"]); 
```



### role

#### 目的
ユーザーにロールを追加します。これにより、そのロールに割り当てられたチャネルにアクセスできるようになります。

ユーザーと同様に、ロールは管理者が明示的に作成する必要があります。

#### 引数

次の二つの引数を取ります。

- ロールを識別する文字列、またはその配列
- ユーザーを識別する文字列、またはその配列

ロール名の前には常に`role:`を付ける必要があります。ロール名がこのルールに準拠していない場合、例外がスローされます。



#### コンテクスト
この機能は、アクセス機能が特定のタイプの変更を検証するためにロールメンバーシップを必要とする場合、ドキュメントを改訂するユーザーの機能に影響を与えます。その使用法は`access`に似ています。

存在しないロールはエラーを引き起こしませんが、ユーザーのアクセス権限には影響しません。

遡及的にロールを作成できます。ロールが作成されるとすぐに、そのロールへの既存の参照が有効になります。

#### 使用例

ロールadminはユーザーに割り当てられます

```JAVASCRIPT
role ("jchris", "role:admin"); 
```

名前付きの両方のロールがユーザーに割り当てられます


```JAVASCRIPT
role ("jchris", ["role:portlandians", "role:portlandians-owners"]); 
```

ロールmobileはすべての指定されたユーザーに割り当てられます


```JAVASCRIPT
role (["snej", "jchris", "traun"], "role:mobile"); 
```

操作なし

```JAVASCRIPT
role ("ed", null);
```
  

### throw

#### 目的

ドキュメントが永続化または同期されないようにするために使用します。

#### 引数
なし

#### コンテクスト
ドキュメント構造の有効性を強制するために、必要な制約をチェックし、それらが満たされていない場合は例外をスローすることができます。

ドキュメントを検証する際、状態の違法な変更をチェックするために、新しいリビジョンを古いリビジョンと比較する必要があることがよくあります。たとえば、一部のプロパティは、ドキュメントの作成後に不変である場合や、特定のユーザーのみが変更できる場合、または特定の方法でのみ変更が許可される場合があります。そのため、現在のドキュメントの内容が`oldDoc`パラメータとしてSync関数に渡されます。

そもそも無効なドキュメントを作成しないことをお勧めします。可能な限り、アプリのロジックと検証機能により、ローカルで無効なドキュメントが作成されないようにする必要があります。サーバー側のSync関数の検証は、フェイルセーフおよび悪意のあるアクセスに対する保護と見なす必要があります。

#### 使用例

この例では、sync関数はそれが存在するデータベースへのすべての書き込みを許可しません。

```JAVASCRIPT
function(doc) {

   throw({forbidden: "read only!"}) 

}
```

ドキュメントの更新は、HTTP 403 "Forbidden"エラーコードで拒否され、`forbidden:`プロパティの値はHTTPステータスメッセージになります。


## 参考情報

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