Contentful Preview API で参照中の投稿のフィールド値を URL で使いたい

結論から言うとできません。参照

(2019-02)時点のこのサイトでは、 Post コンテンツタイプで親となる Note コンテンツタイプの投稿を必ず1つ参照しなければならないという形で管理しています。そしてあまり無意味な URL にしてはいけなかったので、 Post コンテンツタイプを表示するページ(?note=xxx&post=yyy)では、

  • xxxnoteというフィールド名で参照する Note コンテンツタイプのslugフィールド値
  • yyyは Post コンテンツタイプのtitleフィールド値

のような形にしました。ですのでコンテンツプレビュー API でもその2つの値を取得する必要があります。

確認したこと

コンテンツプレビューの URL ではentryで投稿オブジェクトが格納されていてこれを JavaScript のオブジェクトアクセスのように辿って、使いたい値を使うという感じです。またそれは{}で囲んで使います。

なので僕は以下のような Post コンテンツタイプのプレビュー URL に以下のように設定しました。(domain.comは適当です)

http://domain.com/note/post?note={entry.fields.note.fields.slug}&post={entry.fields.title}

しかし、これはうまく動きません。entry.fields.note.fields.slugの箇所がundefinedとなってしまいます。

次はこんな URL を試しました。なぜsys.idが分かったかというと確かドキュメントに書かれてたからじゃなかったからかなと思います。(忘れた;)

http://domain.com/note/post?note={entry.fields.note.sys.id}&post={entry.fields.title}

これはちゃんと何かしらの値が埋められた URL が発行されました。つまり参照のメタ情報(sys)は扱えるが、フィールド値は使うことができない仕様のようでした。

一応このことについて問い合わせてみたいのがトップの参照リンクです。

The slug of content type Note can be obtained by passing entry ID of content type Post, however, unfortunately it is not possible to be used in content preview directly.

なんとかコンテンツプレビュー API に対応する

コンテンツプレビュー API で渡ってくる値用のクエリを定義して、「そのクエリが含まれている URL ならコンテンツプレビューで表示する」というようにするしか無いかなと思います。つまり、コンテンツプレビューように、

http://domain.com/note/post?__noteId={entry.fields.note.sys.id}&__postId={entry.sys.id}

みたいな。一応プライベートなものですよみたいに_を頭に付けるなどでやるしかないかなと思います。

スペースの削除

  1. Space Settings
  2. General Settings
  3. 「Remove space and all its contents」ボタンをクリック
  4. モーダルのフィールドにスペース名を入力
  5. Remove!

Image from Gyazo

API を用いたエントリーの取得と更新

NodeJS で contentful-management を使う方法を想定してます。

準備

以下の流れでやっていきます。

  1. 依存のインストール
  2. アクセストークンの取得

依存のインストール

まだpacakge.jsonが無いのであれば以下のコマンドで NodeJS プロジェクトを準備します。

yarn init -y

以下でcontentful-managementをインストールします。

yarn add contentful-management

アクセストークンの取得

上部のメニューから画像の順番のように辿り、出てきたダイアログに後で分かる名前を付けて Generate します。

生成されたアクセストークンはあとで参照できないので、大切に保存します。

処理を書く

取得から更新の流れは以下のようなフローです。

  1. アクセストークンを使って通信する為のclientを取得
  2. clientからspaceインスタンスを取得
  3. spaceからenvironmentインスタンスを取得
  4. environmentからentryインスタンスを取得
  5. フィールド更新
  6. entry更新
  7. entry公開

アクセストークンを使って通信する為の client を取得

contentful-managementからcreateClientを取得し、それにアクセストークンに渡してclientを作ります。

const {createClient} = require('contentful-management');

const client = createClient({accessToken: ACCESS_TOKEN});

(async () => {
  // これより以下はこの中に入る
})();

これより以下はasync関数の中に入ります。

client から space インスタンスを取得

const space = await client.getSpace(SPACE_ID);

スペースIDSPACE_IDを渡してspaceを取得します。このスペースIDは Contentful サイトの上部のメニューから画像の流れで取得できます。

spaceにもgetEntriesgetEntryはありますが、contentful@^5.12.0上では使おうとすると「将来的に撤廃予定です。environmentから使ってください」というような警告メッセージが表示されます。

space から environment インスタンスを取得

environmentには環境名を入れます。何も弄ってなかったり無料プランの場合これはmasterになります。

const environment = await space.getEnvironment(ENVIRONMENT_ID);

environment から entry インスタンスを取得

const entry = await env.getEntry(ENTRY_ID);

エントリーIDENTRY_IDはエントリーを書くページの右サイドバーの Info の画像の位置から取得できます。

フィールド更新

値を更新するには単にfieldsに新しい値を代入して更新します。

entry.fields.title.ja = '新しいタイトル';

entry 更新

投稿を更新するにはEntry#updateを実行します。これでエントリーの内容が更新されます。(公開はまだ)

const updatedEntry = await entry.update();

entry 公開

Entry#publishを実行して公開します。

await updatedEntry.publish();

問題がなければ更新されてるハズです!

注意点は必ずEntry#updateの戻り値を使うことです。もし以下のように実装した場合 API はエラーを返します。

await entry.update();
await entry.publish();
{
  "status": 409,
  "statusText": "Conflict",
  "message": "",
  "details": {},
  "request": {
    "url": "https://api.contentful.com:443/spaces/jfvyr7b4oy3y/environments/master/entries/3uzCE52rkssyRkaFMPwhUF/published",
    "headers": {
      "Accept": "application/json, text/plain, */*",
      "Content-Type": "application/vnd.contentful.management.v1+json",
      "X-Contentful-User-Agent": "sdk contentful-management.js/5.12.0; platform node.js/v12.14.1; os macOS/18.7.0;",
      "Authorization": "Bearer ...D8vrU",
      "user-agent": "node.js/v12.14.1",
      "Accept-Encoding": "gzip",
      "X-Contentful-Version": 1856
    },
    "method": "put",
    "payloadData": null
  },
  "requestId": "ac28b8c5beee6aecde8d1856b6cd4efc"
}

entrysys.versionプロパティを持ちますが、updatepublish後に帰ってくるentryはこの値が更新されてます。恐らくですが、entryは「このバージョンではこういうことをやった」という感じの情報を持っていて、既に実行済みのバージョンで何かしようとすると上記のようなエラーが返ってくるのではと思います。

全体コード

const {createClient} = require('contentful-management');

const client = createClient({accessToken: ACCESS_TOKEN});

(async () => {
  const space = await client.getSpace(SPACE_ID);
  const environment = await space.getEnvironment(ENVIRONMENT_ID);
  const entry = await env.getEntry(ENTRY_ID);
  entry.fields.title.ja = '新しいタイトル';
  const updatedEntry = await entry.update();
  await updatedEntry.publish();
})();