追記:EC2上でSDKを利用する場合は、IAMロールを利用しましょう。
RubyでAWS SDK(aws/aws-sdk-ruby)を使うときに、
そのままアプリケーションのコードに組み込んで使う人はそんなにいないと思う。
SDKのコードを直接組み込むと、ユニットテストしづらくなるし設定用のコードが各所に散らばることになる。
なので、簡単なラッパー&クライアントのファクトリクラスを書くと良い感じに手軽にコードも書けるし
テストも書けるという望ましい状態になるかと思います。
今回は、SDK使う際に自分が書いた簡単なコードを紹介します。
注:AWS SDKはもうすぐv2がリリースされそうな感じですがここではv1のコードを元に記事を書いています。
まず、便利concern。これをファクトリクラスにincludeして使う。
そうすると、config/initializers/aws.rbで使う用の.configureメソッドと、
設定済みクライアントの取得ができる.clientメソッドが生えてくる。
実装が若干おまじないっぽい。
module MyApp::AWS::Configurable
extend ActiveSupport::Concern
def client
self.class.client
end
included do
class << self
# cache client object until re-configuring
def client
@client ||= service_class.new(configuration.to_h)
end
def configure(&block)
# remove client cache at first
clear_client_cache!
options = Options.new(AWS::Core::Configuration.accepted_options)
yield options
@aws_core_configuration = AWS::Core::Configuration.new(aws_options.to_h)
end
def configuration
stub_configuration || @aws_core_configuration
end
def stub_configuration
# override at your spec_helper
end
def clear_client_cache!
@client = nil
end
protected
def service_class
matched = self.to_s.match(/\AMyApp\:\:([a-zA-Z_\:]+)/).captures[0]
(matched + '::Client').constantize
end
end
end
class Options < BasicObject
def initialize(accepted_options)
@accepted_options = accepted_options
@config = {}
end
def to_h
@config
end
def method_missing(method, *args)
return super unless setter?(method.to_s)
name = extract_name(method.to_s)
return super unless accept?(name)
@config[name] = args[0]
end
def setter?(method)
method =~ /\A\w*=\z/
end
def extract_name(method)
method.match(/\A(\w*)=\z/).captures[0].to_sym
end
def accept?(name)
@accepted_options.include?(name)
end
end
end
ファクトリクラスの定義
module MyApp
module AWS
class SNS
include MyApp::AWS::Configurable
# 以下に必要なコードがあれば書いてく
end
end
end
初期化処理。よくあるアクセスキーの設定がアプリケーションのコードから分離できる
MyApp::AWS::SNS.configure do |conf|
conf.access_key_id = Rails.application.secrets.aws_access_key_id
conf.secret_access_key = Rails.application.secrets.aws_secret_access_key
conf.region = Rails.application.secrets.aws_region_sns
end
ファクトリクラスの使い方。ファクトリクラスのclientメソッドから
初期化済みのクライアントのインスタンスが取得できるので、設定処理が散らかることがない。
これを元にラッパークラスを書いていく。
class MyApp::Hoge
def create(xxx)
sns = MyApp::AWS::SNS.client
sns.create_platform_endpoint(xxx)
end
end
テストを書くときは、AWS::Core::Configurationの設定にstub_requestsをtrueで渡してやる。
すると、requestがstubのresponseを返すようになる。
しかしこのままだと、空のAWS::Core::Responseが返ってくるのみ。
module MyApp
module AWS
class SNS
def self.stub_configuration
AWS::Core::Configuration.new(
access_key_id: 'ACCESS_KEY_ID',
secret_access_key: 'SECRET_ACCESS_KEY',
stub_requests: true,
)
end
end
end
end
このようにstub_forでレスポンスの値を書くと実際のレスポンスを好きな値にすることができる。
ただし、SDKだけではモック化には対応してない。
参考: Stubbing AWS Responses - AWS Developer Blog - Ruby
before do
sns = MyApp::AWS::SNS.client
resp = sns.stub_for(:create_platform_endpoint)
resp.data[:endpoint_arn] = 'arn:aws....'
end
after do
MyApp::AWS::SNS.clear_client_cache!
end
it 'xxxx xxx xxx' do
resp = MyApp::Hoge.create(xxx)
expect(resp[:endpoint_arn]).to eq('arn:aws....')
end
こんな感じでサクッとテストも書けるようになりました。便利。