messagemedia_lookups_sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +42 -0
- data/lib/message_media_lookups.rb +32 -0
- data/lib/message_media_lookups/api_helper.rb +259 -0
- data/lib/message_media_lookups/configuration.rb +35 -0
- data/lib/message_media_lookups/controllers/base_controller.rb +63 -0
- data/lib/message_media_lookups/controllers/lookups_controller.rb +121 -0
- data/lib/message_media_lookups/exceptions/api_exception.rb +16 -0
- data/lib/message_media_lookups/http/auth/basic_auth.rb +21 -0
- data/lib/message_media_lookups/http/auth/hmac_auth.rb +52 -0
- data/lib/message_media_lookups/http/faraday_client.rb +54 -0
- data/lib/message_media_lookups/http/http_call_back.rb +20 -0
- data/lib/message_media_lookups/http/http_client.rb +90 -0
- data/lib/message_media_lookups/http/http_context.rb +16 -0
- data/lib/message_media_lookups/http/http_method_enum.rb +9 -0
- data/lib/message_media_lookups/http/http_request.rb +46 -0
- data/lib/message_media_lookups/http/http_response.rb +19 -0
- data/lib/message_media_lookups/message_media_lookups_client.rb +28 -0
- data/lib/message_media_lookups/models/base_model.rb +32 -0
- data/lib/message_media_lookups/models/lookup_a_phone_number_response.rb +58 -0
- data/test/controllers/controller_test_base.rb +31 -0
- data/test/controllers/test_lookups_controller.rb +58 -0
- data/test/http_response_catcher.rb +12 -0
- data/test/test_helper.rb +90 -0
- metadata +158 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
|
4
|
+
Logging.logger.root.appenders = Logging.appenders.stdout
|
5
|
+
Logging.logger.root.level = :info
|
6
|
+
|
7
|
+
# All configuration including auth info and base URI for the API access
|
8
|
+
# are configured in this class.
|
9
|
+
class Configuration
|
10
|
+
# The base Uri for API calls
|
11
|
+
@base_uri = 'https://api.messagemedia.com'
|
12
|
+
|
13
|
+
# The username to use with basic authentication
|
14
|
+
@basic_auth_user_name = 'TODO: Replace'
|
15
|
+
|
16
|
+
# The password to use with basic authentication
|
17
|
+
@basic_auth_password = 'TODO: Replace'
|
18
|
+
|
19
|
+
# The username to use with HMAC authentication
|
20
|
+
@hmac_auth_user_name = nil
|
21
|
+
|
22
|
+
# The password to use with HMAC authentication
|
23
|
+
@hmac_auth_password = nil
|
24
|
+
|
25
|
+
# The attribute accessors for public properties.
|
26
|
+
class << self
|
27
|
+
attr_accessor :array_serialization
|
28
|
+
attr_accessor :base_uri
|
29
|
+
attr_accessor :basic_auth_user_name
|
30
|
+
attr_accessor :basic_auth_password
|
31
|
+
attr_accessor :hmac_auth_user_name
|
32
|
+
attr_accessor :hmac_auth_password
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
# Base controller.
|
4
|
+
class BaseController
|
5
|
+
attr_accessor :http_client, :http_call_back
|
6
|
+
|
7
|
+
def initialize(http_client: nil, http_call_back: nil)
|
8
|
+
@http_client = http_client || FaradayClient.new
|
9
|
+
@http_call_back = http_call_back
|
10
|
+
|
11
|
+
@global_headers = {
|
12
|
+
'user-agent' => 'messagemedia-lookups-ruby-sdk-1.0.0'
|
13
|
+
}
|
14
|
+
@logger = Logging.logger[self]
|
15
|
+
@logger.info("Instantiated controller class.")
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_parameters(args)
|
19
|
+
args.each do |_name, value|
|
20
|
+
if value.nil?
|
21
|
+
raise ArgumentError, "Required parameter #{_name} cannot be nil."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def apply_authentication(request, url, body=nil)
|
27
|
+
if Configuration.hmac_auth_user_name == nil or Configuration.hmac_auth_password == nil
|
28
|
+
BasicAuth.apply(request)
|
29
|
+
else
|
30
|
+
HmacAuth.apply(request, url, body)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute_request(request, binary: false, name: nil)
|
35
|
+
@logger.info("Calling the on_before_request method of http_call_back for #{name}.") if @http_call_back
|
36
|
+
@http_call_back.on_before_request(request) if @http_call_back
|
37
|
+
|
38
|
+
@logger.info("Merging global headers with endpoint headers for #{name}.")
|
39
|
+
APIHelper.clean_hash(request.headers)
|
40
|
+
request.headers = @global_headers.clone.merge(request.headers)
|
41
|
+
|
42
|
+
@logger.debug("Raw request for #{name} is: #{request.inspect}")
|
43
|
+
response = if binary
|
44
|
+
@http_client.execute_as_binary(request)
|
45
|
+
else
|
46
|
+
@http_client.execute_as_string(request)
|
47
|
+
end
|
48
|
+
@logger.debug("Raw response for #{name} is: #{response.inspect}")
|
49
|
+
@logger.info("Wrapping request and response in a context object for #{name}.")
|
50
|
+
context = HttpContext.new(request, response)
|
51
|
+
|
52
|
+
@logger.info("Calling on_after_response method of http_call_back for #{name}.") if @http_call_back
|
53
|
+
@http_call_back.on_after_response(context) if @http_call_back
|
54
|
+
|
55
|
+
context
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_response(context)
|
59
|
+
raise APIException.new 'HTTP Response Not OK', context unless
|
60
|
+
context.response.status_code.between?(200, 208) # [200,208] = HTTP OK
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
# LookupsController
|
4
|
+
class LookupsController < BaseController
|
5
|
+
@instance = LookupsController.new
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :instance
|
9
|
+
end
|
10
|
+
|
11
|
+
def instance
|
12
|
+
self.class.instance
|
13
|
+
end
|
14
|
+
|
15
|
+
# Use the Lookups API to find information about a phone number.
|
16
|
+
# A request to the lookups API has the following format:
|
17
|
+
# ```/v1/lookups/phone/{phone_number}?options={carrier,type}```
|
18
|
+
# The `{phone_number}` parameter is a required field and should be set to
|
19
|
+
# the phone number to be looked up.
|
20
|
+
# The options query parameter can also be used to request additional
|
21
|
+
# information about the phone number.
|
22
|
+
# By default, a request will only return the `country_code` and
|
23
|
+
# `phone_number` properties in the response.
|
24
|
+
# To request details about the the carrier, include `carrier` as a value of
|
25
|
+
# the options parameter.
|
26
|
+
# To request details about the type, include `type` as a value of the
|
27
|
+
# options parameter. To pass multiple values
|
28
|
+
# to the options parameter, use a comma separated list, i.e. `carrier,type`.
|
29
|
+
# A successful request to the lookups endpoint will return a response body
|
30
|
+
# as follows:
|
31
|
+
# ```json
|
32
|
+
# {
|
33
|
+
# "country_code": "AU",
|
34
|
+
# "phone_number": "+61491570156",
|
35
|
+
# "type": "mobile",
|
36
|
+
# "carrier": {
|
37
|
+
# "name": "Telstra"
|
38
|
+
# }
|
39
|
+
# }
|
40
|
+
# ```
|
41
|
+
# Each property in the response body is defined as follows:
|
42
|
+
# - ```country_code``` ISO ALPHA 2 country code of the phone number
|
43
|
+
# - ```phone_number``` E.164 formatted phone number
|
44
|
+
# - ```type``` The type of number. This can be ```"mobile"``` or
|
45
|
+
# ```"landline"```
|
46
|
+
# - ```carrier``` Holds information about the specific carrier (if
|
47
|
+
# available)
|
48
|
+
# - ```name``` The carrier's name as reported by the network
|
49
|
+
# @param [String] phone_number Required parameter: The phone number to be
|
50
|
+
# looked up
|
51
|
+
# @param [String] options Optional parameter: Example:
|
52
|
+
# @return LookupAPhoneNumberResponse response from the API call
|
53
|
+
def get_lookup_a_phone_number(phone_number,
|
54
|
+
options = nil)
|
55
|
+
begin
|
56
|
+
@logger.info("get_lookup_a_phone_number called.")
|
57
|
+
|
58
|
+
request_url = '/v1/lookups/phone/{phone_number}'
|
59
|
+
request_url = APIHelper.append_url_with_template_parameters(
|
60
|
+
request_url,
|
61
|
+
'phone_number' => phone_number
|
62
|
+
)
|
63
|
+
|
64
|
+
# Prepare query url.
|
65
|
+
@logger.info("Preparing query URL for get_lookup_a_phone_number.")
|
66
|
+
_query_builder = Configuration.base_uri.dup
|
67
|
+
_query_builder << '/v1/lookups/phone/{phone_number}'
|
68
|
+
_query_builder = APIHelper.append_url_with_template_parameters(
|
69
|
+
_query_builder,
|
70
|
+
'phone_number' => phone_number
|
71
|
+
)
|
72
|
+
_query_builder = APIHelper.append_url_with_query_parameters(
|
73
|
+
_query_builder,
|
74
|
+
{
|
75
|
+
'options' => options
|
76
|
+
},
|
77
|
+
array_serialization: Configuration.array_serialization
|
78
|
+
)
|
79
|
+
_query_url = APIHelper.clean_url _query_builder
|
80
|
+
|
81
|
+
# Prepare headers.
|
82
|
+
@logger.info("Preparing headers for get_lookup_a_phone_number.")
|
83
|
+
_headers = {
|
84
|
+
'accept' => 'application/json',
|
85
|
+
'user-agent' => 'messagemedia-lookups-ruby-sdk-1.1.0'
|
86
|
+
}
|
87
|
+
|
88
|
+
# Prepare and execute HttpRequest.
|
89
|
+
@logger.info('Preparing and executing HttpRequest for get_lookup_a_phone_number.')
|
90
|
+
_request = @http_client.get(
|
91
|
+
_query_url,
|
92
|
+
headers: _headers
|
93
|
+
)
|
94
|
+
|
95
|
+
apply_authentication(_request, request_url)
|
96
|
+
# BasicAuth.apply(_request)
|
97
|
+
|
98
|
+
_context = execute_request(_request, name: 'get_lookup_a_phone_number')
|
99
|
+
|
100
|
+
# Validate response against endpoint and global error codes.
|
101
|
+
@logger.info("Validating response for get_lookup_a_phone_number.")
|
102
|
+
if _context.response.status_code == 404
|
103
|
+
raise APIException.new(
|
104
|
+
'Number was invalid',
|
105
|
+
_context
|
106
|
+
)
|
107
|
+
end
|
108
|
+
validate_response(_context)
|
109
|
+
|
110
|
+
# Return appropriate response type.
|
111
|
+
@logger.info("Returning response for get_lookup_a_phone_number.")
|
112
|
+
decoded = APIHelper.json_deserialize(_context.response.raw_body)
|
113
|
+
LookupAPhoneNumberResponse.from_hash(decoded)
|
114
|
+
|
115
|
+
rescue Exception => e
|
116
|
+
@logger.error(e)
|
117
|
+
raise e
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
# Class for exceptions when there is a network error, status code error, etc.
|
4
|
+
class APIException < StandardError
|
5
|
+
attr_reader :context, :response_code
|
6
|
+
|
7
|
+
# The constructor.
|
8
|
+
# @param [String] The reason for raising an exception.
|
9
|
+
# @param [HttpContext] The HttpContext of the API call.
|
10
|
+
def initialize(reason, context)
|
11
|
+
super(reason)
|
12
|
+
@context = context
|
13
|
+
@response_code = context.response.status_code
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module MessageMediaLookups
|
5
|
+
# Utility class for basic authorization.
|
6
|
+
class BasicAuth
|
7
|
+
# Add basic authentication to the request.
|
8
|
+
# @param [HttpRequest] The HttpRequest object to which authentication will
|
9
|
+
# be added.
|
10
|
+
def self.apply(http_request)
|
11
|
+
username = Configuration.basic_auth_user_name
|
12
|
+
password = Configuration.basic_auth_password
|
13
|
+
if(username.length != 20 || password.length != 30)
|
14
|
+
puts("~~~~~ It appears as though your REST API Keys are invalid. Please check and make sure they are correct. (Invalid Length) ~~~~~")
|
15
|
+
end
|
16
|
+
value = Base64.strict_encode64("#{username}:#{password}")
|
17
|
+
header_value = "Basic #{value}"
|
18
|
+
http_request.headers['Authorization'] = header_value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module MessageMediaMessages
|
5
|
+
# Utility class for basic authorization.
|
6
|
+
class HmacAuth
|
7
|
+
def self.create_signature(date, content_signature, url, body=nil)
|
8
|
+
request_type = "GET"
|
9
|
+
|
10
|
+
if body != nil
|
11
|
+
request_type = "POST"
|
12
|
+
end
|
13
|
+
|
14
|
+
signing_string = "date: #{date}\n#{content_signature}#{request_type} #{url} HTTP/1.1"
|
15
|
+
hmac = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), Configuration.hmac_auth_password.encode("ASCII"), signing_string.encode("ASCII"))
|
16
|
+
|
17
|
+
return Base64.encode64(hmac).chomp
|
18
|
+
end
|
19
|
+
|
20
|
+
# Add basic authentication to the request.
|
21
|
+
# @param [HttpRequest] The HttpRequest object to which authentication will
|
22
|
+
# be added.
|
23
|
+
def self.apply(http_request, url=nil, body=nil)
|
24
|
+
username = Configuration.hmac_auth_user_name
|
25
|
+
|
26
|
+
content_signature = ""
|
27
|
+
content_header = ""
|
28
|
+
|
29
|
+
now = DateTime.now.new_offset(0).to_time.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
30
|
+
|
31
|
+
date_header = now
|
32
|
+
|
33
|
+
if body != nil
|
34
|
+
md5 = Digest::MD5.new
|
35
|
+
md5.update(body)
|
36
|
+
|
37
|
+
content_hash = md5.hexdigest
|
38
|
+
content_signature = "x-Content-MD5: #{content_hash}\n"
|
39
|
+
content_header = "x-Content-MD5 "
|
40
|
+
http_request.headers["x-Content-MD5"] = content_hash
|
41
|
+
end
|
42
|
+
|
43
|
+
http_request.headers["date"] = date_header
|
44
|
+
|
45
|
+
hmac_signature = HmacAuth.create_signature(date_header, content_signature, url, body)
|
46
|
+
|
47
|
+
joined = "username=\"#{username}\", algorithm=\"hmac-sha1\", headers=\"date #{content_header}request-line\", signature=\"#{hmac_signature}\""
|
48
|
+
header_value = "hmac #{joined}"
|
49
|
+
http_request.headers["Authorization"] = header_value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
|
2
|
+
require 'faraday/http_cache'
|
3
|
+
|
4
|
+
module MessageMediaLookups
|
5
|
+
# An implementation of HttpClient.
|
6
|
+
class FaradayClient < HttpClient
|
7
|
+
# The constructor.
|
8
|
+
def initialize(timeout: nil, cache: false,
|
9
|
+
max_retries: nil, retry_interval: nil)
|
10
|
+
@connection = Faraday.new do |faraday|
|
11
|
+
faraday.use Faraday::HttpCache, serializer: Marshal if cache
|
12
|
+
faraday.request :multipart
|
13
|
+
faraday.request :url_encoded
|
14
|
+
faraday.ssl[:ca_file] = Certifi.where
|
15
|
+
faraday.adapter Faraday.default_adapter
|
16
|
+
faraday.options[:params_encoder] = Faraday::FlatParamsEncoder
|
17
|
+
faraday.options[:open_timeout] = timeout if timeout
|
18
|
+
faraday.request :retry, max: max_retries, interval: if max_retries &&
|
19
|
+
retry_interval
|
20
|
+
retry_interval
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Method overridden from HttpClient.
|
26
|
+
def execute_as_string(http_request)
|
27
|
+
response = @connection.send(
|
28
|
+
http_request.http_method.downcase,
|
29
|
+
http_request.query_url
|
30
|
+
) do |request|
|
31
|
+
request.headers = http_request.headers
|
32
|
+
request.body = http_request.parameters
|
33
|
+
end
|
34
|
+
convert_response(response)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Method overridden from HttpClient.
|
38
|
+
def execute_as_binary(http_request)
|
39
|
+
response = @connection.send(
|
40
|
+
http_request.http_method.downcase,
|
41
|
+
http_request.query_url
|
42
|
+
) do |request|
|
43
|
+
request.headers = http_request.headers
|
44
|
+
request.body = http_request.parameters
|
45
|
+
end
|
46
|
+
convert_response(response)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Method overridden from HttpClient.
|
50
|
+
def convert_response(response)
|
51
|
+
HttpResponse.new(response.status, response.headers, response.body)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
# HttpCallBack allows defining callables for pre and post API calls.
|
4
|
+
class HttpCallBack
|
5
|
+
# A controller will call this method before making an HTTP Request.
|
6
|
+
# @param [HttpRequest] The HttpRequest object which the HttpClient
|
7
|
+
# will execute.
|
8
|
+
def on_before_request(_http_request)
|
9
|
+
raise NotImplementedError, 'This method needs
|
10
|
+
to be implemented in a child class.'
|
11
|
+
end
|
12
|
+
|
13
|
+
# A controller will call this method after making an HTTP Request.
|
14
|
+
# @param [HttpContext] The HttpContext of the API call.
|
15
|
+
def on_after_response(_http_context)
|
16
|
+
raise NotImplementedError, 'This method needs
|
17
|
+
to be implemented in a child class.'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
module MessageMediaLookups
|
3
|
+
# An interface for the methods that an HTTP Client must implement.
|
4
|
+
#
|
5
|
+
# This class should not be instantiated but should be used as a base class
|
6
|
+
# for HTTP Client classes.
|
7
|
+
class HttpClient
|
8
|
+
# Execute an HttpRequest when the response is expected to be a string.
|
9
|
+
# @param [HttpRequest] The HttpRequest to be executed.
|
10
|
+
def execute_as_string(_http_request)
|
11
|
+
raise NotImplementedError, 'This method needs
|
12
|
+
to be implemented in a child class.'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Execute an HttpRequest when the response is expected to be binary.
|
16
|
+
# @param [HttpRequest] The HttpRequest to be executed.
|
17
|
+
def execute_as_binary(_http_request)
|
18
|
+
raise NotImplementedError, 'This method needs
|
19
|
+
to be implemented in a child class.'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Converts the HTTP Response from the client to an HttpResponse object.
|
23
|
+
# @param [Dynamic] The response object received from the client.
|
24
|
+
def convert_response(_response)
|
25
|
+
raise NotImplementedError, 'This method needs
|
26
|
+
to be implemented in a child class.'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get a GET HttpRequest object.
|
30
|
+
# @param [String] The URL to send the request to.
|
31
|
+
# @param [Hash, Optional] The headers for the HTTP Request.
|
32
|
+
def get(query_url,
|
33
|
+
headers: {})
|
34
|
+
HttpRequest.new(HttpMethodEnum::GET,
|
35
|
+
query_url,
|
36
|
+
headers: headers)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get a POST HttpRequest object.
|
40
|
+
# @param [String] The URL to send the request to.
|
41
|
+
# @param [Hash, Optional] The headers for the HTTP Request.
|
42
|
+
# @param [Hash, Optional] The parameters for the HTTP Request.
|
43
|
+
def post(query_url,
|
44
|
+
headers: {},
|
45
|
+
parameters: {})
|
46
|
+
HttpRequest.new(HttpMethodEnum::POST,
|
47
|
+
query_url,
|
48
|
+
headers: headers,
|
49
|
+
parameters: parameters)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get a PUT HttpRequest object.
|
53
|
+
# @param [String] The URL to send the request to.
|
54
|
+
# @param [Hash, Optional] The headers for the HTTP Request.
|
55
|
+
# @param [Hash, Optional] The parameters for the HTTP Request.
|
56
|
+
def put(query_url,
|
57
|
+
headers: {},
|
58
|
+
parameters: {})
|
59
|
+
HttpRequest.new(HttpMethodEnum::PUT,
|
60
|
+
query_url,
|
61
|
+
headers: headers,
|
62
|
+
parameters: parameters)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get a PATCH HttpRequest object.
|
66
|
+
# @param [String] The URL to send the request to.
|
67
|
+
# @param [Hash, Optional] The headers for the HTTP Request.
|
68
|
+
# @param [Hash, Optional] The parameters for the HTTP Request.
|
69
|
+
def patch(query_url,
|
70
|
+
headers: {},
|
71
|
+
parameters: {})
|
72
|
+
HttpRequest.new(HttpMethodEnum::PATCH,
|
73
|
+
query_url,
|
74
|
+
headers: headers,
|
75
|
+
parameters: parameters)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Get a DELETE HttpRequest object.
|
79
|
+
# @param [String] The URL to send the request to.
|
80
|
+
# @param [Hash, Optional] The headers for the HTTP Request.
|
81
|
+
def delete(query_url,
|
82
|
+
headers: {},
|
83
|
+
parameters: {})
|
84
|
+
HttpRequest.new(HttpMethodEnum::DELETE,
|
85
|
+
query_url,
|
86
|
+
headers: headers,
|
87
|
+
parameters: parameters)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|