verticalresponse 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +4 -0
- data/lib/client.rb +112 -0
- data/lib/contact.rb +43 -0
- data/lib/custom_field.rb +30 -0
- data/lib/email.rb +110 -0
- data/lib/error.rb +11 -0
- data/lib/list.rb +69 -0
- data/lib/message.rb +44 -0
- data/lib/oauth.rb +67 -0
- data/lib/resource.rb +152 -0
- data/lib/response.rb +48 -0
- data/lib/social_post.rb +37 -0
- data/lib/verticalresponse.rb +21 -0
- metadata +59 -0
data/README.md
ADDED
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
|
data/lib/custom_field.rb
ADDED
@@ -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
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
|