皆様こんにちは、ユニファの赤沼です。
弊社では「ルクミー午睡チェック」というプロダクトで SORACOM Air Sim を使用しています。通常はセルラーモデルの iPad mini での通信用途に使っていて、API の活用はまだまだ十分にはできていないのですが、最近 Ruby のスクリプトから API を利用する機会があり、Sandbox 環境でのテストも行ったので書いてみます。
SORACOM のサービスは API が充実
まず改めてのおさらいですが、 SORACOM のサービスは IoT 用途ということもあり、 API が充実しています。サービスの数もかなり増えており、コンソールも使いやすいのですが、API でも大半のことはできるようになっているのではないでしょうか。ドキュメントもしっかり提供されています。
全ての API についてリファレンスも提供されています。リクエスト時のパラメータの内容やレスポンスの各項目についてはもう少し説明があると良いなとは思いますが、実際に認証と各APIの実行までリファレンスページから行うことができるようになっているので、実装に組み込む前に実際のレスポンスなどが確認しやすくなっています。
テスト用の Sandbox 環境
実際に動かして試せる API Reference はとても便利なのですが、一つ気を付けないといけないのは、 API Reference のページから実行した内容は、実際の環境に対して実行されるということです。情報取得系の API であれば問題ないのですが、何か変更を加えたり、Sim の解約等まで行えてしまいますので、軽い気持ちで試したりするとあとで大変なことになります。また、実装に組み込む際にも開発段階ではテストが必要になると思いますが、そういう時のために SORACOM には Sandbox 環境が用意されています。Sandbox 環境で実行した内容は本番環境には影響しませんので、解約処理等の危険な処理も試すことができます。
本番アカウントも必要
Sandbox環境は本番環境とは別環境ではありますが、本番環境にもアカウントを持っている必要はあります。そのため本番環境のコンソールから SAM(SORACOM Access Management)ユーザを作って認証キーを生成しておく必要があります。SAMユーザがいて認証キーさえあれば良いので、権限は何も与えなくてもOKです。間違って本番環境に対して実行してしまっても影響がないように、テスト用に何も権限のない SAMユーザを作っておくのが良いかと思います。
Sandbox 環境での準備: オペレータ作成
Sandbox環境にはオペレータは用意されておらず、またGUIもないので、まずはオペレータを下記APIで作成する必要があります。
sandboxInitializeOperator API https://dev.soracom.io/jp/docs/api_sandbox/#!/Operator/sandboxInitializeOperator
Sandbox専用の API もリファレンスページから実行できますが、今回は Ruby で下記のようなコードを書いて実行しました。 init
メソッドの中で実際に API にリクエストを投げています。ちなみに使用するメールアドレスは過去に使ったものと重複するとエラーになりますので、作成時には新たなメールアドレスを使用する必要があります。これを実行すると Operator ID, API Key, API Token が取得できますので、以降の API 実行にはこれらを使用します。
#! /usr/bin/env ruby require 'httpclient' require 'json' require './errors' class SoracomSandbox attr_reader :api_base_url def initialize @api_base_url = 'https://api-sandbox.soracom.io/v1' @client = HTTPClient.new end def post(url:, body: {} , header: {}) res = @client.post(url, body: body, header: header) check_status(res) JSON.parse(res.body) end def check_status(res) return if HTTP::Status::successful? res.status res_json = JSON.parse(res.body) msg = "CODE: #{res_json['code']} MSG: #{res_json['message']}" raise SoracomApiError, msg end def init(email: nil, password: nil, auth_key_id: nil, auth_key: nil) request_body = { email: email, password: password, authKeyId: auth_key_id, authKey: auth_key, registerPaymentMethod: 'true' }.to_json res_json = post(url: "#{@api_base_url}/sandbox/init", body: request_body, header: { 'Content-Type' => 'application/json' }) @operator_id = res_json['operatorId'] @api_key = res_json['apiKey'] @token = res_json['token'] @request_header = { 'Content-Type' => 'application/json', 'X-Soracom-API-Key' => @api_key, 'X-Soracom-Token' => @token } return @operator_id, @api_key, @token end end
Sandbox環境での準備: Simデータ作成
続いてテストに使用する Sim のデータを作成します。実際に Sim を契約しなくてもいくらでも Sim を用意できるのが Sandbox 環境の良いところです。 Simデータを用意するには、架空のSimを作成してからそれを登録するという手順を踏みます。架空のSim作成には Sandbox 用の API が用意されています。
Sim作成: sandboxCreateSubscriber https://dev.soracom.io/jp/docs/api_sandbox/#!/Subscriber/sandboxCreateSubscriber
Simの登録は本番環境と同様の API で行います。
Sim登録: registerSubscriber https://dev.soracom.io/jp/docs/api/#!/Subscriber/registerSubscriber
先ほどのクラスに下記のようなメソッドを追加して実行します。
def create_sims(sim_count) sims = [] sim_count.times do res = @client.post("#{@api_base_url}/sandbox/subscribers/create", header: @request_header) res_json = JSON.parse(res.body) sims << { imsi: res_json['imsi'], msisdn: res_json['msisdn'], registration_secret: res_json['registrationSecret'] } end sims.each do |sim| request_body = { registrationSecret: sim.fetch(:registration_secret) }.to_json post(url: "#{@api_base_url}/subscribers/#{sim.fetch(:imsi)}/register", body: request_body, header: @request_header) end end
実際にはさらにオペレータ作成とSimデータの作成をラップするコードを用意して、それを毎回 Sandbox 利用時に実行しました。
#! /usr/bin/env ruby require 'optparse' require './soracom_sandbox' opt = OptionParser.new params = {} opt.on('-e EMAIL') {|v| params[:email] = v } opt.on('-i AUTH_KEY_ID') {|v| params[:auth_key_id] = v } opt.on('-k AUTH_KEY') {|v| params[:auth_key] = v } opt.on('-n NUM_OF_SUBSCRIBERS') {|v| params[:num] = v.to_i } opt.parse!(ARGV) sandbox = SoracomSandbox.new operator_id, api_key, token = sandbox.init( email: params[:email], password: 'superStrongP@ssw0rd', auth_key_id: params[:auth_key_id], auth_key: params[:auth_key] ) sandbox.create_sims(params[:num]) puts "#{operator_id},#{api_key},#{token}"
テストしたい API を実行する
ここまでで Sandbox 環境の準備ができましたので、あとは本番と同様の API を Sandbox 環境向けに実行すればテストをすることができます。 例えば Sim 情報の取得は下記のようなメソッドを書いて実行します。
def subscribers(status: [], limit: 100) parameter = "limit=#{limit}" if not status.empty? parameter += "&" parameter += URI.encode_www_form(status_filter: "#{status.join('|')}") end get(url: "#{@api_base_url}/subscribers?#{parameter}", header: @request_header) end
これで事前準備で登録された Sim の情報が分かりますので、 Activate なども実行してみることができます。
def activate_subscribers(imsis: []) subscribers = [] imsis.each do |imsi| url = "#{@api_base_url}/subscribers/#{imsi}/activate" subscribers << post(url: url, header: @request_header) end subscribers end
こちらもラップするコードを書きました。 API_KEY と TOKEN はオペレータ作成時に取得したものを使います。
#! /usr/bin/env ruby require 'csv' require 'optparse' require './soracom' opt = OptionParser.new params = { api_key: nil, token: nil, file: nil, production: false } opt.on('-f SUBSCRIBERS_CSV_FILE') {|v| params[:file] = v } opt.on('-a API_KEY') {|v| params[:api_key] = v } opt.on('-t TOKEN') {|v| params[:token] = v } opt.on('--production') {|v| params[:production] = true } opt.parse!(ARGV) soracom = Soracom.new(production: params[:production]) soracom.set_request_header(api_key: params[:api_key], token: params[:token]) subscribers = CSV.table(params[:file], { converters: nil }) imsis = subscribers[:imsi] puts soracom.activate_subscribers(imsis: imsis).to_json
activate_subscribers メソッドや subscribers メソッドは前述の SoracomSandbox とは別の、本番と同様の API をまとめたクラスに実装しましたので参考までに抜粋して載せておきます。
#! /usr/bin/env ruby require 'httpclient' require 'json' require 'uri' require './errors' API_BASE_URL = 'https://api.soracom.io/v1' API_BASE_URL_SANDBOX = 'https://api-sandbox.soracom.io/v1' class Soracom def initialize( production: false ) @production = production @client = HTTPClient.new @api_base_url = production ? API_BASE_URL : API_BASE_URL_SANDBOX end def production? @production end def set_request_header(api_key:, token:) @request_header = { 'Content-Type' => 'application/json', 'X-Soracom-API-Key' => api_key, 'X-Soracom-Token' => token } end def post(url:, body: {} , header: {}) res = @client.post(url, body: body, header: header) check_status(res) JSON.parse(res.body) end def get(url:, header:) res = @client.get(url, header: header) check_status(res) JSON.parse(res.body) end def check_status(res) return if HTTP::Status::successful? res.status res_json = JSON.parse(res.body) msg = "CODE: #{res_json['code']} MSG: #{res_json['message']}" raise SoracomApiError, msg end def subscribers(status: [], limit: 100) parameter = "limit=#{limit}" if not status.empty? parameter += "&" parameter += URI.encode_www_form(status_filter: "#{status.join('|')}") end get(url: "#{@api_base_url}/subscribers?#{parameter}", header: @request_header) end def activate_subscribers(imsis: []) subscribers = [] imsis.each do |imsi| url = "#{@api_base_url}/subscribers/#{imsi}/activate" subscribers << post(url: url, header: @request_header) end subscribers end end
まとめ
SORACOM のサービスの魅力は単に Sim での通信というだけでなく、関連する様々なサービスや API を活用することで、よりコスト効率を良くしたり、プロダクト提供までの実装コストの削減や運用時の柔軟性をあげることができるというところかと思います。 API を活用していくためにも、 Sandbox 環境でしっかりテストして安全に利用したいものです。
また弊社では複数ポジションを募集中ですので、興味ある方は是非ご連絡ください! unifa-e.com