アトミック操作

この拡張機能は、複数の「操作」を線形かつアトミックに実行する手段を提供します。操作とは、基本JSON:API仕様で許可されている変更のシリアル化された形式です。

クライアントは、単一の要求で操作の配列を送信できます。この拡張機能は、これらの操作が順番に処理され、完全に成功するか、一緒に失敗するかのいずれかであることを保証します。

URI

この拡張機能のURIはhttps://jsonapi.dokyumento.jp/ext/atomicです。

名前空間

この拡張機能は、atomic名前空間を使用します。

注:JSON:API拡張機能は、予約された名前空間をプレフィックスとして使用して、新しいドキュメントメンバーを導入することのみができます。

ドキュメント構造

この拡張機能をサポートするドキュメントには、dataincludedを除く、基本仕様で許可されているトップレベルメンバーのいずれかを含めることができます(dataincludedは含めることはできません)。

さらに、そのようなドキュメントには、次のメンバーのいずれかを含めることができますが、両方を含めることはできません。

atomic:operationsまたはatomic:resultsのいずれかが存在する場合、errorsメンバーを同じドキュメントに含めることはできません。

操作オブジェクト

操作オブジェクトには、次のメンバーを含める必要があります。

  • op:実行する操作の種類を示す、文字列で表される操作コード。値は次のいずれかでなければなりません。

    • "add":新しいリソースまたはリレーションシップを作成します。
    • "update":リソースまたはリレーションシップを更新します。
    • "remove":リソースまたはリレーションシップを削除します。

操作オブジェクトには、操作のターゲットを指定するために、次のメンバーのいずれかを含めることができますが、両方を含めることはできません。

  • ref:次のメンバーの組み合わせのいずれかを含まなければならないオブジェクト。

    • typeid:個々のリソースをターゲットにします。
    • typelid:前の操作オブジェクトでローカルID(lid)が割り当てられた個々のリソースをターゲットにします。
    • typeid、およびrelationship:個々のリソースのリレーションシップをターゲットにします。
    • typelid、およびrelationship:前の操作オブジェクトでローカルID(lid)が割り当てられた個々のリソースのリレーションシップをターゲットにします。
  • href:操作のターゲットを識別するURI参照[RFC3986 セクション 4.1]を含む文字列。

特定の種類の操作では、以下のようにrefまたはhrefを含める必要があります。

操作オブジェクトには、次のメンバーも入れることができます。

  • data:操作の「主要なデータ」。

  • meta:操作に関する非標準のメタ情報を含むメタオブジェクト

以下のように、異なる種類の操作を処理するには、異なるメンバーが必要です。

結果オブジェクト

操作の結果オブジェクトには、次のメンバーのいずれかを含めることができます。

  • data:操作の結果として得られる「主要なデータ」。

  • meta:結果に関する非標準のメタ情報を含むメタオブジェクト

dataを返す必要がない操作の場合、空の結果オブジェクト({})が許容されます。

処理

この拡張機能で送信されるすべてのHTTPリクエストは、POSTで発行する必要があります。

サーバーは、atomic:operations配列に表示されている順序で操作を実行する必要があります。

サーバーは、すべての操作をアトミックに実行する必要があります。そのため、いずれかの操作の実行に失敗すると、先行する操作の影響が無効になります。

サーバーは、レスポンスドキュメントが返される場合、成功した操作リクエストに200 OKで応答する必要があります。結果オブジェクトの配列は、トップレベルのatomic:resultsメンバーで返す必要があります。結果配列の長さは要求された操作配列と同じでなければならず、各結果は、関連付けられた操作と位置的に対応していなければなりません。

操作にdataを返す必要がない場合、サーバーは、dataを含む操作を含む成功したリクエストに204 No Contentとレスポンスドキュメントなしで応答することができます。

処理エラー

要求内のいずれかの操作が失敗した場合、サーバーは基本仕様に記載されているとおりにレスポンスする必要があります。1つ以上のエラーオブジェクトの配列を返す必要があります。各エラーオブジェクトには、要求ドキュメント内の問題のソースへのpointerを含むsourceメンバーが含まれている必要があります。

要求された操作が不正な形式であるか、不完全である場合、サーバーは400 Bad Requestで応答し、トップレベルのerrorsメンバーを含むドキュメントを含める必要があります。そのerrorsメンバーにはエラーオブジェクトが含まれています。エラーオブジェクトには、無効な操作へのpointerを含むsourceメンバーを含める必要があります。

操作が適切な形式であるにもかかわらず、サーバーで処理できない場合、サーバーは400 Bad Requestまたはより適切なエラー応答(例:409 Conflictまたは422 Unprocessable Entity)で応答し、トップレベルのerrorsメンバーを含むドキュメントを含める必要があります。そのerrorsメンバーには、詳細を提供する1つ以上のエラーオブジェクトが含まれています。

特定の操作の処理

次のセクションでは、特定の操作の処理方法について説明します。

リソースの作成

リソースを作成する操作は、操作のhrefメンバーを介してリソースコレクションをターゲットにすることができます。

操作には、"add"opコードと、dataとしてのリソースオブジェクトを含める必要があります。リソースオブジェクトには、少なくともtypeメンバーを含める必要があります。

例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "add",
    "href": "/blogPosts",
    "data": {
      "type": "articles",
      "attributes": {
        "title": "JSON API paints my bikeshed!"
      }
    }
  }]
}

この例では、hrefを使用して、リソースのtypeとは異なるリソースコレクション(この場合はblogPosts)をターゲットにしていることに注意してください。hrefの使用はこの拡張機能では完全にオプションですが、個々の実装では必須になる可能性があります。

応答

サーバーがクライアント生成のIDを使用してリソースを作成でき、その表現が操作内のリソースと同一である場合、サーバーはdataメンバーを含まない結果を返すことができます。または、作成されたリソースをdataとして含む結果を返すことができます。

サーバーがリソースを正常に作成できるその他すべてのケースでは、サーバーは作成されたリソースをdataとして含む結果を返す必要があります。

例:

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:results": [{
    "data": {
      "links": {
        "self": "http://example.com/blogPosts/13"
      },
      "type": "articles",
      "id": "13",
      "attributes": {
        "title": "JSON API paints my bikeshed!"
      }
    }
  }]
}

サーバーがリソースを作成できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrorsを含む応答ドキュメントを返す必要があります。

リソースの更新

リソースを更新する操作は、操作のrefメンバーまたはhrefメンバーを介してそのリソースをターゲットにすることができますが、両方を使用することはできません。

操作には、"update"opコードを含める必要があります。

例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "update",
    "data": {
      "type": "articles",
      "id": "13",
      "attributes": {
        "title": "To TDD or Not"
      }
    }
  }]
}
応答

サーバーが更新を受け入れるが、要求で指定されたもの以外の方法でリソースを変更する場合(たとえば、updatedAt属性または計算されたshaを更新する場合)、サーバーは更新されたリソースの表現をdataとして含む結果を返す必要があります。

サーバーが更新を受け入れ、提供されたもの以外のフィールドを更新しない場合、サーバーはdataを含まない結果、またはリソースの表現をdataとして含む結果を返す必要があります。すべての結果が空の場合、サーバーは204 No Contentとドキュメントなしで応答することができます。

サーバーがリソースを更新できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrorsを含む応答ドキュメントを返す必要があります。

リソースの削除

リソースを削除する操作は、操作のrefメンバーまたはhrefメンバーを介してそのリソースをターゲットにする必要がありますが、両方を使用することはできません。

操作には、"remove"opコードを含める必要があります。

例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "remove",
    "ref": {
      "type": "articles",
      "id": "13"
    }
  }]
}
応答

サーバーがリソースを削除できる場合、サーバーはdataを含まない結果を返す必要があります。すべての結果が空の場合、サーバーは204 No Contentとドキュメントなしで応答することができます。

サーバーがリソースを削除できない場合、適切なエラー応答を返す必要があり、上記のようにトップレベルのerrorsを含む応答ドキュメントを返す必要があります。

1対1リレーションシップの更新

リソースの1対1リレーションシップを更新する操作は、操作のrefメンバーまたはhrefメンバーを介してそのリレーションシップをターゲットにする必要がありますが、両方を使用することはできません。

操作には、"update"opコードを含める必要があります。

たとえば、次のリクエストは1対1のリレーションシップを割り当てます。

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "13",
      "relationship": "author"
    },
    "data": {
      "type": "people",
      "id": "9"
    }
  }]
}

そして、次のリクエストは1対1のリレーションシップをクリアします。

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "13",
      "relationship": "author"
    },
    "data": null
  }]
}
応答

サーバーがリレーションシップの更新に成功した場合、サーバーはdataを含まない結果を**必ず**返却する必要があります。すべての結果が空の場合、サーバーは204 No Contentとドキュメントなしで応答しても**構いません**。

サーバーがリレーションシップの更新に失敗した場合、適切なエラーレスポンスを**必ず**返却する必要があり、トップレベルのerrorsを含むレスポンスドキュメントを**返すことが推奨**されます(上記の説明を参照)。

多対多リレーションシップの更新

リソースの多対多リレーションシップを更新する操作は、操作のrefメンバまたはhrefメンバを介してそのリレーションシップをターゲットにする**必要**がありますが、両方同時に指定することはできません。

多対多リレーションシップにメンバを追加するには、操作にopコードとして"add"を含める**必要**があります。例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "add",
    "ref": {
      "type": "articles",
      "id": "1",
      "relationship": "comments"
    },
    "data": [
      { "type": "comments", "id": "123" }
    ]
  }]
}

多対多リレーションシップのすべてのメンバを置き換えるには、操作にopコードとして"update"を含める**必要**があります。例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "update",
    "ref": {
      "type": "articles",
      "id": "1",
      "relationship": "tags"
    },
    "data": [
      { "type": "tags", "id": "2" },
      { "type": "tags", "id": "3" }
    ]
  }]
}

多対多リレーションシップからメンバを削除するには、操作にopコードとして"remove"を含める**必要**があります。例:

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "remove",
    "ref": {
      "type": "articles",
      "id": "1",
      "relationship": "comments"
    },
    "data": [
      { "type": "comments", "id": "12" },
      { "type": "comments", "id": "13" }
    ]
  }]
}
応答

サーバーがリレーションシップの更新に成功した場合、サーバーはdataを含まない結果を**必ず**返却する必要があります。すべての結果が空の場合、サーバーは204 No Contentとドキュメントなしで応答しても**構いません**。

サーバーがリレーションシップの更新に失敗した場合、適切なエラーレスポンスを**必ず**返却する必要があり、トップレベルのerrorsを含むレスポンスドキュメントを**返すことが推奨**されます(上記の説明を参照)。

複数操作の処理

上記の例はすべて、基本仕様からの同等の単一リクエストと一致する単一操作を実行します。しかし、この拡張の主な価値は、複数の操作を線形かつアトミックに実行できるようになることです。

次の例では、2つのリソースを追加し、それらの間にリレーションシップを単一のリクエストで作成します。

POST /operations HTTP/1.1
Host: example.org
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"
Accept: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:operations": [{
    "op": "add",
    "data": {
      "type": "authors",
      "id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
      "attributes": {
        "name": "dgeb"
      }
    }
  }, {
    "op": "add",
    "data": {
      "type": "articles",
      "id": "bb3ad581-806f-4237-b748-f2ea0261845c",
      "attributes": {
        "title": "JSON API paints my bikeshed!"
      },
      "relationships": {
        "author": {
          "data": {
            "type": "authors",
            "id": "acb2ebd6-ed30-4877-80ce-52a14d77d470"
          }
        }
      }
    }
  }]
}

サーバーはこのリクエストに対して次のように応答する場合があります。

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json;ext="https://jsonapi.dokyumento.jp/ext/atomic"

{
  "atomic:results": [{
    "data": {
      "links": {
        "self": "http://example.com/authors/acb2ebd6-ed30-4877-80ce-52a14d77d470"
      },
      "type": "authors",
      "id": "acb2ebd6-ed30-4877-80ce-52a14d77d470",
      "attributes": {
        "name": "dgeb"
      }
    }
  }, {
    "data": {
      "links": {
        "self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c"
      },
      "type": "articles",
      "id": "bb3ad581-806f-4237-b748-f2ea0261845c",
      "attributes": {
        "title": "JSON API paints my bikeshed!"
      },
      "relationships": {
        "author": {
          "links": {
            "self": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/relationships/author",
            "related": "http://example.com/articles/bb3ad581-806f-4237-b748-f2ea0261845c/author"
          }
        }
      }
    }
  }]
}

この操作リクエストは、著者を追加してから記事を追加し、次にそれらの間のリレーションシップを追加するように構成することもできたことに注意してください。

また、ローカルID(つまり、lidメンバ)は、複数の操作を含むリクエストに特に役立ちます。ローカルIDは、まだidが割り当てられていないリソースを関連付けるために使用できます。