HitoHana(ひとはな)の舞台裏

HitoHana(ひとはな)運営の舞台裏をご紹介いたします!

RMSのAPIをRubyで実装したい 2/3

前回のブログより

新人の田中です。この記事ではRMSの受注APIをRailsのアプリケーションに導入した事例を紹介しています。
前回は動作確認まででしたが、今回からは実際にアプリケーションに取り込むためのコードを書いていきたいと思います。

backstage.hitohana.tokyo

今回の目標

今回の目標はライブラリ層の実装です。前回の記事にも記載しましたが、全体の設計上は3階層に分けて考えています。

  • モデル層(業務ロジック部分)
  • サービス層(業務ロジックとRMSのAPIをつなぐ部分)
  • ライブラリ層(RMSのAPIをRubyでも使用できるようにした部分)

気をつけたこと

ライブラリ層はAPIをRubyで使えるようにするだけなので、インターフェースの加工はRubyで使いやすいようにするだけの最小限に抑えます。

ひとはなのアプリケーションで使いやすいようにインターフェースを加工するのはサービス層で行います。ライブラリ層ではなるべく本家のAPIの仕様書に沿った形のインターフェースで作成することにしました。

とはいえレスポンスがHashのままだと扱いにくいので、Hashie::Mashを使用してレスポンスの各データにはドットノーテーションでアクセスできるようにしました。

github.com

実装

の前に

最終的にどんなコードでAPIを呼び出すことにするのか考えました。こんな感じにできるように実装していこうと思います。

args = {
  is_order_number_only_flag: false,
  order_number: ['楽天の注文番号'],
}

response = Rms::Order.get_order(args)
# => #<Hashie::Mash>

ope_response = response.get_order_response.return
# => #<Hashie::Mash>

ope_response.error_code
# => 'N00-000'

ope_response.message
# => '正常終了'

rakuten_order_model = ope_response.order_model
# => #<Hashie::Mash> ※注文情報が入っている

初期設定

初期設定関連はよくあるものを使用したので詳細は割愛します。以下のように設定してあとから呼び出せるようにしています。

Rms.configure do |config|
  config.service_secret = 'WEB API契約時に配布されるサービスシークレット'
  config.license_key    = 'WEB API契約時に配布されるライセンスキー(有効期限付き)'
  config.shop_url       = '楽天の店舗URL'
  config.user_name      = 'なんでもよい'
end

# Rms.config.service_secret の形で呼び出せるように実装しています(詳細は割愛)

認証

APIを呼び出すごとに何回も認証キーを計算しなくてよいように、認証キーはシングルトンクラスに作らせるようにしました。

require 'singleton'
require 'base64'

module Rms
  class Auth
    include Singleton

    attr_reader :key

    class << self
      def key
        instance.key
      end
    end

    def initialize
      @key = "ESA #{Base64.strict_encode64(Rms.config.service_secret + ':' + Rms.config.license_key)}"
    end
  end
end

Soapクライアント

本家RMSの受注APIのページを見てみると、メソッドはたくさんあるけど基本的なつくりは同じようだったのでSoapクライアントをひとつ作って、各APIはそれを使ってリクエストをするという構成にしました。

require 'savon'
require 'singleton'
require 'hashie/mash'

module Rms
  class Order
    class SoapClient
      include Singleton

      WSDL = 'https://api.rms.rakuten.co.jp/es/1.0/order/ws?WSDL'.freeze

      class << self
        def call(operation, args)
          instance.call(operation, args)
        end
      end

      def initialize
        @client = Savon.client(wsdl: WSDL)
      end

      def call(operation, args)
        response = @client.call(operation, message: message(args))
        Hashie::Mash.new(response.body)
      end

      private

      def message(args)
        auth_params.merge(arg1: args)
      end

      def auth_params
        {
          arg0: {
            auth_key: Rms::Auth.key,
            shop_url: Rms.config.shop_url,
            user_name: Rms.config.user_name
          }
        }
      end
    end
  end
end

各メソッド

各メソッドはSoapクライアントを呼び出すだけで中身は変わらないのでMethod Missingを使って実装してみました。 define_methodで一括して全てのメソッドを作成しました。

module Rms
  class Order
    API_METHODS = %i(
      get_order
      update_order
      cancel_order
      change_status
      decision_point
      r_bank_account_transfer
      change_r_bank_to_unprocessing
      do_enclosure
      do_un_enclosure
      change_enclosure_parent
      get_enclosure_list
      get_request_id
      get_result
    ).freeze

    class << self
      API_METHODS.each do |api_method|
        define_method api_method do |args = nil|
          SoapClient.call(api_method, args)
        end
      end
    end
  end
end

できました!実際にはじめのテストコードの通りに動いてくれました!

次回は

今回の記事ではRMS受注APIのRubyのラッパーを作成しました。次回は最後にひとはなの業務ロジックと今回実装したAPIとをつなぐサービス層を実装していきたいと思います。

おわりに

HitoHana(ひとはな)では、仕事が大好きで個性的なエンジニアを大募集しています!

www.wantedly.com