verticalresponse 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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