signal_api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +19 -0
  2. data/.travis.yml +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +22 -0
  5. data/README.md +43 -0
  6. data/Rakefile +49 -0
  7. data/lib/signal_api/carrier.rb +43 -0
  8. data/lib/signal_api/contact.rb +60 -0
  9. data/lib/signal_api/core_ext/array.rb +7 -0
  10. data/lib/signal_api/core_ext/hash.rb +7 -0
  11. data/lib/signal_api/core_ext/nil_class.rb +7 -0
  12. data/lib/signal_api/core_ext/string.rb +7 -0
  13. data/lib/signal_api/coupon_group.rb +47 -0
  14. data/lib/signal_api/deliver_sms.rb +54 -0
  15. data/lib/signal_api/exceptions.rb +19 -0
  16. data/lib/signal_api/list.rb +224 -0
  17. data/lib/signal_api/mocks/api_mock.rb +70 -0
  18. data/lib/signal_api/mocks/contact.rb +13 -0
  19. data/lib/signal_api/mocks/deliver_sms.rb +14 -0
  20. data/lib/signal_api/mocks/list.rb +19 -0
  21. data/lib/signal_api/mocks/short_url.rb +9 -0
  22. data/lib/signal_api/segment.rb +161 -0
  23. data/lib/signal_api/short_url.rb +56 -0
  24. data/lib/signal_api/signal_http_api.rb +49 -0
  25. data/lib/signal_api/util/email_address.rb +10 -0
  26. data/lib/signal_api/util/phone.rb +37 -0
  27. data/lib/signal_api/version.rb +3 -0
  28. data/lib/signal_api.rb +114 -0
  29. data/signal_api.gemspec +27 -0
  30. data/test/api/carrier_test.rb +43 -0
  31. data/test/api/contact_test.rb +93 -0
  32. data/test/api/coupon_group_test.rb +36 -0
  33. data/test/api/deliver_sms_test.rb +66 -0
  34. data/test/api/general_test.rb +26 -0
  35. data/test/api/list_test.rb +261 -0
  36. data/test/api/segment_test.rb +144 -0
  37. data/test/api/short_url_test.rb +50 -0
  38. data/test/mocks/contact_mock_test.rb +24 -0
  39. data/test/mocks/deliver_sms_mock_test.rb +21 -0
  40. data/test/mocks/list_mock_test.rb +33 -0
  41. data/test/mocks/short_url_mock_test.rb +17 -0
  42. data/test/test_helper.rb +20 -0
  43. metadata +248 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ log/test.log
14
+ pkg
15
+ rdoc
16
+ spec/reports
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Signal
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # SignalApi
2
+
3
+ A simple library for working with the Signal API (see [http://dev.signalhq.com](http://dev.signalhq.com))
4
+
5
+ [![Build Status](https://secure.travis-ci.org/signal/signal-ruby.png?branch=master)](http://travis-ci.org/signal/signal-ruby)
6
+
7
+ ## Installation
8
+ ------------
9
+
10
+ ### RubyGems ###
11
+ Signal can be installed using RubyGems
12
+
13
+ gem install signal_api
14
+
15
+ Inside your script, be sure to
16
+
17
+ require "rubygems"
18
+ require "signal_api"
19
+
20
+ ### Bundler ###
21
+ If you're using Bundler, add the following to your Gemfile
22
+
23
+ gem "signal_api"
24
+
25
+ and then run
26
+
27
+ bundle install
28
+
29
+ Usage
30
+ ------------
31
+
32
+ Before using any of the APIs, you will need to set your API key:
33
+
34
+ SignalApi.api_key = 'foobar123456abcxyz77'
35
+
36
+ You can find your Signal API key while logged into Signal and looking at your account settings.
37
+
38
+ You may also specify where Signal should log messages (optional):
39
+
40
+ SignalApi.logger = Rails.logger
41
+ SignalApi.logger = Logger.new(STDERR)
42
+
43
+ After SignalApi has been configured, you may use any of the API classes to interact with the Signal platform.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
12
+ end
13
+
14
+ require 'rake'
15
+ require 'rake/testtask'
16
+ require 'yard'
17
+
18
+ task :default => :test
19
+
20
+ namespace :test do
21
+ Rake::TestTask.new(:api) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = ENV['TEST'] || "test/api/**/*_test.rb"
24
+ test.verbose = true
25
+ end
26
+
27
+ Rake::TestTask.new(:mocks) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = ENV['TEST'] || "test/mocks/**/*_test.rb"
30
+ test.verbose = true
31
+ end
32
+ end
33
+
34
+ desc 'Run all of the tests'
35
+ task :test => ["test:api", "test:mocks"]
36
+
37
+ YARD::Rake::YardocTask.new do |t|
38
+ t.files = ['lib/**/*.rb']
39
+ end
40
+
41
+ desc 'Delete yard, and other generated files'
42
+ task :clobber => [:clobber_yard]
43
+
44
+ desc 'Delete yard generated files'
45
+ task :clobber_yard do
46
+ puts 'rm -rf doc .yardoc'
47
+ FileUtils.rm_rf ['doc', '.yardoc']
48
+ end
49
+
@@ -0,0 +1,43 @@
1
+ module SignalApi
2
+
3
+ # Managed carrier from signal api
4
+ class Carrier < SignalHttpApi
5
+
6
+ # The Carrier id from Signal
7
+ attr_reader :id
8
+
9
+ # The Carrier name from Signal
10
+ attr_reader :name
11
+
12
+ def initialize(id, name)
13
+ @id = id
14
+ @name = name
15
+ end
16
+
17
+ # Lookup a carrier on textme
18
+ #
19
+ # @param [String] mobile_phone The mobile phone to lookup
20
+ #
21
+ # @return [carrier] A Carrier object representing the Carrier on the Signal platform
22
+ def self.lookup(mobile_phone)
23
+ raise InvalidParameterException.new("mobile_phone cannot be blank") if mobile_phone.blank?
24
+
25
+ SignalApi.logger.info "Attempting to lookup carrier for mobile phone #{mobile_phone}"
26
+
27
+ with_retries do
28
+ response = get("/app/carriers/lookup/#{mobile_phone}.xml",
29
+ :format => :xml,
30
+ :headers => common_headers)
31
+
32
+ if response.code == 200 && response.parsed_response['carrier']
33
+ Carrier.new(response.parsed_response['carrier']['id'], response.parsed_response['carrier']['name'])
34
+ elsif response.code == 404
35
+ raise InvalidMobilePhoneException.new("carrier for mobile phone #{mobile_phone} could not be found")
36
+ else
37
+ handle_api_failure(response)
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,60 @@
1
+ module SignalApi
2
+
3
+ # A Contact (person, subscriber, etc) on the Signal platform
4
+ class Contact
5
+
6
+ # The user attributes associated with the contact
7
+ attr_accessor :attributes
8
+
9
+ def initialize(attributes={})
10
+ @attributes = attributes
11
+ end
12
+
13
+ # Convenience accessor for the contact's mobile phone
14
+ def mobile_phone
15
+ @attributes['mobile-phone']
16
+ end
17
+
18
+ # Convenience accessor for the contact's email address
19
+ def email_address
20
+ @attributes['email-address']
21
+ end
22
+
23
+ # Update the contact's data on the Signal platform.
24
+ #
25
+ # @return true If the contact's data was saved successfully.
26
+ def save
27
+ validate_contact_update
28
+
29
+ xml = Builder::XmlMarkup.new
30
+ xml.user_attributes do
31
+ attributes.each do |key, value|
32
+ xml.tag!(key, value)
33
+ end
34
+ end
35
+
36
+ contact_identifier = mobile_phone.blank? ? email_address : mobile_phone
37
+
38
+ with_retries do
39
+ response = put("/api/contacts/#{contact_identifier}.xml",
40
+ :body => xml.target!,
41
+ :format => :xml,
42
+ :headers => common_headers)
43
+
44
+ if response.code == 200
45
+ true
46
+ else
47
+ handle_api_failure(response)
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def validate_contact_update
55
+ raise InvalidParameterException.new("mobile_phone or email is required") if mobile_phone.blank? && email_address.blank?
56
+ raise InvalidParameterException.new("nothing to update, only identifier provided") if attributes.count < 2
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+
3
+ def blank?
4
+ empty?
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class Hash
2
+
3
+ def blank?
4
+ empty?
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class NilClass
2
+
3
+ def blank?
4
+ true
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class String
2
+
3
+ def blank?
4
+ strip.empty?
5
+ end
6
+
7
+ end
@@ -0,0 +1,47 @@
1
+ module SignalApi
2
+
3
+ # manage a copuon group using signals api
4
+ class CouponGroup < SignalHttpApi
5
+
6
+ # Consume a coupon
7
+ #
8
+ # @param [String] coupon_group_tag The tag for the coupon group in Textme to consume this coupon from.]
9
+ # @param [String] mobile_phone The mobile phone to consume this coupon for
10
+ #
11
+ # @return a coupon code
12
+ def self.consume_coupon(coupon_group_tag, mobile_phone)
13
+ validate_consume_coupon_parameters(coupon_group_tag, mobile_phone)
14
+
15
+ SignalApi.logger.info "Attempting to consume coupon from group #{coupon_group_tag} #{mobile_phone}"
16
+
17
+ xml = Builder::XmlMarkup.new
18
+ xml.request do
19
+ xml.user do
20
+ xml.tag!('mobile_phone',mobile_phone)
21
+ end
22
+ xml.tag!('coupon_group', coupon_group_tag)
23
+ end
24
+
25
+ with_retries do
26
+ response = get('/api/coupon_groups/consume_coupon.xml',
27
+ :body => xml.target!,
28
+ :format => :xml,
29
+ :headers => common_headers)
30
+
31
+ if response.code == 200
32
+ response.parsed_response['coupon_code']
33
+ else
34
+ handle_api_failure(response)
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def self.validate_consume_coupon_parameters(coupon_group_tag, mobile_phone)
42
+ raise InvalidParameterException.new("Coupon group tag cannot be blank") if coupon_group_tag.blank?
43
+ raise InvalidParameterException.new("Mobile_phone cannot be blank") if mobile_phone.blank?
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,54 @@
1
+ module SignalApi
2
+
3
+ # Deliver a SMS message using Signal's messaging API
4
+ class DeliverSms < SignalHttpApi
5
+
6
+ base_uri "https://api.imws.us"
7
+
8
+ # Create an instance of this class, with your messaging API credentials.
9
+ # These credentials are separate from the api_key that is used by the
10
+ # other APIs, and can be found in the API campaign configuration.
11
+ def initialize(username, password)
12
+ @username = username
13
+ @password = password
14
+
15
+ if @username.nil? || @password.nil?
16
+ raise InvalidParameterException.new("username and password must be provided")
17
+ end
18
+ end
19
+
20
+ # Deliver a SMS message to a mobile phone. Messages exceeding the 160 character
21
+ # limit will be split into multiple messages.
22
+ #
23
+ # @param [String] mobile_phone The mobile phone to send the message to
24
+ # @param [String] message The message to send
25
+ #
26
+ # @return [String] The unique message ID
27
+ def deliver(mobile_phone, message)
28
+ sanitized_mobile_phone = Phone.sanitize(mobile_phone)
29
+ unless Phone.valid?(sanitized_mobile_phone)
30
+ raise InvalidParameterException.new("An invalid mobile phone was specified: #{mobile_phone}")
31
+ end
32
+
33
+ if message.nil? || message.strip.empty?
34
+ raise InvalidParameterException.new("A message must be provided")
35
+ end
36
+
37
+ SignalApi.logger.info "Delivering the following message to #{sanitized_mobile_phone}: #{message}"
38
+ self.class.with_retries do
39
+ response = self.class.post('/messages/send',
40
+ :basic_auth => { :username => @username, :password => @password },
41
+ :query => { :mobile_phone => sanitized_mobile_phone, :message => message })
42
+
43
+ if response.code == 200
44
+ response.parsed_response =~ /^Message ID: (.*)$/
45
+ $1
46
+ else
47
+ self.class.handle_api_failure(response)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,19 @@
1
+ module SignalApi
2
+ # Base class for exceptions that should not be retried
3
+ class NonRetryableException < StandardError; end
4
+
5
+ # Exception raised when a request to the Signal API fails
6
+ class ApiException < StandardError; end
7
+
8
+ # Exception raised when the api key is not properly set
9
+ class InvalidApiKeyException < StandardError; end
10
+
11
+ # Authentication to the Signal platform failed. Make sure your API key is correct.
12
+ class AuthFailedException < StandardError; end
13
+
14
+ # An invalid parameter was passed to the given method
15
+ class InvalidParameterException < StandardError; end
16
+
17
+ # An invalid mobile phone number was passed
18
+ class InvalidMobilePhoneException < NonRetryableException; end
19
+ end
@@ -0,0 +1,224 @@
1
+ module SignalApi
2
+
3
+ # The type of subscription
4
+ class SubscriptionType
5
+ SMS = "SMS"
6
+ EMAIL = "EMAIL"
7
+ end
8
+
9
+ # Represents a message to be sent to users on a particular carrier
10
+ class CarrierOverrideMessage
11
+ attr_accessor :carrier_id, :text
12
+
13
+ def initialize(carrier_id, text)
14
+ @carrier_id = carrier_id
15
+ @text = text
16
+ end
17
+ end
18
+
19
+ # Manage subscriptions to, and send messages to subscribers of a List.
20
+ class List < SignalHttpApi
21
+
22
+ # Create a new List object
23
+ #
24
+ # @param [Fixnum] list_id The ID of the list in the Signal platform
25
+ def initialize(list_id)
26
+ @list_id = list_id
27
+ raise InvalidParameterException.new("list_id cannot be nil") if @list_id.nil?
28
+ end
29
+
30
+ # Create a new subscription to the list.
31
+ #
32
+ # @param [SubscriptionType] subscription_type The type of subscription to create
33
+ # @param [Contact] contact The contact to create the subscription for. The contact must contain a valid
34
+ # mobile phone number for SMS subscriptions, and a valid email address for
35
+ # EMAIL subscriptions. Any other attributes stored with the contact will also
36
+ # be stored on the Signal platform.
37
+ # @param [Hash] options <b>Optional</b> The options used to create the subscription
38
+ # @option options [String] :source_keyword The source keyword to use when creating the subscription (for SMS subscriptions)
39
+ #
40
+ # @return [Bool] True if a subscription was created, false if the subscription already existed.
41
+ def create_subscription(subscription_type, contact, options={})
42
+ validate_create_subscription_request(subscription_type, contact, options)
43
+
44
+ builder = Builder::XmlMarkup.new
45
+ body = builder.subscription do |subscription|
46
+ subscription.tag!('subscription-type', subscription_type)
47
+ subscription.tag!('source-keyword', options[:source_keyword]) if options[:source_keyword]
48
+ subscription.user do |user|
49
+ contact.attributes.each do |attribute_name, attribute_value|
50
+ user.__send__(attribute_name, attribute_value)
51
+ end
52
+ end
53
+ end
54
+
55
+ SignalApi.logger.info "Attempting to create a subscription to list #{@list_id}"
56
+ SignalApi.logger.debug "Subscription data: #{body}"
57
+ self.class.with_retries do
58
+ response = self.class.post("/api/subscription_campaigns/#{@list_id}/subscriptions.xml",
59
+ :body => body,
60
+ :format => :xml,
61
+ :headers => self.class.common_headers)
62
+
63
+ if response.code == 200
64
+ return true
65
+ else
66
+ if response.body.include?("Could not find the carrier for mobile phone")
67
+ raise SignalApi::InvalidMobilePhoneException.new(response.body)
68
+ elsif response.body.include?("already signed up")
69
+ SignalApi.logger.info response.body
70
+ return false
71
+ elsif response.body.include?("Subscriber cannot be re-added since they have unsubscribed within the past")
72
+ SignalApi.logger.info response.body
73
+ return false
74
+ elsif response.body.include?("User already subscribed, resending confirmation message")
75
+ SignalApi.logger.info response.body
76
+ return false
77
+ else
78
+ self.class.handle_api_failure(response)
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # Destroy a subscription which exists in this list.
85
+ #
86
+ # @param [SubscriptionType] subscription_type The type of subscription to destroy
87
+ # @param [Contact] contact The contact to destroy the subscription for. The contact must contain a valid
88
+ # mobile phone number for SMS subscriptions, and a valid email address for
89
+ # EMAIL subscriptions.
90
+ #
91
+ # @return [Bool] True if a subscription was desctroyed, false if the subscription did not exist.
92
+ def destroy_subscription(subscription_type, contact)
93
+ validate_destroy_subscription_request(subscription_type, contact)
94
+
95
+ SignalApi.logger.info "Attempting to destroy a subscription to list #{@list_id}"
96
+ SignalApi.logger.debug "Contact data: #{contact.inspect}"
97
+
98
+ if subscription_type == SubscriptionType::SMS
99
+ contact_id = contact.mobile_phone
100
+ else
101
+ contact_id = contact.email_address
102
+ end
103
+
104
+ self.class.with_retries do
105
+ response = self.class.delete("/api/subscription_campaigns/#{@list_id}/subscriptions/#{contact_id}.xml",
106
+ :headers => self.class.common_headers)
107
+
108
+ if response.code == 200
109
+ return true
110
+ else
111
+ if response.body.include?("is not subscribed to campaign")
112
+ SignalApi.logger.info response.body
113
+ return false
114
+ elsif response.body.include?("Invalid user ID")
115
+ SignalApi.logger.info response.body
116
+ return false
117
+ else
118
+ self.class.handle_api_failure(response)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ # Sends an SMS message to the subscribers of the subscription list.
125
+ #
126
+ # @param [String] description A description of the message.
127
+ # @param [String] text The message to send. Must not be greater than 160 characters.
128
+ # @param [Hash] options <b>Optional</b> The options used when sending the message.
129
+ # @option options [Time] :send_at The date and time to send the message. The message will be
130
+ # sent immediately if not provided.
131
+ # @option options [Fixnum] :segment_id The id of the segment to send the message to. If not
132
+ # specified, the message will be sent to all subscribers in the list.
133
+ # @option options [Array<Fixnum>] :tags An array of tag ids to tag the scheduled message with.
134
+ # @option options [Array<CarrierOverrideMessage>] :carrier_overrides An alternate text message to send to
135
+ # users on a particular carrier.
136
+ # @return [Fixnum] The ID of the scheduled message on the Signal platform.
137
+ def send_message(description, text, options={})
138
+ raise InvalidParameterException.new("A description must be provided") if description.blank?
139
+ raise InvalidParameterException.new("A text message must be provided") if text.blank?
140
+ raise InvalidParameterException.new("The text message must not be greater than 160 characters") if text.size > 160
141
+
142
+ builder = Builder::XmlMarkup.new
143
+ body = builder.message do |message|
144
+ message.description(description)
145
+ message.text(text)
146
+ message.send_at(options[:send_at].strftime("%Y-%m-%d %H:%M:%S")) if options[:send_at]
147
+ message.segment_id(options[:segment_id]) if options[:segment_id]
148
+
149
+ if options[:tags]
150
+ message.tags(:type => :array) do |tags|
151
+ options[:tags].each { |tag_id| tags.tag(tag_id) }
152
+ end
153
+ end
154
+
155
+ if options[:carrier_overrides]
156
+ message.carrier_overrides(:type => :array) do |carrier_overrides|
157
+ options[:carrier_overrides].each do |carrier_override_message|
158
+ carrier_overrides.carrier_override do |carrier_override|
159
+ carrier_override.carrier_id(carrier_override_message.carrier_id)
160
+ carrier_override.text(carrier_override_message.text)
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ SignalApi.logger.info "Attempting to send a message to list #{@list_id}"
168
+ SignalApi.logger.debug "Message data: #{body}"
169
+ self.class.with_retries do
170
+ response = self.class.post("/api/subscription_campaigns/#{@list_id}/send_message.xml",
171
+ :body => body,
172
+ :format => :xml,
173
+ :headers => self.class.common_headers)
174
+
175
+ if response.code == 200
176
+ data = response.parsed_response['scheduled_message']
177
+ data['id']
178
+ else
179
+ self.class.handle_api_failure(response)
180
+ end
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def validate_create_subscription_request(subscription_type, contact, options)
187
+ unless [SubscriptionType::SMS, SubscriptionType::EMAIL].include?(subscription_type)
188
+ raise InvalidParameterException.new("Invalid subscription type")
189
+ end
190
+
191
+ if contact.nil?
192
+ raise InvalidParameterException.new("A contact must be provided")
193
+ end
194
+
195
+ if subscription_type == SubscriptionType::SMS && !Phone.valid?(contact.mobile_phone)
196
+ raise InvalidMobilePhoneException.new("A valid mobile phone number required for SMS subscriptions")
197
+ end
198
+
199
+ if subscription_type == SubscriptionType::EMAIL && !EmailAddress.valid?(contact.email_address)
200
+ raise InvalidParameterException.new("A valid email address required for EMAIL subscriptions")
201
+ end
202
+ end
203
+
204
+ def validate_destroy_subscription_request(subscription_type, contact)
205
+ unless [SubscriptionType::SMS, SubscriptionType::EMAIL].include?(subscription_type)
206
+ raise InvalidParameterException.new("Invalid subscription type")
207
+ end
208
+
209
+ if contact.nil?
210
+ raise InvalidParameterException.new("A contact must be provided")
211
+ end
212
+
213
+ if subscription_type == SubscriptionType::SMS && !Phone.valid?(contact.mobile_phone)
214
+ raise InvalidMobilePhoneException.new("A valid mobile phone number required for SMS subscriptions")
215
+ end
216
+
217
+ if subscription_type == SubscriptionType::EMAIL && !EmailAddress.valid?(contact.email_address)
218
+ raise InvalidParameterException.new("A valid email address required for EMAIL subscriptions")
219
+ end
220
+ end
221
+
222
+ end
223
+ end
224
+