diff --git a/lib/Mailosaur/analysis.rb b/lib/Mailosaur/analysis.rb index fa733dd..a56b313 100644 --- a/lib/Mailosaur/analysis.rb +++ b/lib/Mailosaur/analysis.rb @@ -24,7 +24,7 @@ def initialize(conn, handle_http_error) def spam(email) response = conn.get "api/analysis/spam/#{email}" @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::SpamAnalysisResult.new(model) end end diff --git a/lib/Mailosaur/devices.rb b/lib/Mailosaur/devices.rb new file mode 100644 index 0000000..64f47a1 --- /dev/null +++ b/lib/Mailosaur/devices.rb @@ -0,0 +1,83 @@ +module Mailosaur + class Devices + # + # Creates and initializes a new instance of the Devices class. + # @param client connection. + # + def initialize(conn, handle_http_error) + @conn = conn + @handle_http_error = handle_http_error + end + + # @return [Connection] the client connection. + attr_reader :conn + + # + # List all devices + # + # Returns a list of your virtual security devices. + # + # @return [DeviceListResult] operation results. + # + def list + response = conn.get 'api/devices' + @handle_http_error.call(response) unless response.status == 200 + model = JSON.parse(response.body) + Mailosaur::Models::DeviceListResult.new(model) + end + + # + # Create a device + # + # Creates a new virtual security device and returns it. + # + # @param device_create_options [DeviceCreateOptions] + # + # @return [Device] operation results. + # + def create(device_create_options) + response = conn.post 'api/devices', device_create_options.to_json + @handle_http_error.call(response) unless response.status == 200 + model = JSON.parse(response.body) + Mailosaur::Models::Device.new(model) + end + + # + # Retrieve OTP + # + # Retrieves the current one-time password for a saved device, or given base32-encoded shared secret. + # + # @param query [String] Either the unique identifier of the device, or a base32-encoded shared secret. + # + # @return [OtpResult] operation results. + # + def otp(query) + if query.include? '-' + response = conn.get "api/devices/#{query}/otp" + @handle_http_error.call(response) unless response.status == 200 + model = JSON.parse(response.body) + return Mailosaur::Models::OtpResult.new(model) + end + + options = Mailosaur::Models::DeviceCreateOptions.new + options.shared_secret = query + response = conn.post 'api/devices/otp', options.to_json + @handle_http_error.call(response) unless response.status == 200 + model = JSON.parse(response.body) + Mailosaur::Models::OtpResult.new(model) + end + + # + # Delete a device + # + # Permanently deletes a device. This operation cannot be undone. + # + # @param id [String] The identifier of the device to be deleted. + # + def delete(id) + response = conn.delete "api/devices/#{id}" + @handle_http_error.call(response) unless response.status == 204 + nil + end + end +end diff --git a/lib/Mailosaur/messages.rb b/lib/Mailosaur/messages.rb index 7676d1b..49edef1 100644 --- a/lib/Mailosaur/messages.rb +++ b/lib/Mailosaur/messages.rb @@ -51,7 +51,7 @@ def get(server, criteria, timeout: 10_000, received_after: DateTime.now - (1.0 / def get_by_id(id) response = conn.get "api/messages/#{id}" @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Message.new(model) end @@ -96,7 +96,7 @@ def list(server, page: nil, items_per_page: nil, received_after: nil) @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::MessageListResult.new(model) end @@ -151,7 +151,7 @@ def search(server, criteria, page: nil, items_per_page: nil, timeout: nil, recei @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) return Mailosaur::Models::MessageListResult.new(model) if timeout.to_i.zero? || !model['items'].empty? delay_pattern = (response.headers['x-ms-delay'] || '1000').split(',').map(&:to_i) @@ -186,7 +186,7 @@ def search(server, criteria, page: nil, items_per_page: nil, timeout: nil, recei def create(server, message_create_options) response = conn.post "api/messages?server=#{server}", message_create_options.to_json @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Message.new(model) end @@ -204,7 +204,7 @@ def create(server, message_create_options) def forward(id, message_forward_options) response = conn.post "api/messages/#{id}/forward", message_forward_options.to_json @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Message.new(model) end @@ -223,7 +223,7 @@ def forward(id, message_forward_options) def reply(id, message_reply_options) response = conn.post "api/messages/#{id}/reply", message_reply_options.to_json @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Message.new(model) end end diff --git a/lib/Mailosaur/models/device.rb b/lib/Mailosaur/models/device.rb new file mode 100644 index 0000000..5590b24 --- /dev/null +++ b/lib/Mailosaur/models/device.rb @@ -0,0 +1,16 @@ +module Mailosaur + module Models + class Device < BaseModel + def initialize(data = {}) + @id = data['id'] + @name = data['name'] + end + + # @return [String] Unique identifier for the device. + attr_accessor :id + + # @return [String] The name of the device. + attr_accessor :name + end + end +end diff --git a/lib/Mailosaur/models/device_create_options.rb b/lib/Mailosaur/models/device_create_options.rb new file mode 100644 index 0000000..e613ba2 --- /dev/null +++ b/lib/Mailosaur/models/device_create_options.rb @@ -0,0 +1,16 @@ +module Mailosaur + module Models + class DeviceCreateOptions < BaseModel + def initialize(data = {}) + @name = data['name'] + @shared_secret = data['shared_secret'] + end + + # @return [String] A name used to identify the device. + attr_accessor :name + + # @return [String] The base32-encoded shared secret for this device. + attr_accessor :shared_secret + end + end +end diff --git a/lib/Mailosaur/models/device_list_result.rb b/lib/Mailosaur/models/device_list_result.rb new file mode 100644 index 0000000..97979c2 --- /dev/null +++ b/lib/Mailosaur/models/device_list_result.rb @@ -0,0 +1,13 @@ +module Mailosaur + module Models + class DeviceListResult < BaseModel + def initialize(data = {}) + @items = [] + (data['items'] || []).each { |i| @items << Mailosaur::Models::Device.new(i) } + end + + # @return [Array] The individual devices forming the result. + attr_accessor :items + end + end +end diff --git a/lib/Mailosaur/models/otp_result.rb b/lib/Mailosaur/models/otp_result.rb new file mode 100644 index 0000000..4af6bb7 --- /dev/null +++ b/lib/Mailosaur/models/otp_result.rb @@ -0,0 +1,16 @@ +module Mailosaur + module Models + class OtpResult < BaseModel + def initialize(data = {}) + @code = data['code'] + @expires = data['expires'] + end + + # @return [String] The current one-time password. + attr_accessor :code + + # @return [String] The expiry date/time of the current one-time password. + attr_accessor :expires + end + end +end diff --git a/lib/Mailosaur/servers.rb b/lib/Mailosaur/servers.rb index e1c844d..d31fc20 100644 --- a/lib/Mailosaur/servers.rb +++ b/lib/Mailosaur/servers.rb @@ -23,7 +23,7 @@ def initialize(conn, handle_http_error) def list response = conn.get 'api/servers' @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::ServerListResult.new(model) end @@ -39,7 +39,7 @@ def list def create(server_create_options) response = conn.post 'api/servers', server_create_options.to_json @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Server.new(model) end @@ -56,7 +56,7 @@ def create(server_create_options) def get(id) response = conn.get "api/servers/#{id}" @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Server.new(model) end @@ -73,7 +73,7 @@ def get(id) def get_password(id) response = conn.get "api/servers/#{id}/password" @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) model['value'] end @@ -90,7 +90,7 @@ def get_password(id) def update(id, server) response = conn.put "api/servers/#{id}", server.to_json @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::Server.new(model) end @@ -110,7 +110,7 @@ def delete(id) def generate_email_address(server) host = ENV['MAILOSAUR_SMTP_HOST'] || 'mailosaur.net' - '%s@%s.%s' % [SecureRandom.hex(3), server, host] + format('%s@%s.%s', SecureRandom.hex(3), server, host) end end end diff --git a/lib/Mailosaur/usage.rb b/lib/Mailosaur/usage.rb index 698f0b9..24beceb 100644 --- a/lib/Mailosaur/usage.rb +++ b/lib/Mailosaur/usage.rb @@ -22,7 +22,7 @@ def initialize(conn, handle_http_error) def limits response = conn.get 'api/usage/limits' @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::UsageAccountLimits.new(model) end @@ -34,7 +34,7 @@ def limits def transactions response = conn.get 'api/usage/transactions' @handle_http_error.call(response) unless response.status == 200 - model = JSON.load(response.body) + model = JSON.parse(response.body) Mailosaur::Models::UsageTransactionListResult.new(model) end end diff --git a/lib/mailosaur.rb b/lib/mailosaur.rb index 91dc5f9..b4b7457 100644 --- a/lib/mailosaur.rb +++ b/lib/mailosaur.rb @@ -14,6 +14,7 @@ module Mailosaur autoload :Messages, 'Mailosaur/messages.rb' autoload :Servers, 'Mailosaur/servers.rb' autoload :Usage, 'Mailosaur/usage.rb' + autoload :Devices, 'Mailosaur/devices.rb' autoload :MailosaurError, 'Mailosaur/mailosaur_error.rb' module Models @@ -42,6 +43,10 @@ module Models autoload :UsageAccountLimit, 'Mailosaur/models/usage_account_limit.rb' autoload :UsageTransactionListResult, 'Mailosaur/models/usage_transaction_list_result.rb' autoload :UsageTransaction, 'Mailosaur/models/usage_transaction.rb' + autoload :Device, 'Mailosaur/models/device.rb' + autoload :DeviceListResult, 'Mailosaur/models/device_list_result.rb' + autoload :DeviceCreateOptions, 'Mailosaur/models/device_create_options.rb' + autoload :OtpResult, 'Mailosaur/models/otp_result.rb' autoload :BaseModel, 'Mailosaur/models/base_model.rb' end @@ -81,6 +86,11 @@ def usage @usage ||= Usage.new(connection, method(:handle_http_error)) end + # @return [Devices] usage + def devices + @devices ||= Devices.new(connection, method(:handle_http_error)) + end + private def connection diff --git a/test/devices_test.rb b/test/devices_test.rb new file mode 100644 index 0000000..8c51c54 --- /dev/null +++ b/test/devices_test.rb @@ -0,0 +1,47 @@ +require 'mailosaur' +require 'test/unit' +require 'shoulda/context' + +module Mailosaur + class DevicesTest < Test::Unit::TestCase + setup do + api_key = ENV['MAILOSAUR_API_KEY'] + base_url = ENV['MAILOSAUR_BASE_URL'] + + raise ArgumentError, 'Missing necessary environment variables - refer to README.md' if api_key.nil? + + @client = MailosaurClient.new(api_key, base_url) + end + + should 'perform CRUD operations' do + device_name = 'My test' + shared_secret = 'ONSWG4TFOQYTEMY=' + + # Create a new device + create_options = Mailosaur::Models::DeviceCreateOptions.new + create_options.name = device_name + create_options.shared_secret = shared_secret + + created_device = @client.devices.create(create_options) + assert_not_nil(created_device.id) + assert_equal(device_name, created_device.name) + + # Retrieve an otp via device ID + otp_result = @client.devices.otp(created_device.id) + assert_equal(6, otp_result.code.length) + + list_result = @client.devices.list + assert_equal(1, list_result.items.length) + @client.devices.delete(created_device.id) + list_result = @client.devices.list + assert_equal(0, list_result.items.length) + end + + should 'return OTP given via a shared secret' do + shared_secret = 'ONSWG4TFOQYTEMY=' + + otp_result = @client.devices.otp(shared_secret) + assert_equal(6, otp_result.code.length) + end + end +end