NotionRubyMapping のアップデート(7) : hkob の雑記録 (162)

はじめに

hkob の雑記録の第162回目は、File upload object の作成までをテストします。

FileUploadObject のテスト

まず、FileUploadObject のテストを記述しました。とりあえずテストデータを作成している small file だけテストします。fname で既存のファイル名を指定したら API で File upload object を作成し、その ID を保持します。

# frozen_string_literal: true

module NotionRubyMapping
  RSpec.describe FileUploadObject do
    tc = TestConnection.instance
    tc.read_json "create_file_upload_image"

    describe "initialize" do
      context "with a small file" do
        subject { FileUploadObject.new fname: "spec/fixtures/ErSxuLeq.png-medium.png" }
        it "creates a new FileUploadObject" do
          expect(subject).to be_a(FileUploadObject)
          expect(subject.id).to eq "20cd8e4e-98ab-81aa-973b-00b23083c115"
        end
      end
    end
  end
end

FileUploadObject#initialize

これだけであれば実装は簡単なので、さっと書いてしまいます。external_url の場合やサイズの大きいファイルの場合も記述してしまっています。

# frozen_string_literal: true

class FileUploadObject
  MAX_SIZE = 10 * 1024 * 1024 # 10 MB
  # @param [String] id
  def initialize(fname:, external_url: nil)
    @fname = fname
    if external_url
      payload = {mode: "external_url", external_url: external_url, filename: fname}
      create payload
    else
      raise StandardError, "FileUploadObject requires a valid file name: #{fname}" unless File.exist?(fname)

      @file_size = File.size fname
      @number_of_parts = (@file_size - 1) / MAX_SIZE + 1
      payload = @number_of_parts == 1 ? {} : {number_of_parts: @number_of_parts, mode: "multi_part", filename: @fname}
      create payload
      # single_file_upload if @number_of_parts == 1
    end
  end
  attr_reader :id

  # @return [FileUploadObject]
  def create(payload)
    nc = NotionRubyMapping::NotionCache.instance
    response = nc.create_file_upload_request(payload)
    @id = response[:id]
  end
end

NotionCache#create_file_upload_request

ここで create_file_upload_request を実装します。path が異なるだけで他のものと変わりませんので、特にテストは記載していません。

    # @param [Hash] payload
    # @return [Hash] response
    def create_file_upload_request(payload = {})
      request :post, file_uploads_path, payload
    end

file_uploads_path のテスト

file_uploads_path は

    describe "file_uploads_path" do
      it { expect(nc.file_uploads_path).to eq "v1/file_uploads" }
    end

NotionCache#file_uploads_path

実装は簡単ですね。

    def file_uploads_path
      "v1/file_uploads"
    end

WebMock の設定

テストでは実際に Web アクセスをするのではなく、WebMock を使っているのでこのままでは Mock が存在しないためにエラーになります。以下の記述により、stub が作成されます。

    def create_file_upload
      generate_stubs_sub :post, __method__, :file_uploads_path, {
        image: [nil, 200],
      }
    end

ちなみに、generate_stubs_sub は以下のようになっています。今回の場合、メソッド名から得た prefix として create_file_upload と hash のキーから得た image を使って、昨日作成した create_file_upload_image というファイル名を得るようになっています。

    # @param [Symbol] method
    # @param [Symbol, nil] prefix
    # @param [Symbol] path_method
    # @param [Hash<Array>] hash
    def generate_stubs_sub(method, prefix, path_method, hash)
      hash.each do |key, (id, code, payload)|
        raise StandardError, "code is missing" unless code.is_a? Integer

        path = id ? @nc.send(path_method, *id) : @nc.send(path_method)
        stub method, "#{prefix}_#{key}", path, code, payload
      end
    end

この中の stub は実際の Notion API の呼び出しのフリをする部分です。呼び出しの代わりに用意した json を返します。明日はこの部分を multipart に対応させる必要がありそうです。

    # @param [Symbol] method
    # @param [String] json_file response body
    # @param [String] path
    # @param [Fixnum] code
    # @param [Hash] payload request body
    def stub(method, json_file, path, code, payload = nil)
      request = {
        headers: {
          "Accept" => "*/*",
          "Accept-Encoding" => "gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
          "Authorization" => "Bearer #{notion_token}",
          "Notion-Version" => NotionRubyMapping::NOTION_VERSION,
          "User-Agent" => "Faraday v2.13.1",
        },
      }
      request[:body] = JSON.generate(payload) if payload
      response = {
        body: JSON.generate(read_json(json_file)),
        status: code,
        headers: {
          "Content-Type" => "application/json",
        },
      }
      WebMock.stub_request(method, "https://api.notion.com/#{path}").with(request).to_return(response)
    end

テスト確認

ここまで記述したところ、無事にテストは通過しました。

FileUploadObject
  initialize
    with a small file
      creates a new FileUploadObject

Finished in 0.00603 seconds (files took 0.30782 seconds to load)
1 example, 0 failures

おわりに

FileUploadObject の作成はしましたが、まだファイルのアップロードは実装していません。ここで、 # single_file_upload if @number_of_parts == 1 の部分を有効にしたいところですが、WebMock の部分や NotionCache の部分で multipart content-type の対応が必要なので、大きく修正が必要なようです。今日はここまでにしておきます。

hkob.notion.site