verticalresponse 0.1.1

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.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ VerticalResponse
2
+ ================
3
+
4
+ Gem used to make it easier to connect to VerticalResponse.com's API
data/lib/client.rb ADDED
@@ -0,0 +1,112 @@
1
+ # This is the base class for the VerticalResponse API.
2
+ # It contains common functionality that other classes can use to connect to the
3
+ # API and make REST calls to it.
4
+
5
+ require 'httparty'
6
+ require_relative 'response'
7
+
8
+ module VerticalResponse
9
+ module API
10
+ class Client
11
+ include HTTParty
12
+
13
+ format :json
14
+
15
+ class << self
16
+ def config
17
+ VerticalResponse::CONFIG
18
+ end
19
+
20
+ # Assign the headers required by our partner Mashery
21
+ def assign_headers(headers_info = {})
22
+ access_token = headers_info[:access_token]
23
+ add_default_query_param(:access_token, access_token)
24
+ end
25
+
26
+ def embed_resource(resource, resource_id = nil)
27
+ @embed_resource = resource
28
+ @embed_resource_id = resource_id if resource_id
29
+ end
30
+
31
+ # Returns the base URI of the VerticalResponse API.
32
+ # It builds the URI based on the values from the API configuration file.
33
+ # 'host' must be defined (unless host_uri is specified as an input param)
34
+ # otherwise the method will raise an exception.
35
+ def base_uri(host_uri = nil)
36
+ uri = host_uri
37
+ unless uri
38
+ unless VerticalResponse::CONFIG[:host]
39
+ raise ConfigurationError, 'Configuration option "host" must be defined.'
40
+ end
41
+
42
+ uri = URI::Generic.new(
43
+ VerticalResponse::CONFIG[:protocol] || 'http', # protocol scheme
44
+ nil, # user info
45
+ VerticalResponse::CONFIG[:host], # host
46
+ VerticalResponse::CONFIG[:port], # port
47
+ nil, # registry of naming authorities
48
+ nil, # path on server
49
+ nil, # opaque part
50
+ nil, # query data
51
+ nil # fragment (part of URI after '#' sign)
52
+ )
53
+ end
54
+
55
+ paths = ['api', 'v1']
56
+ paths << @embed_resource.to_s if @embed_resource
57
+ paths << @embed_resource_id.to_s if @embed_resource_id
58
+ URI.join(uri, File.join(*paths))
59
+ end
60
+
61
+ def build_params(params, query_params = {})
62
+ request_params = {}
63
+ request_params[:body] = params if params
64
+ # Add whatever query params we have as well
65
+ request_params.merge(build_query_params(query_params))
66
+ end
67
+
68
+ def build_query_params(params = {})
69
+ query_params = {}
70
+ # Include the default query params
71
+ params = params.merge(default_query_params)
72
+ query_params[:query] = params if params.any?
73
+ query_params
74
+ end
75
+
76
+ def default_query_params
77
+ @@default_query_params ||= {}
78
+ end
79
+
80
+ def add_default_query_param(param, value)
81
+ @@default_query_params ||= {}
82
+ @@default_query_params[param] = value
83
+ end
84
+
85
+ # Resource URI for the current class
86
+ def resource_uri(*additional_paths)
87
+ uri = base_uri
88
+ if additional_paths.any?
89
+ # Convert all additional paths to string
90
+ additional_paths = additional_paths.map do |path|
91
+ # We need to escape each path in case it contains caracters that
92
+ # are not appropriate to use as part of an URL.
93
+ # Unescape and then escape again in case the path is already escaped
94
+ URI::escape(URI::unescape(path.to_s))
95
+ end
96
+ uri = File.join(uri, *additional_paths)
97
+ end
98
+ uri
99
+ end
100
+ end
101
+
102
+ # Set default headers for OAuth authentication
103
+ assign_headers
104
+
105
+ attr_accessor :response
106
+
107
+ def initialize(response)
108
+ self.response = response
109
+ end
110
+ end
111
+ end
112
+ end
data/lib/contact.rb ADDED
@@ -0,0 +1,43 @@
1
+ # Class that represents a contact resource from the VerticalResponse API.
2
+ # It has the ability to make REST calls to the API, as well as to wrap
3
+ # the contact objects we get as response.
4
+ #
5
+ # NOTE: This class does not necessarily include all the available methods
6
+ # the API has for the contact resource. You can consider this an initial
7
+ # approach for an object oriented solution that you can expand according to
8
+ # your needs.
9
+
10
+ require_relative 'message'
11
+
12
+ module VerticalResponse
13
+ module API
14
+ class Contact < Resource
15
+ class << self
16
+ # Base URI for the Contact resource
17
+ def base_uri(*args)
18
+ @base_uri ||= File.join(super.to_s, 'contacts')
19
+ end
20
+
21
+ def fields(options = {})
22
+ Response.new get(resource_uri('fields'), build_query_params(options))
23
+ end
24
+ end
25
+
26
+ def initialize(*args)
27
+ super
28
+ @list_class = self.class.class_for_resource(List, id)
29
+ @message_class = self.class.class_for_resource(Message, id)
30
+ end
31
+
32
+ # Returns all the lists this contact belongs to
33
+ def lists(options = {})
34
+ @list_class.all(options)
35
+ end
36
+
37
+ # Returns all the messages targetted to the current contact
38
+ def messages(options = {})
39
+ @message_class.all(options)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ # Class that represents a custom field resource from the VerticalResponse API.
2
+ # It has the ability to make REST calls to the API, as well as to wrap
3
+ # the custom field objects we get as response.
4
+ #
5
+ # NOTE: This class does not necessarily include all the available methods
6
+ # the API has for the custom field resource. You can consider this an initial approach
7
+ # for an object oriented solution that you can expand according to your needs.
8
+
9
+ require_relative 'resource'
10
+
11
+ module VerticalResponse
12
+ module API
13
+ class CustomField < Resource
14
+ class << self
15
+ # Base URI for the Message resource
16
+ def base_uri(*args)
17
+ @base_uri ||= File.join(super.to_s, 'custom_fields')
18
+ end
19
+
20
+ def id_regexp
21
+ /[a-z0-9 _%]{1,255}/i
22
+ end
23
+
24
+ def id_attribute_name
25
+ 'name'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/email.rb ADDED
@@ -0,0 +1,110 @@
1
+ # Class that represents an email resource from the VerticalResponse API.
2
+ # It has the ability to make REST calls to the API, as well as to wrap
3
+ # the email objects we get as response.
4
+ #
5
+ # NOTE: This class does not necessarily include all the available methods
6
+ # the API has for the email resource. You can consider this an initial approach
7
+ # for an object oriented solution that you can expand according to your needs.
8
+
9
+ require_relative 'resource'
10
+ require_relative 'list'
11
+
12
+ module VerticalResponse
13
+ module API
14
+ class Email < Resource
15
+ class << self
16
+ # Base URI for the Email resource
17
+ def base_uri(*args)
18
+ @base_uri ||= File.join(super.to_s, 'messages', 'emails')
19
+ end
20
+
21
+ # Overwrite from parent class since it's a special type of
22
+ # resource name (with messages at the beginning)
23
+ def resource_name
24
+ 'messages/emails'
25
+ end
26
+
27
+ # The Email API does not support the 'all' method on its own for now.
28
+ # To get all emails we need to do it through the Message API
29
+ def all(options = {})
30
+ Message.all(options.merge({ :message_type => MESSAGE_TYPE }))
31
+ end
32
+ end
33
+
34
+ MESSAGE_TYPE = 'email'
35
+
36
+ def initialize(*args)
37
+ super
38
+ @list_class = self.class.class_for_resource(List, id)
39
+ end
40
+
41
+ # Returns all the lists this email is targeted to
42
+ def lists(options = {})
43
+ @list_class.all(options)
44
+ end
45
+
46
+ def test_launch(params = {})
47
+ Response.new self.class.post(
48
+ self.class.resource_uri(id, 'test'),
49
+ self.class.build_params(params)
50
+ )
51
+ end
52
+
53
+ # Launches an email and return the response object
54
+ def launch(params = {})
55
+ # Supports receiving an array of List objects (Object Oriented)
56
+ lists = params.delete(:lists)
57
+ if lists
58
+ params[:list_ids] ||= []
59
+ params[:list_ids] += lists.map do |list|
60
+ list.respond_to?(:id) ? list.id : list.to_i
61
+ end
62
+ # Remove duplicate IDs, if any
63
+ params[:list_ids].uniq!
64
+ end
65
+
66
+ Response.new self.class.post(
67
+ self.class.resource_uri(id),
68
+ self.class.build_params(params)
69
+ )
70
+ end
71
+
72
+ def unschedule(params = {})
73
+ Response.new self.class.post(
74
+ self.class.resource_uri(id, 'unschedule'),
75
+ self.class.build_params(params)
76
+ )
77
+ end
78
+
79
+ def opens_stats(options = {})
80
+ detailed_stat(:opens)
81
+ end
82
+
83
+ def clicks_stats(options = {})
84
+ detailed_stat(:clicks)
85
+ end
86
+
87
+ def unsubscribes_stats(options = {})
88
+ detailed_stat(:unsubscribes)
89
+ end
90
+
91
+ private
92
+
93
+ def detailed_stat(stat_name, options = {})
94
+ stat_name = stat_name.to_s
95
+ unless %w(opens clicks unsubscribes).include?(stat_name)
96
+ raise NotImplementedError,
97
+ "'#{stat_name}' stat is not supported for the #{class_name} class"
98
+ end
99
+
100
+ if response.links && response.links.has_key?(stat_name)
101
+ uri = response.links[stat_name]['url']
102
+ else
103
+ uri = self.class.resource_uri(id, 'stats', stat_name)
104
+ end
105
+
106
+ Response.new self.class.get(uri, self.class.build_query_params(options))
107
+ end
108
+ end
109
+ end
110
+ end
data/lib/error.rb ADDED
@@ -0,0 +1,11 @@
1
+ # Client errors that can be raised from VerticalResponse API response errors.
2
+
3
+ module VerticalResponse
4
+ module API
5
+ class Error < StandardError
6
+ attr_accessor :code, :api_response
7
+ end
8
+
9
+ class ConfigurationError < Error; end
10
+ end
11
+ end
data/lib/list.rb ADDED
@@ -0,0 +1,69 @@
1
+ # Class that represents a list resource from the VerticalResponse API.
2
+ # It has the ability to make REST calls to the API, as well as to wrap
3
+ # the list objects we get as response.
4
+ #
5
+ # NOTE: This class does not necessarily include all the available methods
6
+ # the API has for the list resource. You can consider this an initial approach
7
+ # for an object oriented solution that you can expand according to your needs.
8
+
9
+ require_relative 'contact'
10
+
11
+ module VerticalResponse
12
+ module API
13
+ class List < Resource
14
+ class << self
15
+ # Base URI for the List resource
16
+ def base_uri(*args)
17
+ @base_uri ||= File.join(super.to_s, 'lists')
18
+ end
19
+ end
20
+
21
+ def initialize(*args)
22
+ super
23
+ @contact_class = self.class.class_for_resource(Contact, id)
24
+ @message_class = self.class.class_for_resource(Message, id)
25
+ end
26
+
27
+ # Returns all the messages targetted to the current list
28
+ def messages(options = {})
29
+ @message_class.all(options)
30
+ end
31
+
32
+ # Returns all the contacts that belong to the list
33
+ def contacts(options = {})
34
+ @contact_class.all(options)
35
+ end
36
+
37
+ def find_contact(contact_id, options = {})
38
+ @contact_class.find(contact_id, options)
39
+ end
40
+
41
+ # Creates a contact for the list with the parameters provided
42
+ def create_contact(params)
43
+ @contact_class.create(params)
44
+ end
45
+
46
+ # Creates contacts in batch for the list with the parameters provided
47
+ def create_contacts(params)
48
+ params = { :contacts => params } if params.is_a?(Array)
49
+ @contact_class.create(params)
50
+ end
51
+
52
+ # Deletes a contact from the list
53
+ def delete_contact(contact)
54
+ # Make a copy of the original contact but embedding the request
55
+ # within the list resource
56
+ contact_to_delete = @contact_class.new(contact.response)
57
+ contact_to_delete.delete
58
+ end
59
+
60
+ # Deletes contacts in batch from the list
61
+ def delete_contacts(contact_emails)
62
+ Response.new @contact_class.delete(
63
+ @contact_class.resource_uri,
64
+ self.class.build_params({ :contacts => contact_emails })
65
+ )
66
+ end
67
+ end
68
+ end
69
+ end
data/lib/message.rb ADDED
@@ -0,0 +1,44 @@
1
+ # Class that represents a message resource from the VerticalResponse API.
2
+ # It has the ability to make REST calls to the API, as well as to wrap
3
+ # the message objects we get as response.
4
+ #
5
+ # NOTE: This class does not necessarily include all the available methods
6
+ # the API has for the message resource. You can consider this an initial approach
7
+ # for an object oriented solution that you can expand according to your needs.
8
+
9
+ require_relative 'email'
10
+ require_relative 'social_post'
11
+
12
+ module VerticalResponse
13
+ module API
14
+ class Message < Resource
15
+ class << self
16
+ # Base URI for the Message resource
17
+ def base_uri(*args)
18
+ @base_uri ||= File.join(super.to_s, 'messages')
19
+ end
20
+
21
+ # Overwritting this method from the parent class since we want to
22
+ # return object instances depending on the message type
23
+ def object_collection(response)
24
+ response.handle_collection do |response_item|
25
+ message_class = self
26
+ if response_item.attributes && response_item.attributes.has_key?('message_type')
27
+ type = response_item.attributes['message_type'].downcase.gsub(' ', '_')
28
+ if type == Email::MESSAGE_TYPE
29
+ message_class = Email
30
+ elsif type == SocialPost::MESSAGE_TYPE
31
+ message_class = SocialPost
32
+ end
33
+ end
34
+ message_class.new(response_item)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Remove methods that are not supported by the Message API.
40
+ # Message only supports the 'all' method for now
41
+ exclude_methods :create, :find, :update, :delete, :stats
42
+ end
43
+ end
44
+ end
data/lib/oauth.rb ADDED
@@ -0,0 +1,67 @@
1
+ # This class provides users the ability to generate oAuth tokens for the
2
+ # VerticalResponse API.
3
+ #
4
+ # Check the examples/sample code to know how to use this class.
5
+
6
+ require_relative 'client'
7
+
8
+ module VerticalResponse
9
+ module API
10
+ class OAuth < Client
11
+ # We expect HTML format as the API might redirect to a signin page or
12
+ # return errors in HTML format
13
+ format :html
14
+
15
+ def initialize(access_token)
16
+ @access_token = access_token
17
+ end
18
+
19
+ def lists
20
+ VerticalResponse::API::List.all({"access_token" => @access_token})
21
+ end
22
+
23
+ def contacts
24
+ VerticalResponse::API::Contact.all({"access_token" => @access_token})
25
+ end
26
+
27
+ def get_list(list_id)
28
+ VerticalResponse::API::List.find(list_id, {"access_token" => @access_token})
29
+ end
30
+
31
+ class << self
32
+ # Overwrite this method as we don't need to setup headers for
33
+ # OAuth calls
34
+ def assign_headers(*args)
35
+ end
36
+
37
+ # Base URI for the OAuth calls
38
+ def base_uri(*args)
39
+ @base_uri ||= File.join(super.to_s, 'oauth')
40
+ end
41
+
42
+ # client_id is the application key
43
+ def authorize(redirect_uri = "", client_id = "")
44
+ get(
45
+ resource_uri('authorize'),
46
+ build_query_params({ :client_id => client_id, :redirect_uri => redirect_uri })
47
+ )
48
+ end
49
+
50
+ def access_token(auth_code,
51
+ redirect_uri = "",
52
+ client_id = "",
53
+ client_secret = "")
54
+ get(
55
+ resource_uri('access_token'),
56
+ build_query_params({
57
+ :client_id => client_id,
58
+ :client_secret => client_secret,
59
+ :code => auth_code,
60
+ :redirect_uri => redirect_uri
61
+ })
62
+ )
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end