diff --git a/CHANGELOG.md b/CHANGELOG.md index 50765542..d9b4aa13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,20 @@ # Change Log +## [4.59.0)](https://github.com/plivo/plivo-go/tree/v4.59.0) (2024-05-31) +**Feature - SubAccount and GeoMatch** +- Added sub_account and geo_match support + ## [4.58.0](https://github.com/plivo/plivo-ruby/tree/v4.58.0) (2023-05-17) **Feature - Adding support for location whatsapp messages** - Added new param `location` to [send message API](https://www.plivo.com/docs/sms/api/message#send-a-message) to support location `whatsapp` messages - Added new param `location` in templates to support location based templated messages +- ## [4.57.0](https://github.com/plivo/plivo-ruby/tree/v4.57.0) (2023-05-07) **Feature - Adding support for interactive whatsapp messages** - Added new param `interactive` to [send message API](https://www.plivo.com/docs/sms/api/message#send-a-message) to support interactive `whatsapp` messages + ## [4.56.0](https://github.com/plivo/plivo-ruby/tree/v4.56.0) (2023-04-18) **Feature - Support for dynamic button components when sending a templated WhatsApp message** - Added new param `payload` in templates to support dynamic payload in templates diff --git a/README.md b/README.md index 9930fdf5..bafa7c28 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ The Plivo Ruby SDK makes it simpler to integrate communications into your Ruby a Add this line to your application's Gemfile: ```ruby -gem 'plivo', '>= 4.58.0' +gem 'plivo', '>= 4.59.0' ``` And then execute: diff --git a/lib/plivo/base.rb b/lib/plivo/base.rb index 6bc2710e..7ef52b79 100644 --- a/lib/plivo/base.rb +++ b/lib/plivo/base.rb @@ -14,4 +14,4 @@ module Base PHLO_API_URL = 'https://phlorunner.plivo.com'.freeze LOOKUP_API_URL = 'https://lookup.plivo.com'.freeze end -end +end \ No newline at end of file diff --git a/lib/plivo/base/resource.rb b/lib/plivo/base/resource.rb index 7bcfd2aa..4fbd37ac 100644 --- a/lib/plivo/base/resource.rb +++ b/lib/plivo/base/resource.rb @@ -51,10 +51,39 @@ def parse_and_set(resource_json) @id = resource_json[@_identifier_string] end + def set_instance_variables(hash) + hash.each do |k, v| + instance_var_name = "@#{k}" + + if v.is_a?(Hash) + instance_variable_set(instance_var_name, v) + self.class.send(:attr_reader, k.to_sym) + v.each do |nested_k, nested_v| + instance_var_name = "@#{nested_k}" + instance_variable_set(instance_var_name, nested_v) + self.class.send(:attr_reader, nested_k.to_sym) + end + else + instance_variable_set(instance_var_name, v) + self.class.send(:attr_reader, k.to_sym) + end + end + end + + def parse_and_set_response(resource_json) + return unless resource_json.is_a?(Hash) + + set_instance_variables(resource_json) + + if @_identifier_string && resource_json.key?(@_identifier_string) + @id = resource_json[@_identifier_string] + end + end + def perform_update(params, use_multipart_conn = false) unless @id raise_invalid_request("Cannot update a #{@_name} resource "\ - 'without an identifier') + 'without an identifier') end response_json = @_client.send_request(@_resource_uri, 'POST', params, nil, use_multipart_conn, is_voice_request: @_is_voice_request) @@ -64,6 +93,18 @@ def perform_update(params, use_multipart_conn = false) self end + def perform_masking_update(params, use_multipart_conn = false) + unless @id + raise_invalid_request("Cannot update a #{@_name} resource "\ + 'without an identifier') + end + + response_json = @_client.send_request(@_resource_uri, 'POST', params, nil, use_multipart_conn, is_voice_request: @_is_voice_request) + parse_and_set(params) + parse_and_set_response(response_json) + self + end + def perform_action(action = nil, method = 'GET', params = nil, parse = false) resource_path = action ? @_resource_uri + action + '/' : @_resource_uri response = @_client.send_request(resource_path, method, params,nil,false,is_voice_request: @_is_voice_request) @@ -71,7 +112,7 @@ def perform_action(action = nil, method = 'GET', params = nil, parse = false) method == 'POST' ? parse_and_set(params) : self self end - + def perform_custome_action(action = nil, method = 'GET', params = nil, parse = false) resource_path = action ? @_resource_uri + action + '/' : @_resource_uri response = @_client.send_request(resource_path, method, params,nil,false,is_voice_request: @_is_voice_request) @@ -101,7 +142,7 @@ def perform_custom_action_apiresponse(action = nil, method = 'GET', params = nil def perform_delete(params=nil) unless @id raise_invalid_request("Cannot delete a #{@_name} resource "\ - 'without an identifier') + 'without an identifier') end Response.new(@_client.send_request(@_resource_uri, 'DELETE', params, nil, false, is_voice_request: @_is_voice_request), @@ -145,4 +186,4 @@ def configure_secondary_resource_uri end end end -end +end \ No newline at end of file diff --git a/lib/plivo/base/resource_interface.rb b/lib/plivo/base/resource_interface.rb index 5b21313c..1f9d88fc 100644 --- a/lib/plivo/base/resource_interface.rb +++ b/lib/plivo/base/resource_interface.rb @@ -46,6 +46,14 @@ def perform_get(identifier, params = nil) @_resource_type.new(@_client, resource_json: response_json) end + def perform_get_with_response(identifier, params = nil) + valid_param?(:identifier, identifier, [String, Symbol], true) + response_json = @_client.send_request(@_resource_uri + identifier.to_s + '/', 'GET', params, nil, false, is_voice_request: @_is_voice_request) + resource_json = response_json["response"] + # Pass the parsed JSON to initialize the resource object + @_resource_type.new(@_client, resource_json: resource_json) + end + def perform_get_without_identifier(params) valid_param?(:params, params, Hash, true) response_json = @_client.send_request(@_resource_uri, 'GET', params, nil, false, is_voice_request: @_is_voice_request) @@ -98,6 +106,10 @@ def perform_list(params = nil) } end + def perform_list_with_response(params = nil) + @_client.send_request(@_resource_uri, 'GET', params, nil, false, is_voice_request: @_is_voice_request) + end + def perform_action(action = nil, method = 'GET', params = nil, parse = false) resource_path = action ? @_resource_uri + action + '/' : @_resource_uri response = @_client.send_request(resource_path, method, params, nil, false, is_voice_request: @_is_voice_request) diff --git a/lib/plivo/base_client.rb b/lib/plivo/base_client.rb index 764dbbc5..50673731 100644 --- a/lib/plivo/base_client.rb +++ b/lib/plivo/base_client.rb @@ -343,41 +343,41 @@ def send_delete(resource_path, data, timeout, options = nil) def handle_response_exceptions(response) exception_mapping = { - 400 => [ - Exceptions::ValidationError, - 'A parameter is missing or is invalid while accessing resource' - ], - 401 => [ - Exceptions::AuthenticationError, - 'Failed to authenticate while accessing resource' - ], - 404 => [ - Exceptions::ResourceNotFoundError, - 'Resource not found' - ], - 405 => [ - Exceptions::InvalidRequestError, - 'HTTP method used is not allowed to access resource' - ], - 409 => [ - Exceptions::InvalidRequestError, - 'Conflict' - ], - 422 => [ - Exceptions::InvalidRequestError, - 'Unprocessable Entity' - ], - 500 => [ - Exceptions::PlivoServerError, - 'A server error occurred while accessing resource' - ] + 400 => [ + Exceptions::ValidationError, + 'A parameter is missing or is invalid while accessing resource' + ], + 401 => [ + Exceptions::AuthenticationError, + 'Failed to authenticate while accessing resource' + ], + 404 => [ + Exceptions::ResourceNotFoundError, + 'Resource not found' + ], + 405 => [ + Exceptions::InvalidRequestError, + 'HTTP method used is not allowed to access resource' + ], + 409 => [ + Exceptions::InvalidRequestError, + 'Conflict' + ], + 422 => [ + Exceptions::InvalidRequestError, + 'Unprocessable Entity' + ], + 500 => [ + Exceptions::PlivoServerError, + 'A server error occurred while accessing resource' + ] } response_json = response[:body] - return unless exception_mapping.key? response[:status] + return unless exception_mapping.key?(response[:status]) exception_now = exception_mapping[response[:status]] - error_message = if (response_json.is_a? Hash) && (response_json.key? 'error') + error_message = if response_json.is_a?(Hash) && response_json.key?('error') response_json['error'] else exception_now[1] + " at: #{response[:url]}" @@ -386,6 +386,10 @@ def handle_response_exceptions(response) error_message = error_message['error'] end + # Add api_id to the error message if present + if response_json.is_a?(Hash) && response_json.key?('api_id') + error_message += " (api_id: #{response_json['api_id']})" + end raise exception_now[0], error_message.to_s end end diff --git a/lib/plivo/resources.rb b/lib/plivo/resources.rb index bf655cc3..28d47c3b 100644 --- a/lib/plivo/resources.rb +++ b/lib/plivo/resources.rb @@ -9,6 +9,7 @@ require_relative 'resources/calls' require_relative 'resources/token' require_relative 'resources/endpoints' +require_relative 'resources/maskingsession' require_relative 'resources/addresses' require_relative 'resources/identities' require_relative 'resources/phlos' diff --git a/lib/plivo/resources/maskingsession.rb b/lib/plivo/resources/maskingsession.rb new file mode 100644 index 00000000..a8646ba8 --- /dev/null +++ b/lib/plivo/resources/maskingsession.rb @@ -0,0 +1,214 @@ +module Plivo + module Resources + include Plivo::Utils + class MaskingSession < Base::Resource + def initialize(client, options = nil) + @_name = 'Masking/Session' + @_identifier_string = 'session_uuid' + super + @_is_voice_request = true + end + + def update(options = nil) + return if options.nil? + valid_param?(:options, options, Hash, true) + + params = {} + params_expected = %i[session_expiry call_time_limit record record_file_format recording_callback_url + callback_url callback_method ring_timeout first_party_play_url second_party_play_url recording_callback_method + subaccount geomatch] + params_expected.each do |param| + if options.key?(param) && valid_param?(param, options[param], [String, Symbol, TrueClass, FalseClass], true) + params[param] = options[param] + end + end + + updated_session = perform_masking_update(params) + session_data = updated_session.instance_variables.map do |var| + [var[1..-1].to_sym, updated_session.instance_variable_get(var)] + end.to_h + + relevant_keys = %i[api_id message session] + filtered_session_data = session_data.select { |key, _| relevant_keys.include?(key) } + + if filtered_session_data[:session] + session_instance = filtered_session_data[:session] + session_data = session_instance.map do |key, value| + [key.to_sym, value] + end.to_h + + # Extract relevant keys from session + session_relevant_keys = %i[first_party second_party virtual_number status initiate_call_to_first_party session_uuid callback_url callback_method created_time + modified_time expiry_time duration amount call_time_limit ring_timeout first_party_play_url second_party_play_url record record_file_format recording_callback_url + recording_callback_method interaction total_call_amount total_call_count total_call_billed_duration total_session_amount last_interaction_time unknown_caller_play + is_pin_authentication_required generate_pin generate_pin_length first_party_pin second_party_pin pin_prompt_play pin_retry pin_retry_wait incorrect_pin_play] + + filtered_session_data[:session] = session_data.select { |key, _| session_relevant_keys.include?(key) } + end + + filtered_session_data + end + + def delete + perform_delete + end + + def to_s + { + first_party: @first_party, + second_party: @second_party, + virtual_number: @virtual_number, + status: @status, + initiate_call_to_first_party: @initiate_call_to_first_party, + session_uuid: @session_uuid, + callback_url: @callback_url, + callback_method: @callback_method, + created_time: @created_time, + modified_time: @modified_time, + expiry_time: @expiry_time, + duration: @duration, + amount: @amount, + call_time_limit: @call_time_limit, + ring_timeout: @ring_timeout, + first_party_play_url: @first_party_play_url, + second_party_play_url: @second_party_play_url, + record: @record, + record_file_format: @record_file_format, + recording_callback_url: @recording_callback_url, + recording_callback_method: @recording_callback_method, + interaction: @interaction, + total_call_amount: @total_call_amount, + total_call_count: @total_call_count, + total_call_billed_duration: @total_call_billed_duration, + total_session_amount: @total_session_amount, + last_interaction_time: @last_interaction_time, + is_pin_authentication_required: @is_pin_authentication_required, + generate_pin: @generate_pin, + generate_pin_length: @generate_pin_length, + second_party_pin: @second_party_pin, + pin_prompt_play: @pin_prompt_play, + pin_retry: @pin_retry, + pin_retry_wait: @pin_retry_wait, + incorrect_pin_play: @incorrect_pin_play, + unknown_caller_play: @unknown_caller_play + }.to_s + end + end + + class MaskingSessionInterface < Base::ResourceInterface + def initialize(client, resource_list_json = nil) + @_name = 'Masking/Session' + @_resource_type = MaskingSession + @_identifier_string = 'session_uuid' + super + @_is_voice_request = true + end + + def get(session_uuid) + valid_param?(:session_uuid, session_uuid, [String, Symbol], true) + perform_get_with_response(session_uuid) + end + + def create(first_party:, second_party:, session_expiry: nil, call_time_limit: nil, record: nil, record_file_format: nil, + recording_callback_url: nil, initiate_call_to_first_party: nil, callback_url: nil, callback_method: nil, ring_timeout: nil, + first_party_play_url: nil, second_party_play_url: nil, recording_callback_method: nil, is_pin_authentication_required: nil, + generate_pin: nil, generate_pin_length: nil, first_party_pin: nil, second_party_pin: nil, pin_prompt_play: nil, pin_retry: nil, + pin_retry_wait: nil, incorrect_pin_play: nil, unknown_caller_play: nil, subaccount: nil, geomatch: nil) + + valid_param?(:first_party, first_party, [String, Symbol], true) + valid_param?(:second_party, second_party, [String, Symbol], true) + + params = {} + params[:first_party] = first_party + params[:second_party] = second_party + params[:session_expiry] = session_expiry unless session_expiry.nil? + params[:call_time_limit] = call_time_limit unless call_time_limit.nil? + params[:record] = record unless record.nil? + params[:record_file_format] = record_file_format unless record_file_format.nil? + params[:recording_callback_url] = recording_callback_url unless recording_callback_url.nil? + params[:initiate_call_to_first_party] = initiate_call_to_first_party unless initiate_call_to_first_party.nil? + params[:callback_url] = callback_url unless callback_url.nil? + params[:callback_method] = callback_method unless callback_method.nil? + params[:ring_timeout] = ring_timeout unless ring_timeout.nil? + params[:first_party_play_url] = first_party_play_url unless first_party_play_url.nil? + params[:second_party_play_url] = second_party_play_url unless second_party_play_url.nil? + params[:recording_callback_method] = recording_callback_method unless recording_callback_method.nil? + params[:is_pin_authentication_required] = is_pin_authentication_required unless is_pin_authentication_required.nil? + params[:generate_pin] = generate_pin unless generate_pin.nil? + params[:generate_pin_length] = generate_pin_length unless generate_pin_length.nil? + params[:first_party_pin] = first_party_pin unless first_party_pin.nil? + params[:second_party_pin] = second_party_pin unless second_party_pin.nil? + params[:pin_prompt_play] = pin_prompt_play unless pin_prompt_play.nil? + params[:pin_retry] = pin_retry unless pin_retry.nil? + params[:pin_retry_wait] = pin_retry_wait unless pin_retry_wait.nil? + params[:incorrect_pin_play] = incorrect_pin_play unless incorrect_pin_play.nil? + params[:unknown_caller_play] = unknown_caller_play unless unknown_caller_play.nil? + params[:subaccount] = subaccount unless subaccount.nil? + params[:geomatch] = geomatch unless geomatch.nil? + + perform_create(params) + end + + def list(options = nil) + return perform_list_with_response if options.nil? + valid_param?(:options, options, Hash, true) + + raise_invalid_request("Offset can't be negative") if options.key?(:offset) && options[:offset] < 0 + + if options.key?(:limit) && (options[:limit] > 20 || options[:limit] <= 0) + raise_invalid_request('The maximum number of results that can be '\ + "fetched is 20. limit can't be more than 20 or less than 1") + end + + params = %i[ + first_party + second_party + virtual_number + status + created_time + created_time__lt + created_time__gt + created_time__lte + created_time__gte + expiry_time + expiry_time__lt + expiry_time__gt + expiry_time__lte + expiry_time__gte + duration + duration__lt + duration__gt + duration__lte + duration__gte + limit + offset + subaccount + ].reduce({}) do |result_hash, param| + if options.key?(param) + if valid_param?(param, options[param], [String, Symbol], true) + result_hash[param] = options[param] + end + end + result_hash + end + + perform_list_with_response(params) + end + + def each + maskingsession_list = list + maskingsession_list[:objects].each { |maskingsession| yield maskingsession } + end + + def update(session_uuid, options = nil) + valid_param?(:session_uuid, session_uuid, [String, Symbol], true) + MaskingSession.new(@_client, resource_id: session_uuid).update(options) + end + + def delete(session_uuid) + valid_param?(:session_uuid, session_uuid, [String, Symbol], true) + MaskingSession.new(@_client, resource_id: session_uuid).delete + end + end + end +end diff --git a/lib/plivo/rest_client.rb b/lib/plivo/rest_client.rb index 1a73bd94..84fbc9da 100644 --- a/lib/plivo/rest_client.rb +++ b/lib/plivo/rest_client.rb @@ -21,6 +21,7 @@ class RestClient < BaseClient attr_reader :verify_session attr_reader :tollfree_verifications attr_reader :verify_caller_id + attr_reader :maskingsession def initialize(auth_id = nil, auth_token = nil, proxy_options = nil, timeout = 5) configure_base_uri @@ -56,6 +57,7 @@ def configure_interfaces @calls = Resources::CallInterface.new(self) @token = Resources::TokenInterface.new(self) @endpoints = Resources::EndpointInterface.new(self) + @maskingsession = Resources::MaskingSessionInterface.new(self) @applications = Resources::ApplicationInterface.new(self) @addresses = Resources::AddressInterface.new(self) @identities = Resources::IdentityInterface.new(self) diff --git a/lib/plivo/version.rb b/lib/plivo/version.rb index e12c03b4..09484f08 100644 --- a/lib/plivo/version.rb +++ b/lib/plivo/version.rb @@ -1,3 +1,3 @@ module Plivo - VERSION = "4.58.0".freeze + VERSION = "4.59.0".freeze end diff --git a/spec/resource_maskingsession_spec.rb b/spec/resource_maskingsession_spec.rb new file mode 100644 index 00000000..db88d529 --- /dev/null +++ b/spec/resource_maskingsession_spec.rb @@ -0,0 +1,84 @@ +require 'rspec' + +describe 'MaskingSession test' do + def to_json(masking_session) + { + first_party: masking_session.first_party, + second_party: masking_session.second_party, + virtual_number: masking_session.virtual_number, + status: masking_session.status, + initiate_call_to_first_party: masking_session.initiate_call_to_first_party, + session_uuid: masking_session.session_uuid, + callback_url: masking_session.callback_url, + callback_method: masking_session.callback_method, + created_time: masking_session.created_time, + modified_time: masking_session.modified_time, + expiry_time: masking_session.expiry_time, + duration: masking_session.duration, + amount: masking_session.amount, + call_time_limit: masking_session.call_time_limit, + ring_timeout: masking_session.ring_timeout, + first_party_play_url: masking_session.first_party_play_url, + second_party_play_url: masking_session.second_party_play_url, + record: masking_session.record, + record_file_format: masking_session.record_file_format, + recording_callback_url: masking_session.recording_callback_url, + recording_callback_method: masking_session.recording_callback_method, + interaction: masking_session.interaction, + total_call_amount: masking_session.total_call_amount, + total_call_count: masking_session.total_call_count, + total_call_billed_duration: masking_session.total_call_billed_duration, + total_session_amount: masking_session.total_session_amount, + last_interaction_time: masking_session.last_interaction_time, + is_pin_authentication_required: masking_session.is_pin_authentication_required, + generate_pin: masking_session.generate_pin, + generate_pin_length: masking_session.generate_pin_length, + second_party_pin: masking_session.second_party_pin, + pin_prompt_play: masking_session.pin_prompt_play, + pin_retry: masking_session.pin_retry, + pin_retry_wait: masking_session.pin_retry_wait, + incorrect_pin_play: masking_session.incorrect_pin_play, + unknown_caller_play: masking_session.unknown_caller_play + }.reject { |_, v| v.nil? }.to_json + end + + def to_json_update(masking_session) + { + api_id: masking_session.api_id, + message: masking_session.message + }.reject { |_, v| v.nil? }.to_json + end + + def to_json_create(masking_session) + { + api_id: masking_session.api_id, + session_uuid: masking_session.session_uuid, + virtual_number: masking_session.virtual_number, + message: masking_session.message, + session: masking_session.session + }.reject { |_, v| v.nil? }.to_json + end + + def to_json_list(list_object) + objects_json = list_object[:objects].map do |object| + obj = JSON.parse(to_json(object)) + obj.delete('api_id') + obj.reject { |_, v| v.nil? } + end + { + api_id: list_object[:api_id], + meta: list_object[:meta], + objects: objects_json + }.to_json + end + + it 'deletes the session_uuid' do + id = 'SAXXXXXXXXXXXXXXXXXX' + contents = '{}' + mock(204, JSON.parse(contents).reject { |_, v| v.nil? }) + @api.maskingsession.delete(id) + compare_requests(uri: '/v1/Account/MAXXXXXXXXXXXXXXXXXX/Masking/Session/' + id + '/', + method: 'DELETE', + data: nil) + end +end