NotionRubyMapping の markdown 対応(3) : hkob の雑記録 (425)

はじめに

hkob の雑記録の第425回目(通算19日目)も、NotionRubyMapping を makedown に対応させます。今日は、マークダウンを特定の場所に追加する Page#insert_markdown メソッドを作成します。

テストデータの作成

最初、markdown の GET を実装しようとしたのですが、 "message": "Only public integrations can access this API.", というエラーが出てしまいました。こちらのページに記載がありました。

public integrations にしか対応していないようですね。

This endpoint is restricted to public integrations only. Internal integrations (workspace-level bots) will receive a restricted_resource error. Use the block-based API instead for internal integrations.

まずはいつものようにスクリプト作成です。最初は after を指示せず、ページの末端に追加します。

curl 'https://api.notion.com/v1/pages/315d8e4e98ab8108901dcfa2d3993f6e/markdown' \
  -H 'Notion-Version: 2025-09-03' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -X PATCH \
  --data '{
    "type": "insert_content",
    "insert_content": {
      "content": "- Added new item in page bottom\n"
    }
  }'

make するとページの一番下に箇条書きが追加されました。

一番下に追加

markdown の GET はできませんが、insert の返り値としては markdown が返却されるのですね。

{
  "object": "page_markdown",
  "id": "315d8e4e-98ab-8108-901d-cfa2d3993f6e",
  "markdown": "## Paragraph 2\n- [ ] To Do\n- [x] Checked To Do\n- Added new item in page bottom",
  "truncated": false,
  "unknown_block_ids": [],
  "request_id": "d19f3038-b10d-4c11-8422-7d6726d47130"
}

もう一つ after を指定した場合も追加してみます。

curl 'https://api.notion.com/v1/pages/315d8e4e98ab8108901dcfa2d3993f6e/markdown' \
  -H 'Notion-Version: 2025-09-03' \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Content-Type: application/json" \
  -X PATCH \
  --data '{
    "type": "insert_content",
    "insert_content": {
      "content": "1. Added numbered item after To Do item\n",
      "after": "- [ ] To Do\n"
    }
  }'

正しく、To Do の後ろに追加されました。

To Do の後ろに追加

テストの記述

まずは page bottom への挿入だけテストしてみます。今回、返却されるのは page_markdown ですが、Page object に markdown だけ登録する形にしてみようと思います。

    describe "insert markdown" do
      let(:pid) { TestConnection::NEW_PAGE_WITH_MARKDOWN_PAGE_ID }
      let(:page) { Page.new id: pid }

      context "when insert bottom" do
        markdown = "- Added new item in page bottom\n"
        let(:target) { page.insert_markdown markdown }

        context "when not dry_run" do
          it { expect(target.id).to eq pid }

          it {
            expect(target.markdown).to
            eq "## Paragraph 2\n- [ ] To Do\n- [x] Checked To Do\n- Added new item in page bottom"
          }
        end

        context "when dry_run" do
          it_behaves_like "dry run", :patch, :markdown_page_path, use_id: true, json: {
            "markdown" => markdown,
          }
        end
      end
    end

当然 undefined method になります。

     NoMethodError:
       undefined method 'insert_markdown' for an instance of NotionRubyMapping::Page

実装の作成

json を作成して markdown_page_path に関するメソッドなどを呼べばいいはずです。

    # @param [String] markdown
    # @param [Boolean] dry_run
    # @return [String, NotionRubyMapping::Base]
    def insert_markdown(markdown, dry_run: false)
      json = {"type" => "insert_content", "insert_content" => {"content" => markdown}}
      if dry_run
        self.class.dry_run_script :patch, @nc.markdown_page_path(@id), json
      else
        update_json @nc.markdown_page_request(@id, json)
      end
    end

markdown_page_path はないのでテストを書きます。

    describe "markdown_page_path" do
      it { expect(nc.markdown_page_path("ABC")).to eq "v1/pages/ABC/markdown" }
    end

markdown_page_path と markdown_page_request を追加してみます。

    # @param [String] page_id
    # @return [String] path
    def markdown_page_path(page_id)
      "v1/pages/#{page_id}/markdown"
    end

    # @param [String] page_id
    # @param [Hash] payload
    # @return [Hash] response hash
    def markdown_page_request(page_id, payload)
      request :patch, markdown_page_path(page_id), payload
    end

WebMock の追加

markdown_page に関する WebMock を追加します。

    def markdown_page
      generate_stubs_sub :patch, __method__, :markdown_page_path, {
        by_insert_bottom: [
          NEW_PAGE_WITH_MARKDOWN_PAGE_ID, 200, {
            "type" => "insert_content",
            "insert_content" => {
              "content" => "- Added new item in page bottom\n",
            },
          }
        ],
      }
    end

Base の修正

上にも書いたように page_markdown が返却されても Page object を生成して欲しいです。object が page_markdown の場合にも Page.new を実行します。

    # @param [Hash, Notion::Messages] json
    # @return [NotionRubyMapping::Base]
    def self.create_from_json(json)
      case json["object"]
      when "page", "page_markdown"
        Page.new json: json
      when "data_source"
        DataSource.new json: json
      when "database"
        Database.new json: json
      when "list"
        List.new json: json
      when "block"
        Block.decode_block json
      else
        raise StandardError, json.inspect
      end
    end

あとは markdown だけ取得したいです。@json に入っているので、その markdown キーを返すようにしましょう。

    # @return [String]
    def markdown
      @json && @json["markdown"]
    end

無事にテストが通過しました。

  insert markdown
    when insert bottom
      when not dry_run
        is expected to eq "315d8e4e98ab8108901dcfa2d3993f6e"
        is expected to eq "## Paragraph 2\n- [ ] To Do\n- [x] Checked To Do\n- Added new item in page bottom"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/pages/315d8e4e98ab8108901dcfa2d3993f6e/markdown'...type\":\"insert_content\",\"insert_content\":{\"content\":\"- Added new item in page bottom\\n\"}}'"                                                                                                                            

Finished in 0.148 seconds (files took 0.36512 seconds to load)
145 examples, 0 failures

after のテストを追加

追加したテストはこちらです。

      context "when insert after to do" do
        markdown = "1. Added numbered item after To Do item\n"
        after = "- [ ] To Do\n"
        let(:target) { page.insert_markdown markdown, after: after }

        context "when not dry_run" do
          it { expect(target.id).to eq pid }

          it {
            expect(target.markdown).to eq "## Paragraph 2\n- [ ] To Do\n1. Added numbered item after To Do item\n- [x] Checked To Do\n- Added new item in page bottom\n<empty-block/>"
          }
        end

        context "when dry_run" do
          let(:dry_run) { page.insert_markdown markdown, after: after, dry_run: true }

          it_behaves_like "dry run", :patch, :markdown_page_path, use_id: true, json: {
            "type" => "insert_content",
            "insert_content" => {
              "content" => markdown,
              "after" => after,
            },
          }
        end
      end

insert_markdown に after キーワード引数を追加します。

    # @param [String] markdown
    # @param [String, NilClass] after
    # @param [Boolean] dry_run
    # @return [String, NotionRubyMapping::Base]
    def insert_markdown(markdown, after: nil, dry_run: false)
      json = {"type" => "insert_content", "insert_content" => {"content" => markdown}}
      json["insert_content"]["after"] = after if after
      if dry_run
        self.class.dry_run_script :patch, @nc.markdown_page_path(@id), json
      else
        update_json @nc.markdown_page_request(@id, json)
      end
    end

あとは WebMock を更新するだけです。

    def markdown_page
      generate_stubs_sub :patch, __method__, :markdown_page_path, {
        by_insert_bottom: [
          NEW_PAGE_WITH_MARKDOWN_PAGE_ID, 200, {
            "type" => "insert_content",
            "insert_content" => {
              "content" => "- Added new item in page bottom\n",
            },
          }
        ],
        by_insert_after_todo: [
          NEW_PAGE_WITH_MARKDOWN_PAGE_ID, 200,
          {
            "type" => "insert_content",
            "insert_content" => {
              "content" => "1. Added numbered item after To Do item\n",
              "after" => "- [ ] To Do\n",
            },
          }
        ],
      }
    end

これで無事に全てのテストが通過しました。

  insert markdown
    when insert bottom
      when not dry_run
        is expected to eq "315d8e4e98ab8108901dcfa2d3993f6e"
        is expected to eq "## Paragraph 2\n- [ ] To Do\n- [x] Checked To Do\n- Added new item in page bottom"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/pages/315d8e4e98ab8108901dcfa2d3993f6e/markdown'...type\":\"insert_content\",\"insert_content\":{\"content\":\"- Added new item in page bottom\\n\"}}'"                                                                                                                            
    when insert after to do
      when not dry_run
        is expected to eq "315d8e4e98ab8108901dcfa2d3993f6e"
        is expected to eq "## Paragraph 2\n- [ ] To Do\n1. Added numbered item after To Do item\n- [x] Checked To Do\n- Added new item in page bottom\n<empty-block/>"
      when dry_run
        behaves like dry run
          is expected to eq "#!/bin/sh\ncurl -X PATCH 'https://api.notion.com/v1/pages/315d8e4e98ab8108901dcfa2d3993f6e/markdown'...ntent\":{\"content\":\"1. Added numbered item after To Do item\\n\",\"after\":\"- [ ] To Do\\n\"}}'"                                                                                                                            

Finished in 0.22883 seconds (files took 0.56389 seconds to load)
148 examples, 0 failures

おわりに

markdown の GET が使えなかったので、insert_markdown を実装しました。明日は replace_markdown を実装します。

hkob.notion.site