postcode-anywhere 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +30 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +247 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +72 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +319 -0
  9. data/Rakefile +2 -0
  10. data/config/pre_commit.yml +7 -0
  11. data/lib/postcode_anywhere.rb +10 -0
  12. data/lib/postcode_anywhere/bank_account_validation/bank_branch.rb +45 -0
  13. data/lib/postcode_anywhere/bank_account_validation/client.rb +13 -0
  14. data/lib/postcode_anywhere/bank_account_validation/interactive.rb +39 -0
  15. data/lib/postcode_anywhere/bank_account_validation/validation_result.rb +80 -0
  16. data/lib/postcode_anywhere/capture_plus/client.rb +13 -0
  17. data/lib/postcode_anywhere/capture_plus/interactive.rb +84 -0
  18. data/lib/postcode_anywhere/capture_plus/query_type.rb +8 -0
  19. data/lib/postcode_anywhere/capture_plus/retrieve_result.rb +45 -0
  20. data/lib/postcode_anywhere/capture_plus/search_result.rb +39 -0
  21. data/lib/postcode_anywhere/cleanse_plus/cleansed_address.rb +98 -0
  22. data/lib/postcode_anywhere/cleanse_plus/client.rb +13 -0
  23. data/lib/postcode_anywhere/cleanse_plus/interactive.rb +24 -0
  24. data/lib/postcode_anywhere/client.rb +86 -0
  25. data/lib/postcode_anywhere/configuration.rb +47 -0
  26. data/lib/postcode_anywhere/email_validation/client.rb +13 -0
  27. data/lib/postcode_anywhere/email_validation/interactive.rb +25 -0
  28. data/lib/postcode_anywhere/email_validation/validation_result.rb +24 -0
  29. data/lib/postcode_anywhere/error.rb +118 -0
  30. data/lib/postcode_anywhere/model_base.rb +72 -0
  31. data/lib/postcode_anywhere/request.rb +33 -0
  32. data/lib/postcode_anywhere/response/parse_json.rb +100 -0
  33. data/lib/postcode_anywhere/response/raise_error.rb +19 -0
  34. data/lib/postcode_anywhere/utils.rb +15 -0
  35. data/lib/postcode_anywhere/version.rb +3 -0
  36. data/postcode_anywhere.gemspec +31 -0
  37. data/spec/postcode_anywhere/bank_account_validation/interactive_spec.rb +82 -0
  38. data/spec/postcode_anywhere/capture_plus/interactive_spec.rb +240 -0
  39. data/spec/postcode_anywhere/cleanse_plus/interactive_spec.rb +65 -0
  40. data/spec/postcode_anywhere/client_spec.rb +124 -0
  41. data/spec/postcode_anywhere/configuration_spec.rb +62 -0
  42. data/spec/postcode_anywhere/email_validation/interactive_spec.rb +30 -0
  43. data/spec/postcode_anywhere/error_spec.rb +70 -0
  44. data/spec/postcode_anywhere/fixtures/bank_account_retrieve_by_sort.json +1 -0
  45. data/spec/postcode_anywhere/fixtures/bank_account_validate_account.json +1 -0
  46. data/spec/postcode_anywhere/fixtures/capture_plus_retrieve.json +1 -0
  47. data/spec/postcode_anywhere/fixtures/capture_plus_search.json +1 -0
  48. data/spec/postcode_anywhere/fixtures/cleanse_address_multi.json +1 -0
  49. data/spec/postcode_anywhere/fixtures/cleanse_address_single.json +1 -0
  50. data/spec/postcode_anywhere/fixtures/email_validation_validate_email.json +1 -0
  51. data/spec/postcode_anywhere/model_base_spec.rb +10 -0
  52. data/spec/spec_helper.rb +38 -0
  53. metadata +281 -0
@@ -0,0 +1,13 @@
1
+ require 'postcode_anywhere/client'
2
+ require 'postcode_anywhere/cleanse_plus/interactive'
3
+
4
+ module PostcodeAnywhere
5
+ module CleansePlus
6
+ class Client < ::PostcodeAnywhere::Client
7
+ include PostcodeAnywhere::CleansePlus::Interactive
8
+ def initialize(options = {})
9
+ super(options)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ require 'postcode_anywhere/utils'
2
+ require 'postcode_anywhere/cleanse_plus/cleansed_address'
3
+
4
+ module PostcodeAnywhere
5
+ module CleansePlus
6
+ module Interactive
7
+ include ::PostcodeAnywhere::Utils
8
+
9
+ API_VERSION = '1.00'
10
+
11
+ CLEANSE_ADDRESS_ENDPOINT = "CleansePlus/Interactive/Cleanse/v#{API_VERSION}/json.ws"
12
+
13
+ def address_candidates_for(address, options = {})
14
+ options.merge!('Address' => address)
15
+ perform_with_objects(
16
+ :get,
17
+ CLEANSE_ADDRESS_ENDPOINT,
18
+ options,
19
+ PostcodeAnywhere::CleansePlus::CleansedAddress
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,86 @@
1
+ require 'faraday'
2
+ require 'faraday/request/multipart'
3
+ require 'json'
4
+ require 'timeout'
5
+ require 'postcode_anywhere/error'
6
+ require 'postcode_anywhere/response/raise_error'
7
+ require 'postcode_anywhere/response/parse_json'
8
+
9
+ module PostcodeAnywhere
10
+ class Client
11
+ attr_accessor(*Configuration::VALID_CONFIG_KEYS)
12
+
13
+ def initialize(options = {})
14
+ merged_options = PostcodeAnywhere.options.merge(options)
15
+
16
+ Configuration::VALID_CONFIG_KEYS.each do |key|
17
+ send("#{key}=", merged_options[key])
18
+ end
19
+ end
20
+
21
+ # Perform an HTTP GET request
22
+ def get(path, body_hash = {}, params = {})
23
+ request(:get, path, params, body_hash)
24
+ end
25
+
26
+ # Perform an HTTP POST request
27
+ def post(path, body_hash = {}, params = {})
28
+ request(:post, path, params, body_hash)
29
+ end
30
+
31
+ def connection_options
32
+ @connection_options ||= {
33
+ builder: middleware,
34
+ headers: {
35
+ accept: "application/#{@format}",
36
+ content_type: "application/#{@format}",
37
+ user_agent: user_agent
38
+ },
39
+ request: {
40
+ open_timeout: 10,
41
+ timeout: 30
42
+ }
43
+ }
44
+ end
45
+
46
+ def connection
47
+ @connection ||= Faraday.new(@endpoint, connection_options)
48
+ end
49
+
50
+ def middleware
51
+ @middleware ||= Faraday::RackBuilder.new do |faraday|
52
+ # Checks for files in the payload, otherwise leaves everything untouched
53
+ faraday.request :multipart
54
+ # Encodes as "application/x-www-form-urlencoded" if not already encoded
55
+ faraday.request :url_encoded
56
+ # Handle error responses
57
+ faraday.response :postcode_anywhere_raise_error
58
+ # Parse JSON response bodies
59
+ faraday.response :postcode_anywhere_parse_json
60
+ # Set default HTTP adapter
61
+ faraday.adapter :net_http
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def request(method, path, params = {}, body_hash = {})
68
+ attach_api_key_to params
69
+ connection.send(method.to_sym, path, params) do |request|
70
+ request.body = compile_body(body_hash) unless body_hash.empty?
71
+ end.env
72
+ rescue Faraday::Error::TimeoutError, Timeout::Error => error
73
+ raise(PostcodeAnywhere::Error::RequestTimeout.new(error))
74
+ rescue Faraday::Error::ClientError, JSON::ParserError => error
75
+ raise(PostcodeAnywhere::Error.new(error))
76
+ end
77
+
78
+ def attach_api_key_to(params)
79
+ params.merge!('Key' => @api_key) unless params.keys.include? 'Key'
80
+ end
81
+
82
+ def compile_body(body_hash)
83
+ body_hash.to_json
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,47 @@
1
+ module PostcodeAnywhere
2
+ module Configuration
3
+ VALID_CONNECTION_KEYS = [
4
+ :endpoint,
5
+ :api_key,
6
+ :method,
7
+ :user_agent
8
+ ].freeze
9
+
10
+ VALID_OPTIONS_KEYS = [:format].freeze
11
+ VALID_CONFIG_KEYS =
12
+ VALID_CONNECTION_KEYS +
13
+ VALID_OPTIONS_KEYS
14
+
15
+ DEFAULT_METHOD = :post
16
+ DEFAULT_USER_AGENT = "Postcode Anywhere Ruby Gem/#{PostcodeAnywhere::VERSION}"
17
+
18
+ DEFAULT_API_KEY = ''
19
+
20
+ DEFAULT_FORMAT = :json
21
+
22
+ DEFAULT_ENDPOINT = 'http://services.postcodeanywhere.co.uk/'
23
+
24
+ attr_accessor(*VALID_CONFIG_KEYS)
25
+
26
+ def self.extended(base)
27
+ base.reset
28
+ end
29
+
30
+ def reset
31
+ self.api_key = DEFAULT_API_KEY
32
+ self.endpoint = DEFAULT_ENDPOINT
33
+ self.format = DEFAULT_FORMAT
34
+ self.method = DEFAULT_METHOD
35
+ self.user_agent = DEFAULT_USER_AGENT
36
+ end
37
+
38
+ def configure
39
+ yield self
40
+ end
41
+
42
+ # Return the configuration values set in this module
43
+ def options
44
+ Hash[* VALID_CONFIG_KEYS.map { |key| [key, send(key)] }.flatten]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,13 @@
1
+ require 'postcode_anywhere/client'
2
+ require 'postcode_anywhere/email_validation/interactive'
3
+
4
+ module PostcodeAnywhere
5
+ module EmailValidation
6
+ class Client < ::PostcodeAnywhere::Client
7
+ include PostcodeAnywhere::EmailValidation::Interactive
8
+ def initialize(options = {})
9
+ super(options)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'postcode_anywhere/utils'
2
+ require 'postcode_anywhere/email_validation/validation_result'
3
+
4
+ module PostcodeAnywhere
5
+ module EmailValidation
6
+ module Interactive
7
+ include ::PostcodeAnywhere::Utils
8
+
9
+ API_VERSION = '1.10'
10
+
11
+ VALIDATE_EMAIL_ENDPOINT =
12
+ "/EmailValidation/Interactive/Validate/v#{API_VERSION}/json3.ws"
13
+
14
+ def validate_email_address(email, timeout = 3, options = {})
15
+ options.merge!('Email' => email, 'Timeout' => timeout)
16
+ perform_with_object(
17
+ :get,
18
+ VALIDATE_EMAIL_ENDPOINT,
19
+ options,
20
+ PostcodeAnywhere::EmailValidation::ValidationResult
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,24 @@
1
+ require 'postcode_anywhere/model_base'
2
+
3
+ module PostcodeAnywhere
4
+ module EmailValidation
5
+ class ValidationResult < PostcodeAnywhere::ModelBase
6
+ # The address the email was sent to.
7
+ # Example: info@google.com
8
+ attr_reader :email
9
+
10
+ # The mail server that was used to perform the server and account validation steps on.
11
+ # Not populated if the DNS record was not found.
12
+ # Example: google.com.s9a1.psmtp.com
13
+ attr_reader :mail_server
14
+
15
+ # Indicates that the format of the email appears valid.
16
+ # Example: true
17
+ attr_reader :valid_format
18
+
19
+ # Indicates that a valid DNS record was found for the email.
20
+ # Example: true
21
+ attr_reader :found_dns_record
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,118 @@
1
+ module PostcodeAnywhere
2
+ # Custom error class for rescuing from all PostcodeAnywhere errors
3
+ class Error < StandardError
4
+ attr_reader :code
5
+ attr_reader :cause
6
+ attr_reader :resolution
7
+
8
+ class << self
9
+ def from_response(error_hash)
10
+ message, code, cause, resolution = parse_error(error_hash)
11
+ new(message, code, cause, resolution)
12
+ end
13
+
14
+ def errors
15
+ @errors ||= {
16
+ 400 => PostcodeAnywhere::Error::BadRequest,
17
+ 401 => PostcodeAnywhere::Error::Unauthorized,
18
+ 403 => PostcodeAnywhere::Error::Forbidden,
19
+ 404 => PostcodeAnywhere::Error::NotFound,
20
+ 406 => PostcodeAnywhere::Error::NotAcceptable,
21
+ 408 => PostcodeAnywhere::Error::RequestTimeout,
22
+ 422 => PostcodeAnywhere::Error::UnprocessableEntity,
23
+ 429 => PostcodeAnywhere::Error::TooManyRequests,
24
+ 500 => PostcodeAnywhere::Error::InternalServerError,
25
+ 502 => PostcodeAnywhere::Error::BadGateway,
26
+ 503 => PostcodeAnywhere::Error::ServiceUnavailable,
27
+ 504 => PostcodeAnywhere::Error::GatewayTimeout
28
+ }
29
+ end
30
+
31
+ def postcode_anywhere_errors
32
+ @postcode_anywhere_errors ||= {
33
+ -1 => PostcodeAnywhere::Error::UnknownError,
34
+ 2 => PostcodeAnywhere::Error::UnknownKey,
35
+ 3 => PostcodeAnywhere::Error::AccountOutOfCredit,
36
+ 4 => PostcodeAnywhere::Error::IpDenied,
37
+ 5 => PostcodeAnywhere::Error::UrlDenied,
38
+ 6 => PostcodeAnywhere::Error::ServiceDeniedForKey,
39
+ 7 => PostcodeAnywhere::Error::ServiceDeniedForPlan,
40
+ 8 => PostcodeAnywhere::Error::KeyDailyLimitExceeded,
41
+ 9 => PostcodeAnywhere::Error::SurgeProtectorRunning,
42
+ 10 => PostcodeAnywhere::Error::SurgeProtectorTriggered,
43
+ 11 => PostcodeAnywhere::Error::NoValidLicense,
44
+ 12 => PostcodeAnywhere::Error::ManagementKeyRequired,
45
+ 13 => PostcodeAnywhere::Error::DemoLimitExceeded,
46
+ 14 => PostcodeAnywhere::Error::FreeLimitExceeded,
47
+ 15 => PostcodeAnywhere::Error::IncorrectKeyType,
48
+ 16 => PostcodeAnywhere::Error::KeyExpired,
49
+ 17 => PostcodeAnywhere::Error::KeyDailyLimitExceeded
50
+ }
51
+ end
52
+
53
+ private
54
+
55
+ def parse_error(error_hash)
56
+ if error_hash.nil?
57
+ ['', nil, '', '']
58
+ else
59
+ [
60
+ error_hash[:description],
61
+ error_hash[:error],
62
+ error_hash[:cause],
63
+ error_hash[:resolution]
64
+ ]
65
+ end
66
+ end
67
+
68
+ def extract_message_from_error(body)
69
+ m =
70
+ /The exception message is \'(.+)?\'\. See server logs for more details./.match(body)
71
+ return m.captures.first if m
72
+ end
73
+ end
74
+
75
+ def initialize(description = '', code = nil, cause = '', resolution = '')
76
+ super(description)
77
+ @code = code
78
+ @cause = cause
79
+ @resolution = resolution
80
+ end
81
+
82
+ ClientError = Class.new(self)
83
+ BadRequest = Class.new(ClientError)
84
+ Unauthorized = Class.new(ClientError)
85
+ Forbidden = Class.new(ClientError)
86
+ NotFound = Class.new(ClientError)
87
+ NotAcceptable = Class.new(ClientError)
88
+ RequestTimeout = Class.new(ClientError)
89
+ UnprocessableEntity = Class.new(ClientError)
90
+ TooManyRequests = Class.new(ClientError)
91
+ ServerError = Class.new(self)
92
+ InternalServerError = Class.new(ServerError)
93
+ BadGateway = Class.new(ServerError)
94
+ ServiceUnavailable = Class.new(ServerError)
95
+ GatewayTimeout = Class.new(ServerError)
96
+
97
+ # Postcode anywhere specific errors
98
+
99
+ UnknownError = Class.new(ServerError)
100
+ UnknownKey = Class.new(ClientError)
101
+ AccountOutOfCredit = Class.new(Forbidden)
102
+ IpDenied = Class.new(Forbidden)
103
+ UrlDenied = Class.new(Forbidden)
104
+ ServiceDeniedForKey = Class.new(Forbidden)
105
+ ServiceDeniedForPlan = Class.new(Forbidden)
106
+ KeyDailyLimitExceeded = Class.new(Forbidden)
107
+ SurgeProtectorRunning = Class.new(Forbidden)
108
+ SurgeProtectorTriggered = Class.new(Forbidden)
109
+ NoValidLicense = Class.new(Forbidden)
110
+ ManagementKeyRequired = Class.new(Forbidden)
111
+ DemoLimitExceeded = Class.new(Forbidden)
112
+ FreeLimitExceeded = Class.new(Forbidden)
113
+ IncorrectKeyType = Class.new(Forbidden)
114
+ KeyExpired = Class.new(Forbidden)
115
+
116
+ ServiceSpecificError = Class.new(ClientError)
117
+ end
118
+ end
@@ -0,0 +1,72 @@
1
+ require 'memoizable'
2
+
3
+ module PostcodeAnywhere
4
+ class ModelBase
5
+ include Memoizable
6
+ attr_reader :attrs
7
+ alias_method :to_h, :attrs
8
+ alias_method :to_hash, :to_h
9
+
10
+ class << self
11
+ def attr_reader(*attrs)
12
+ attrs.each do |attr|
13
+ define_attribute_method(attr)
14
+ define_predicate_method(attr)
15
+ end
16
+ end
17
+
18
+ def predicate_attr_reader(*attrs)
19
+ attrs.each do |attr|
20
+ define_predicate_method(attr)
21
+ deprecate_attribute_method(attr)
22
+ end
23
+ end
24
+
25
+ def object_attr_reader(klass, key1, key2 = nil)
26
+ define_attribute_method(key1, klass, key2)
27
+ define_predicate_method(key1)
28
+ end
29
+
30
+ def define_attribute_method(key1, klass = nil, key2 = nil)
31
+ define_method(key1) do ||
32
+ if @attrs[key1].nil? || @attrs[key1].respond_to?(:empty?) && @attrs[key1].empty?
33
+ # NullObject.new
34
+ else
35
+ if klass.nil?
36
+ @attrs[key1]
37
+ else
38
+ attrs = attrs_for_object(key1, key2)
39
+ PostcodeAnywhere.const_get(klass).new(attrs)
40
+ end
41
+ end
42
+ end
43
+ memoize(key1)
44
+ end
45
+
46
+ def define_predicate_method(key1, key2 = key1)
47
+ define_method(:"#{key1}?") do ||
48
+ !@attrs[key2].nil? &&
49
+ @attrs[key2] != false &&
50
+ !(@attrs[key2].respond_to?(:empty?) &&
51
+ @attrs[key2].empty?)
52
+ end
53
+ memoize(:"#{key1}?")
54
+ end
55
+ end
56
+
57
+ def initialize(attrs = {})
58
+ @attrs = attrs || {}
59
+ end
60
+
61
+ private
62
+
63
+ def attrs_for_object(key1, key2 = nil)
64
+ if key2.nil?
65
+ @attrs[key1]
66
+ else
67
+ attrs = @attrs.dup
68
+ attrs.delete(key1).merge(key2 => attrs)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,33 @@
1
+ module PostcodeAnywhere
2
+ class Request
3
+ attr_accessor :client, :request_method, :path, :body, :options
4
+ alias_method :verb, :request_method
5
+
6
+ def initialize(client, request_method, path, body_hash = {}, options = {})
7
+ @client = client
8
+ @request_method = request_method.to_sym
9
+ @path = path
10
+ @body_hash = body_hash
11
+ @options = options
12
+ end
13
+
14
+ def perform
15
+ @client.send(@request_method, @path, @body_hash, @options).body
16
+ end
17
+
18
+ def perform_with_object(klass)
19
+ result = perform
20
+ if result.class == Array
21
+ klass.new(result.first)
22
+ else
23
+ klass.new(result)
24
+ end
25
+ end
26
+
27
+ def perform_with_objects(klass)
28
+ perform.map do |element|
29
+ klass.new(element)
30
+ end
31
+ end
32
+ end
33
+ end