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.
- 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
|