ユニファ開発者ブログ

ユニファ株式会社システム開発部メンバーによるブログです。

SORACOM API を Sandbox 環境でテストする

皆様こんにちは、ユニファの赤沼です。

弊社では「ルクミー午睡チェック」というプロダクトで SORACOM Air Sim を使用しています。通常はセルラーモデルの iPad mini での通信用途に使っていて、API の活用はまだまだ十分にはできていないのですが、最近 Ruby のスクリプトから API を利用する機会があり、Sandbox 環境でのテストも行ったので書いてみます。

SORACOM のサービスは API が充実

まず改めてのおさらいですが、 SORACOM のサービスは IoT 用途ということもあり、 API が充実しています。サービスの数もかなり増えており、コンソールも使いやすいのですが、API でも大半のことはできるようになっているのではないでしょうか。ドキュメントもしっかり提供されています。

dev.soracom.io

全ての API についてリファレンスも提供されています。リクエスト時のパラメータの内容やレスポンスの各項目についてはもう少し説明があると良いなとは思いますが、実際に認証と各APIの実行までリファレンスページから行うことができるようになっているので、実装に組み込む前に実際のレスポンスなどが確認しやすくなっています。

dev.soracom.io

テスト用の Sandbox 環境

実際に動かして試せる API Reference はとても便利なのですが、一つ気を付けないといけないのは、 API Reference のページから実行した内容は、実際の環境に対して実行されるということです。情報取得系の API であれば問題ないのですが、何か変更を加えたり、Sim の解約等まで行えてしまいますので、軽い気持ちで試したりするとあとで大変なことになります。また、実装に組み込む際にも開発段階ではテストが必要になると思いますが、そういう時のために SORACOM には Sandbox 環境が用意されています。Sandbox 環境で実行した内容は本番環境には影響しませんので、解約処理等の危険な処理も試すことができます。

dev.soracom.io

本番アカウントも必要

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