MetaMindのRuby Clientが無かったので作っちゃいました。
- metamind | RubyGems.org | your community gem host
- tzmfreedom/metamind-ruby: API Client for Salesforce MetaMind
インストール&使い方
gem "metamind"
でインストールして、以下のようにして使います
require 'metamind'
# client = MetaMind::Client.new(cert: '{certificate path',
# password: '{certificate password}',
# email: '{emai for metamind account}')
client = MetaMind::Client.new(private_key: '{private key path}',
password: '{private key password}',
email: '{email for metamind account}')
client.get_all_datasets
certかprivate_keyどちらかのキーは必須。
苦労したところとか
MetaMindはファイルアップロード以外のAPIでもContent-Typeをmultipart/form-dataの指定をしなければならないのが面倒でした。net/httpを利用するとなると簡単に対応できそうなライブラリがなかったので、以下のように手作り感のあるmultipart/form-dataで対応しました。Fileの方は間違ってるかも(素直にBase64とかにした方が良かったですかね)
def build_multipart_query params
parts = []
params.each do |k, v|
lines = []
if v.is_a?(File)
lines << "--#{@boundary}"
lines << %Q{Content-Disposition: attachment; name="#{k}"}
lines << "Content-type: image/#{File.extname(v)[1..-1]}"
lines << "Content-Transfer-Encoding: binary"
lines << ""
lines << v.read
else
lines << "--#{@boundary}"
lines << %Q{Content-Disposition: form-data; name="#{k}"}
lines << ""
lines << v
end
parts << lines.join(CRLF)
end
parts.join(CRLF) + "#{CRLF}--#{@boundary}--"
end
Hashをクエリパラメータにする方法はActiveSupportのHash#to_queryを使うのがセオリーになっているっぽいですが、そのためだけにActiveSupportをインストールするのは微妙かな、と思ったのでcore_extのHashの特定部分だけ少し修正して使っています。オープンクラス便利だなー
class Object
def to_query(key)
"#{CGI.escape(key.to_s)}=#{CGI.escape(self.to_s)}"
end
end
class Hash
def to_query(namespace = nil)
collect do |key, value|
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
end
end.compact * "&"
end
end
HTTPリクエストのテストをする際のスタブはWebMockが使いやすいです
stub_request(:post, 'https://api.metamind.io/v1/oauth2/token')
.to_return(body: { access_token: 'ACCESS_TOKEN_EXAMPLE'}.to_json,
status: 200,
headers: {'Content-Type' => 'application/json'})
# 何かしらのAPIコール
expect(WebMock).to have_requested(:post, 'https://api.metamind.io/v1/oauth2/token').
with(body: { grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion: jwt }.to_query,
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' })
ただし、WebMockはmultipart/form-dataに対するbodyのアサーションは対応していないようで、アサーションをかけようとすると以下のエラーが発生します。
WebMock does not support matching body
for multipart/form-data requests yet
JWT生成のときにTime.nowを使っているため、テストコードではTimecopを利用。以下のように書くと、ブロック内のTime.now等の値が固定される。
Timecop.freeze(Time.now) do
# ...
end
JWT生成はjwtライブラリが楽です。MetamindのAPIの認証方式はOAuth2.0のJWT bearer token flowになので、そこで使っています。
jwt = JWT.encode({
iss: 'developer.force.com',
sub: @email,
aud: 'https://api.metamind.io/v1/oauth2/token',
iat: Time.now.to_i,
exp: Time.now.to_i + 3600
}, @private_key, 'RS256')
Rubygemの作り方やgemspecの書き方は以下のサイトが詳しいです
- RubyGemはめっちゃ簡単に作れる! - 酒と泪とRubyとRailsと
- Specification Reference - RubyGems Guides
- RubyGemを作る時の注意 - Qiita
- rubygemsに登録するときにはspec.metadata[‘allowed_push_host’]あたりを消しておこう - Qiita
metamind 0.1.0 built to pkg/metamind-0.1.0.gem.
Tagged v0.1.0.
Untagging v0.1.0 due to error.
rake aborted!
Couldn't git push. `git push ' failed with the following output:
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin master