Ruby
Rails
Gem
AmazonProductAdvertisingApi

rapa で Product Advertising API を叩く

とある CGM を作るのに Amazon Product Advertising API を利用することになって、今回は API クライアントに r7kamrua/rapa を使うことにしました。

モンキーパッチ

今回の要件的に、rapa にはいくつかの機能が足りなかったので追加しました。Pull Request しようかなとも思ったんだけど、テストコードを書くのに MPNFeature を含むテスト用のデータを用意するのが激しく面倒だったのでモンキーパッチしちゃいました。が、誰かの参考になるかもしれないので貼っておきます。

config/initializers/rapa.rb
Rapa::Resources::ItemResource.class_eval do
  def mpn
    source.dig('ItemAttributes', 'MPN')
  end

  def feature
    source.dig('ItemAttributes', 'Feature')
  end
end

Rapa::Responses::BaseResponse.class_eval do
  def service_unavailable?
    status == 503
  end
end

Rapa::Responses::SearchItemsResponse.class_eval do
  MIN_PAGE = 1
  MAX_PAGE = 10

  def item_page
    raw = body.dig('ItemSearchResponse', 'Items', 'Request', 'ItemSearchRequest', 'ItemPage')
    raw ? raw.to_i : 1
  end

  def next_page
    page = item_page + 1
    page unless out_of_range?(page)
  end

  def prev_page
    page = item_page - 1
    page unless out_of_range?(page)
  end

  private

  def out_of_range?(page)
    page < MIN_PAGE || page > [MAX_PAGE, total_pages].min
  end
end

ラッパークラス

今回は rapa を採用しましたが、将来 API クライアントを差し替える可能性もあるのでインターフェイスをラップして使っています。

app/models/amazon_client.rb
class AmazonClient
  def initialize(
      access_key_id:     ENV.fetch('AMAZON_ACCESS_KEY_ID'),
      associate_tag:     ENV.fetch('AMAZON_ASSOCIATE_TAG'),
      secret_access_key: ENV.fetch('AMAZON_SECRET_ACCESS_KEY')
    )

    @access_key_id = access_key_id
    @associate_tag = associate_tag
    @secret_access_key = secret_access_key
  end

  def search(query, page: 1)
    response =
      client.search_items(
        domain: 'co.jp',
        search_index: 'DVD',
        response_groups: ['Small', 'ItemAttributes', 'Images'],
        keywords: [query],
        item_page: page
      )

    SearchResult.new(response)
  end

  private

  def client
    @client ||= Rapa::Client.new(
      access_key_id: @access_key_id,
      associate_tag: @associate_tag,
      secret_access_key: @secret_access_key
    )
  end

  class SearchResult
    delegate_missing_to :@response

    def initialize(response)
      @response = response
    end

    def products
      @response.resources.map {|r| Product.new(r) }
    end

    class Product
      delegate_missing_to :@resource

      def initialize(resource)
        @resource = resource
      end

      def jan
        @resource.ean
      end

      def features
        @resource.feature
      end
    end
  end
end

バージョン

$ ruby -v
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux]

$ rails -v
Rails 5.1.4

$ bundle info rapa
The latest bundler is 1.16.0.pre.2, but you are currently running 1.15.4.
To update, run `gem install bundler --pre`
  * rapa (0.5.5)
    Summary: An API client library for Amazon Product Advertising API, written in Ruby.
    Homepage: https://github.com/r7kamura/rapa
    Path: /home/ttanimichi/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/rapa-0.5.5