klaviyo 2.0.6

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c50780aa07007d13ffcbfd5e88692dc46122641627511be21f4988012d48d20e
4
+ data.tar.gz: 453757a911094db7394000a2aa59d4a0f31a3cb6aa9196e50a749dac3e51b336
5
+ SHA512:
6
+ metadata.gz: 62278b5560afe418dc8658e71646f36905cdb10c2b607b923377ea08c8ea247630fc782c6dbceda1d4fe2a88f5f332995abce6dcf90484cfbc1791516d4b6cd3
7
+ data.tar.gz: 1358f2f45b1e92144f8a43f0f52a291f3ee859af3e416924e003a1b7c7a02553965d6240fecf8289eb41167c422565c8909b57344b8baed9091c6f022801183b
data/klaviyo.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ files = ['klaviyo.gemspec', '{lib}/**/**/*'].map {|f| Dir[f]}.flatten
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'klaviyo'
5
+ s.version = '2.0.6'
6
+ s.date = '2021-04-08'
7
+ s.summary = 'You heard us, a Ruby wrapper for the Klaviyo API'
8
+ s.description = 'Ruby wrapper for the Klaviyo API'
9
+ s.authors = ['Klaviyo Team']
10
+ s.email = 'libraries@klaviyo.com'
11
+ s.files = files
12
+ s.homepage = 'https://www.klaviyo.com/'
13
+ s.require_path = 'lib'
14
+ s.add_dependency 'json'
15
+ s.add_dependency 'rack'
16
+ s.add_dependency 'escape'
17
+ s.add_dependency 'faraday'
18
+ end
@@ -0,0 +1,41 @@
1
+ module Klaviyo
2
+ class Campaigns < Client
3
+ CANCEL = 'cancel'
4
+ CAMPAIGN = 'campaign'
5
+ CAMPAIGNS = 'campaigns'
6
+ SEND = 'send'
7
+
8
+ # Retrieves all the campaigns from Klaviyo account
9
+ # @return [List] of JSON formatted campaing objects
10
+ def self.get_campaigns()
11
+ v1_request(HTTP_GET, CAMPAIGNS)
12
+ end
13
+
14
+ # Retrieves the details of the list
15
+ # @param campaign_id the if of campaign
16
+ # @return [JSON] a JSON object containing information about the campaign
17
+ def self.get_campaign_details(campaign_id)
18
+ path = "#{CAMPAIGN}/#{campaign_id}"
19
+
20
+ v1_request(HTTP_GET, path)
21
+ end
22
+
23
+ # Sends the campaign immediately
24
+ # @param campaign_id [String] the id of campaign
25
+ # @return will return with HTTP ok in case of success
26
+ def self.send_campaign(campaign_id)
27
+ path = "#{CAMPAIGN}/#{campaign_id}/#{SEND}"
28
+
29
+ v1_request(HTTP_POST, path)
30
+ end
31
+
32
+ # Cancels the campaign with specified campaign_id
33
+ # @param campaign_id [String] the id of campaign
34
+ # @return [JSON] a JSON object containing the campaign details
35
+ def self.cancel_campaign(campaign_id)
36
+ path = "#{CAMPAIGN}/#{campaign_id}/#{CANCEL}"
37
+
38
+ v1_request(HTTP_POST, path)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ module Klaviyo
2
+ class DataPrivacy < Client
3
+ DATA_PRIVACY = 'data-privacy'
4
+ DELETION_REQUEST = 'deletion-request'
5
+
6
+ # Submits a data privacy-related deletion request
7
+ # @param id_type [String] 'email' or 'phone_number' or 'person_id
8
+ # @param identifier [String] value for the identifier specified
9
+ # @return a dictionary with a confirmation that deletion task submitted for the customer
10
+ def self.request_profile_deletion(id_type, identifier)
11
+ unless ['email', 'phone_number', 'person_id'].include? id_type
12
+ raise Klaviyo::KlaviyoError.new(INVALID_ID_TYPE_ERROR)
13
+ end
14
+ identifier = { id_type => identifier }
15
+ path = "#{DATA_PRIVACY}/#{DELETION_REQUEST}"
16
+ v2_request(HTTP_POST, path, identifier)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module Klaviyo
2
+ class EmailTemplates < Client
3
+ EMAIL_TEMPLATES = 'email-templates'
4
+ EMAIL_TEMPLATE = 'email-template'
5
+ CLONE = 'clone'
6
+ RENDER = 'render'
7
+ SEND = 'send'
8
+
9
+ # Returns a list of all the email templates you've created.
10
+ # The templates are returned in sorted order by name.
11
+ # @return [List] of JSON formatted email template objects
12
+ def self.get_templates()
13
+ v1_request(HTTP_GET, EMAIL_TEMPLATES)
14
+ end
15
+
16
+ # Creates a new email template
17
+ # @param :name [String] The name of the email template
18
+ # @param :html [String] The HTML content for this template
19
+ # @return [JSON] a JSON object containing information about the email template
20
+ def self.create_template(name: nil, html: nil)
21
+ params = {
22
+ name: name,
23
+ html: html
24
+ }
25
+ v1_request(HTTP_POST, EMAIL_TEMPLATES, content_type: CONTENT_URL_FORM, params: params)
26
+ end
27
+
28
+ # Updates the name and/or HTML content of a template. Only updates imported
29
+ # HTML templates; does not currently update drag & drop templates
30
+ # @param template_id [String] The id of the email template
31
+ # @param :name [String] The name of the email template
32
+ # @param :html [String] The HTML content for this template
33
+ # @return [JSON] a JSON object containing information about the email template
34
+ def self.update_template(template_id, name:, html:)
35
+ path = "#{EMAIL_TEMPLATE}/#{template_id}"
36
+ params = {
37
+ name: name,
38
+ html: html
39
+ }
40
+ v1_request(HTTP_PUT, path, params)
41
+ end
42
+
43
+ # Deletes a given template.
44
+ # @param template_id [String] The id of the email template
45
+ # @return [JSON] a JSON object containing information about the email template
46
+ def self.delete_template(template_id)
47
+ path = "#{EMAIL_TEMPLATE}/#{template_id}"
48
+ v1_request(HTTP_DELETE, path)
49
+ end
50
+
51
+ # Creates a copy of a given template with a new name
52
+ # @param template_id [String] The id of the email template to copy
53
+ # @param :name [String] The name of the newly cloned email template
54
+ # @return [JSON] a JSON object containing information about the email template
55
+ def self.clone_template(template_id, name:)
56
+ path = "#{EMAIL_TEMPLATE}/#{template_id}/#{CLONE}"
57
+ params = {
58
+ name: name
59
+ }
60
+ v1_request(HTTP_POST, path, content_type: CONTENT_URL_FORM, params: params)
61
+ end
62
+
63
+ # Renders the specified template with the provided data and return HTML
64
+ # and text versions of the email
65
+ # @param template_id [String] The id of the email template to copy
66
+ # @param :context [Hash] The context the email template will be rendered with
67
+ # @return [JSON] a JSON object containing information about the email template
68
+ def self.render_template(template_id, context: {})
69
+ path = "#{EMAIL_TEMPLATE}/#{template_id}/#{RENDER}"
70
+ params = {
71
+ context: context
72
+ }
73
+ v1_request(HTTP_POST, path, content_type: CONTENT_URL_FORM, params: params)
74
+ end
75
+
76
+ # Renders the specified template with the provided data and then send the
77
+ # contents in an email via the service specified
78
+ # @param template_id [String] The id of the email template to copy
79
+ # @param :from_email [String] The from email address; used in the reply-to header
80
+ # @param :from_name [String] The name the email is sent from
81
+ # @param :subject [String] The subject of the email template
82
+ # @param :to [Mixed] The email this template is being sent to
83
+ # @param :context [Hash] The context the email template will be rendered with
84
+ # @return [JSON] a JSON object containing information about the email template
85
+ def self.send_template(template_id, from_email:, from_name:, subject:, to:, context: {})
86
+ path = "#{EMAIL_TEMPLATE}/#{template_id}/#{SEND}"
87
+ params = {
88
+ from_email: from_email,
89
+ from_name: from_name,
90
+ subject: subject,
91
+ to: to,
92
+ context: context
93
+ }
94
+ v1_request(HTTP_POST, path, content_type: CONTENT_URL_FORM, params: params)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,166 @@
1
+ module Klaviyo
2
+ class Lists < Client
3
+ EXCLUSIONS = 'exclusions'
4
+ GROUP = 'group'
5
+ LIST = 'list'
6
+ LISTS = 'lists'
7
+ MEMBERS = 'members'
8
+ SUBSCRIBE = 'subscribe'
9
+
10
+ # Creates a new list
11
+ # @param list_name [String] the list name
12
+ # @return will return with HTTP OK on success
13
+ def self.create_list(list_name)
14
+ body = {
15
+ :list_name => list_name
16
+ }
17
+ v2_request(HTTP_POST, LISTS, body)
18
+ end
19
+
20
+ # Retrieves all the lists in the Klaviyo account
21
+ # @return [List] a list of JSON objects of the name and id for each list
22
+ def self.get_lists()
23
+ v2_request(HTTP_GET, LISTS)
24
+ end
25
+
26
+ # Retrieves the details of the list
27
+ # @param list_id [String] the id of the list
28
+ # @return [JSON] a JSON object containing information about the list
29
+ def self.get_list_details(list_id)
30
+ path = "#{LIST}/#{list_id}"
31
+ v2_request(HTTP_GET, path)
32
+ end
33
+
34
+ # Updates the properties of a list
35
+ # @param list_id [String] the id of the list
36
+ # @param list_name [String] the new name of the list
37
+ # @return will return with HTTP OK on success
38
+ def self.update_list_details(list_id, list_name)
39
+ path = "#{LIST}/#{list_id}"
40
+ body = {
41
+ :list_name => list_name
42
+ }
43
+ v2_request(HTTP_PUT, path, body)
44
+ end
45
+
46
+ # Deletes a list
47
+ # @param list_id [String] the id of the list
48
+ # @return will return with HTTP OK on success
49
+ def self.delete_list(list_id)
50
+ path = "#{LIST}/#{list_id}"
51
+ v2_request(HTTP_DELETE, path)
52
+ end
53
+
54
+ # Check if profiles are in a list and not supressed
55
+ # @param list_id [String] the id of the list
56
+ # @param :emails [List] the emails of the profiles to check
57
+ # @param :phone_numbers [List] the phone numbers of the profiles to check
58
+ # @param :push_tokens [List] push tokens of the profiles to check
59
+ # @return A list of JSON objects of the profiles. Profiles that are
60
+ # supressed or not found are not included.
61
+ def self.check_list_subscriptions(list_id, emails: [], phone_numbers: [], push_tokens: [])
62
+ path = "#{LIST}/#{list_id}/#{SUBSCRIBE}"
63
+ params = {
64
+ :emails => emails,
65
+ :phone_numbers => phone_numbers,
66
+ :push_tokens => push_tokens
67
+ }
68
+ v2_request(HTTP_GET, path, params)
69
+ end
70
+
71
+ # Subscribe profiles to a list.
72
+ # @param list_id [String] the id of the list
73
+ # @param profiles [List] a list of JSON objects. Each object requires either
74
+ # an email or phone number key.
75
+ # @return will retun HTTP OK on success. If the list is single opt-in then a
76
+ # list of records containing the email address, phone number, push token,
77
+ # and the corresponding profile ID will also be included.
78
+ def self.add_subscribers_to_list(list_id, profiles: [])
79
+ path = "#{LIST}/#{list_id}/#{SUBSCRIBE}"
80
+ params = {
81
+ :profiles => profiles
82
+ }
83
+ v2_request(HTTP_POST, path, params)
84
+ end
85
+
86
+ # Unsubscribe and remove profiles from a list
87
+ # @param list_id [String] the id of the list
88
+ # @param :emails [List] the emails of the profiles to check
89
+ # @return will return with HTTP OK on success
90
+ def self.unsubscribe_from_list(list_id, emails: [])
91
+ path = "#{LIST}/#{list_id}/#{SUBSCRIBE}"
92
+ params = {
93
+ :emails => emails
94
+ }
95
+ v2_request(HTTP_DELETE, path, params)
96
+ end
97
+
98
+ # Add profiles to a list
99
+ # @param list_id [String] the id of the list
100
+ # @param :profiles [List] A list of JSON objects. Each object is a profile
101
+ # that will be added to the list
102
+ # @return will return with HTTP OK on success and a list of records of the
103
+ # corresponding profile id
104
+ def self.add_to_list(list_id, profiles: [])
105
+ path = "#{LIST}/#{list_id}/#{MEMBERS}"
106
+ params = {
107
+ :profiles => profiles
108
+ }
109
+ v2_request(HTTP_POST, path, params)
110
+ end
111
+
112
+ # Check if profiles are on a list
113
+ # @param list_id [String] the id of the list
114
+ # @param :emails [List] the emails of the profiles to check
115
+ # @param :phone_numbers [List] the phone numbers of the profiles to check
116
+ # @param :push_tokens [List] push tokens of the profiles to check
117
+ # @return A list of JSON objects of the profiles. Profiles that are
118
+ # supressed or not found are not included.
119
+ def self.check_list_memberships(list_id, emails: [], phone_numbers: [], push_tokens: [])
120
+ path = "#{LIST}/#{list_id}/#{MEMBERS}"
121
+ params = {
122
+ :emails => emails,
123
+ :phone_numbers => phone_numbers,
124
+ :push_tokens => push_tokens
125
+ }
126
+ v2_request(HTTP_GET, path, params)
127
+ end
128
+
129
+ # Remove profiles from a list
130
+ # @param list_id [String] the id of the list
131
+ # @param :emails [List] the emails of the profiles to check
132
+ # @param :phone_numbers [List] the phone numbers of the profiles to check
133
+ # @param :push_tokens [List] push tokens of the profiles to check
134
+ # @return will return with HTTP OK on success
135
+ def self.remove_from_list(list_id, emails: [], phone_numbers: [], push_tokens: [])
136
+ path = "#{LIST}/#{list_id}/#{MEMBERS}"
137
+ params = {
138
+ :emails => emails,
139
+ :phone_numbers => phone_numbers,
140
+ :push_tokens => push_tokens
141
+ }
142
+ v2_request(HTTP_DELETE, path, params)
143
+ end
144
+
145
+ # Get all emails, phone numbers, along with reasons for list exclusion
146
+ # @param list_id [String] the id of the list
147
+ # @param marker [Integer] a marker from a previous call to get the next batch
148
+ # @return [List] A list of JSON object for each profile with the reason for exclusion
149
+ def self.get_list_exclusions(list_id, marker: nil)
150
+ path = "#{LIST}/#{list_id}/#{EXCLUSIONS}/#{ALL}"
151
+ params = {
152
+ :marker => marker
153
+ }
154
+ v2_request(HTTP_GET, path, params)
155
+ end
156
+
157
+ # Get all of the emails, phone numbers, and push tokens for profiles in a given list or segment
158
+ # @param list_id [String] the id of the list
159
+ # @param marker [Integer] a marker from a previous call to get the next batch
160
+ # @return [List] A list of JSON objects for each profile with the id, email, phone number, and push token
161
+ def self.get_group_members(list_id)
162
+ path = "#{GROUP}/#{list_id}/#{MEMBERS}/#{ALL}"
163
+ v2_request(HTTP_GET, path)
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,80 @@
1
+ module Klaviyo
2
+ class Metrics < Client
3
+ EXPORT = 'export'
4
+
5
+ # Returns a list of all metrics in Klaviyo
6
+ # @param page [Integer] which page to return, default 0
7
+ # @param count [Integer] number of results to return, default 100
8
+ # @return a dictionary with a data property that contains an array of all the metrics
9
+ def self.get_metrics(page: DEFAULT_PAGE, count: DEFAULT_COUNT)
10
+ params = {
11
+ :page => page,
12
+ :count => count
13
+ }
14
+ v1_request(HTTP_GET, METRICS, params)
15
+ end
16
+
17
+ # Returns a batched timeline of all events in your Klaviyo account.
18
+ # @param since [Integer or String] either a Unix timestamp or the UUID from a previous request. Default is the current time.
19
+ # @param count [Integer] number of results to return, default 100
20
+ # @param sort [String] 'asc' or 'desc', sort order to apply to the timeline. Default is 'desc'.
21
+ # @return a dictionary with a data property that contains an array of the metrics
22
+ def self.get_metrics_timeline(since: nil, count: DEFAULT_COUNT, sort: DEFAULT_SORT_DESC)
23
+ path = "#{METRICS}/#{TIMELINE}"
24
+ params = {
25
+ :since => since,
26
+ :count => count,
27
+ :sort => sort
28
+ }
29
+ v1_request(HTTP_GET, path, params)
30
+ end
31
+
32
+ # Returns a batched timeline for one specific type of metric.
33
+ # @param metric_id [String] the id of the metric
34
+ # @param since [Integer or String] either a Unix timestamp or the UUID from a previous request. Default is the current time.
35
+ # @param count [Integer] number of results to return, default 100
36
+ # @param sort [String] 'asc' or 'desc', sort order to apply to the timeline. Default is 'desc'.
37
+ # @return a dictionary with a data property that contains information about what metric the event tracks
38
+ def self.get_metric_timeline(metric_id, since: nil, count: DEFAULT_COUNT, sort: DEFAULT_SORT_DESC)
39
+ path = "#{METRIC}/#{metric_id}/#{TIMELINE}"
40
+ params = {
41
+ :since => since,
42
+ :count => count,
43
+ :sort => sort
44
+ }
45
+ v1_request(HTTP_GET, path, params)
46
+ end
47
+
48
+ # Export event data, optionally filtering and segmented on available event properties
49
+ # @param metric_id [String] the id of the metric
50
+ # @param start_date [String] Beginning of the timeframe to pull event data for. Default is 1 month ago
51
+ # @param end_date [String] End of the timeframe to pull event data for. Default is the current day
52
+ # @param unit [String] Granularity to bucket data points into - one of ‘day’, ‘week’, or ‘month’. Defaults to ‘day’.
53
+ # @param measurement [String or JSON-encoded list] Type of metric to fetch
54
+ # @param where [JSON-encoded list] Conditions to use to filter the set of events. A max of 1 condition can be given.
55
+ # @param by [String] The name of a property to segment the event data on. Where and by parameters cannot be specified at the same time.
56
+ # @param count [Integer] Maximum number of segments to return. The default value is 25.
57
+ # @return A dictionary relecting the input request parameters as well as a results property
58
+ def self.get_metric_export(metric_id,
59
+ start_date: nil,
60
+ end_date: nil,
61
+ unit: nil,
62
+ measurement: nil,
63
+ where: nil,
64
+ by: nil,
65
+ count: nil
66
+ )
67
+ path = "#{METRIC}/#{metric_id}/#{EXPORT}"
68
+ params = {
69
+ :start_date => start_date,
70
+ :end_date => end_date,
71
+ :unit => unit,
72
+ :measurement => measurement,
73
+ :where => where,
74
+ :by => by,
75
+ :count => count
76
+ }
77
+ v1_request(HTTP_GET, path, params)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,68 @@
1
+ module Klaviyo
2
+ class Profiles < Client
3
+ PERSON = 'person'
4
+ PEOPLE = 'people'
5
+ SEARCH = 'search'
6
+
7
+ # Retrieves the id of the profile given email
8
+ # @param email [String] the email of the profile
9
+ # @return [JSON] a JSON object containing id of the profile
10
+ def self.get_profile_id_by_email(email)
11
+ path = "#{PEOPLE}/#{SEARCH}"
12
+ params = {
13
+ :email => email
14
+ }
15
+ v2_request(HTTP_GET, path, params)
16
+ end
17
+
18
+ # Retrieve all the data attributes for a Klaviyo Person ID.
19
+ # @param person_id [String] the id of the profile
20
+ # @return returns a person object
21
+ def self.get_person_attributes(person_id)
22
+ path = "#{PERSON}/#{person_id}"
23
+ v1_request(HTTP_GET, path)
24
+ end
25
+
26
+ # Add or update one more more attributes for a Person
27
+ # @param person_id [String] the id of the profile
28
+ # @param kwargs [Key/value pairs] attributes to add/update in the profile
29
+ # @return returns the updated person object
30
+ def self.update_person_attributes(person_id, kwargs = {})
31
+ path = "#{PERSON}/#{person_id}"
32
+ v1_request(HTTP_PUT, path, kwargs)
33
+ end
34
+
35
+ # Listing a person's event timeline
36
+ # @param person_id [String] the id of the profile
37
+ # @param since [Integer or String] either a Unix timestamp or the UUID from a previous request. Default is the current time.
38
+ # @param count [Integer] number of results to return, default 100
39
+ # @param sort [String] 'asc' or 'desc', sort order to apply to the timeline. Default is 'desc'.
40
+ # @return returns a dictionary containing a list of metric event objects
41
+ def self.get_person_metrics_timeline(person_id, since: nil, count: DEFAULT_COUNT, sort: DEFAULT_SORT_DESC)
42
+ path = "#{PERSON}/#{person_id}/#{METRICS}/#{TIMELINE}"
43
+ params = {
44
+ :since => since,
45
+ :count => count,
46
+ :sort => sort
47
+ }
48
+ v1_request(HTTP_GET, path, params)
49
+ end
50
+
51
+ # Listing a person's event timeline for a particular metric
52
+ # @param person_id [String] the id of the profile
53
+ # @param metric_id [String] the id of the metric
54
+ # @param since [Integer or String] either a Unix timestamp or the UUID from a previous request. Default is the current time.
55
+ # @param count [Integer] number of results to return, default 100
56
+ # @param sort [String] 'asc' or 'desc', sort order to apply to the timeline. Default is 'desc'.
57
+ # @return returns a dictionary containing a list of metric event objects
58
+ def self.get_person_metric_timeline(person_id, metric_id, since: nil, count: DEFAULT_COUNT, sort: DEFAULT_SORT_DESC)
59
+ path = "#{PERSON}/#{person_id}/#{METRIC}/#{metric_id}/#{TIMELINE}"
60
+ params = {
61
+ :since => since,
62
+ :count => count,
63
+ :sort => sort
64
+ }
65
+ v1_request(HTTP_GET, path, params)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,79 @@
1
+ module Klaviyo
2
+ class Public < Client
3
+ # Used for identifying customers and managing profile properties
4
+ #
5
+ # @kwarg :id [String] the customer or profile id
6
+ # @kwarg :email [String] the customer or profile email
7
+ # @kwarg :properties [Hash] properties of the profile to add or update
8
+ def self.identify(kwargs = {})
9
+ defaults = {:id => nil,
10
+ :email => nil,
11
+ :phone_number => nil,
12
+ :properties => {}
13
+ }
14
+ kwargs = defaults.merge(kwargs)
15
+
16
+ unless check_required_args(kwargs)
17
+ return
18
+ end
19
+
20
+ properties = kwargs[:properties]
21
+ properties[:email] = kwargs[:email] unless kwargs[:email].to_s.empty?
22
+ properties[:$phone_number] = kwargs[:phone_number] unless kwargs[:phone_number].to_s.empty?
23
+ properties[:id] = kwargs[:id] unless kwargs[:id].to_s.empty?
24
+
25
+ params = {
26
+ :token => Klaviyo.public_api_key,
27
+ :properties => properties
28
+ }
29
+
30
+ public_request(HTTP_GET, 'identify', params)
31
+ end
32
+
33
+ # Used for tracking events and customer behaviors
34
+ #
35
+ # @param event [String] the event to track
36
+ # @kwarg :id [String] the customer or profile id
37
+ # @kwarg :email [String] the customer or profile email
38
+ # @kwarg :phone_number [String] the customer or profile phone number
39
+ # @kwarg :properties [Hash] properties of the event
40
+ # @kwargs :customer_properties [Hash] properties of the customer or profile
41
+ # @kwargs :time [Integer] timestamp of the event
42
+ def self.track(event, kwargs = {})
43
+ defaults = {
44
+ :id => nil,
45
+ :email => nil,
46
+ :phone_number => nil,
47
+ :properties => {},
48
+ :customer_properties => {},
49
+ :time => nil
50
+ }
51
+
52
+ kwargs = defaults.merge(kwargs)
53
+
54
+ unless check_required_args(kwargs)
55
+ return
56
+ end
57
+
58
+ customer_properties = kwargs[:customer_properties]
59
+ customer_properties[:email] = kwargs[:email] unless kwargs[:email].to_s.empty?
60
+ customer_properties[:$phone_number] = kwargs[:phone_number] unless kwargs[:phone_number].to_s.empty?
61
+ customer_properties[:id] = kwargs[:id] unless kwargs[:id].to_s.empty?
62
+
63
+ params = {
64
+ :token => Klaviyo.public_api_key,
65
+ :event => event,
66
+ :properties => kwargs[:properties],
67
+ :customer_properties => customer_properties
68
+ }
69
+ params[:time] = kwargs[:time] if kwargs[:time]
70
+
71
+ public_request(HTTP_GET, 'track', **params)
72
+ end
73
+
74
+ def self.track_once(event, kwargs = {})
75
+ kwargs.update('__track_once__' => true)
76
+ track(event, kwargs)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,114 @@
1
+ module Klaviyo
2
+ class Client
3
+ BASE_API_URL = 'https://a.klaviyo.com/api'
4
+ V1_API = 'v1'
5
+ V2_API = 'v2'
6
+
7
+ HTTP_DELETE = 'delete'
8
+ HTTP_GET = 'get'
9
+ HTTP_POST = 'post'
10
+ HTTP_PUT = 'put'
11
+
12
+ ALL = 'all'
13
+ METRIC = 'metric'
14
+ METRICS = 'metrics'
15
+ TIMELINE = 'timeline'
16
+
17
+ DEFAULT_COUNT = 100
18
+ DEFAULT_PAGE = 0
19
+ DEFAULT_SORT_DESC = 'desc'
20
+
21
+ CONTENT_JSON = 'application/json'
22
+ CONTENT_URL_FORM = 'application/x-www-form-urlencoded'
23
+
24
+ private
25
+
26
+ def self.request(method, path, content_type, **kwargs)
27
+ check_private_api_key_exists()
28
+ url = "#{BASE_API_URL}/#{path}"
29
+ connection = Faraday.new(
30
+ url: url,
31
+ headers: {
32
+ 'Content-Type' => content_type
33
+ })
34
+ if content_type == CONTENT_JSON
35
+ kwargs[:body] = kwargs[:body].to_json
36
+ end
37
+ response = connection.send(method) do |req|
38
+ req.body = kwargs[:body] || nil
39
+ end
40
+ end
41
+
42
+ def self.public_request(method, path, **kwargs)
43
+ check_public_api_key_exists()
44
+ params = build_params(kwargs)
45
+ url = "#{BASE_API_URL}/#{path}?#{params}"
46
+ res = Faraday.get(url).body
47
+ end
48
+
49
+ def self.v1_request(method, path, content_type: CONTENT_JSON, **kwargs)
50
+ if content_type == CONTENT_URL_FORM
51
+ data = {
52
+ :body => {
53
+ :api_key => Klaviyo.private_api_key
54
+ }
55
+ }
56
+ data[:body] = data[:body].merge(kwargs[:params])
57
+ full_url = "#{V1_API}/#{path}"
58
+ request(method, full_url, content_type, data)
59
+ else
60
+ defaults = {:page => nil,
61
+ :count => nil,
62
+ :since => nil,
63
+ :sort => nil}
64
+ params = defaults.merge(kwargs)
65
+ query_params = encode_params(params)
66
+ full_url = "#{V1_API}/#{path}?api_key=#{Klaviyo.private_api_key}#{query_params}"
67
+ request(method, full_url, content_type)
68
+ end
69
+ end
70
+
71
+ def self.v2_request(method, path, **kwargs)
72
+ path = "#{V2_API}/#{path}"
73
+ key = {
74
+ "api_key": "#{Klaviyo.private_api_key}"
75
+ }
76
+ data = {}
77
+ data[:body] = key.merge(kwargs)
78
+ request(method, path, CONTENT_JSON, data)
79
+ end
80
+
81
+ def self.build_params(params)
82
+ "data=#{CGI.escape(Base64.encode64(JSON.generate(params)).gsub(/\n/, ''))}"
83
+ end
84
+
85
+ def self.check_required_args(kwargs)
86
+ if kwargs[:email].to_s.empty? and kwargs[:phone_number].to_s.empty? and kwargs[:id].to_s.empty?
87
+ raise Klaviyo::KlaviyoError.new(REQUIRED_ARG_ERROR)
88
+ else
89
+ return true
90
+ end
91
+ end
92
+
93
+ def self.check_private_api_key_exists()
94
+ if !Klaviyo.private_api_key
95
+ raise KlaviyoError.new(NO_PRIVATE_API_KEY_ERROR)
96
+ end
97
+ end
98
+
99
+ def self.check_public_api_key_exists()
100
+ if !Klaviyo.public_api_key
101
+ raise KlaviyoError.new(NO_PUBLIC_API_KEY_ERROR)
102
+ end
103
+ end
104
+
105
+ def self.encode_params(kwargs)
106
+ kwargs.select!{|k, v| v}
107
+ params = URI.encode_www_form(kwargs)
108
+
109
+ if !params.empty?
110
+ return "&#{params}"
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,27 @@
1
+ require 'open-uri'
2
+ require 'base64'
3
+ require 'json'
4
+ require 'faraday'
5
+
6
+ require_relative './client'
7
+ require_relative 'apis/public'
8
+ require_relative 'apis/lists'
9
+ require_relative 'apis/metrics'
10
+ require_relative 'apis/profiles'
11
+ require_relative 'apis/campaigns'
12
+ require_relative 'apis/email_templates'
13
+ require_relative 'apis/data_privacy'
14
+
15
+ module Klaviyo
16
+ class << self
17
+ attr_accessor :public_api_key
18
+ attr_accessor :private_api_key
19
+ end
20
+
21
+ class KlaviyoError < StandardError; end
22
+
23
+ NO_PRIVATE_API_KEY_ERROR = 'Please provide your Private API key for this request'
24
+ NO_PUBLIC_API_KEY_ERROR = 'Please provide your Public API key for this request'
25
+ REQUIRED_ARG_ERROR = 'You must identify a user by email, ID or phone_number'
26
+ INVALID_ID_TYPE_ERROR = 'Invalid id_type provided, must be one of: email, phone_number, person_id'
27
+ end
data/lib/klaviyo.rb ADDED
@@ -0,0 +1 @@
1
+ require 'klaviyo/klaviyo_module'
@@ -0,0 +1,68 @@
1
+ module Rack
2
+ class Klaviyo
3
+ def initialize(app, api_key, options={})
4
+ @app = app
5
+ @api_key = api_key
6
+ @options = {
7
+ :insert_js_last => true
8
+ }.merge(options)
9
+ end
10
+
11
+ def call(env)
12
+ @env = env
13
+
14
+ @status, @headers, @response = @app.call(env)
15
+
16
+ if is_trackable_response?
17
+ update_response!
18
+ update_content_length!
19
+ end
20
+
21
+ [@status, @headers, @response]
22
+ end
23
+
24
+ private
25
+
26
+ def update_response!
27
+ @response.each do |part|
28
+ insert_at = part.index(@options[:insert_js_last] ? '</body' : '</head')
29
+ unless insert_at.nil?
30
+ part.insert(insert_at, render_script)
31
+ end
32
+ end
33
+ end
34
+
35
+ def update_content_length!
36
+ new_size = 0
37
+ @response.each{|part| new_size += part.bytesize}
38
+ @headers.merge!('Content-Length' => new_size.to_s)
39
+ end
40
+
41
+ def is_ajax_request?
42
+ @env.has_key?('HTTP_X_REQUESTED_WITH') && @env['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'
43
+ end
44
+
45
+ def is_html_response?
46
+ @headers['Content-Type'].include?('text/html') if @headers.has_key?('Content-Type')
47
+ end
48
+
49
+ def is_trackable_response?
50
+ is_html_response? && !is_ajax_request?
51
+ end
52
+
53
+ def render_script
54
+ <<-EOT
55
+ <script text="text/javascript">
56
+ var _learnq = _learnq || [];
57
+ _learnq.push(['account', '#{@api_key}']);
58
+
59
+ (function () {
60
+ var b = document.createElement('script'); b.type = 'text/javascript'; b.async = true;
61
+ b.src = ('https:' == document.location.protocol ? 'https://' : 'http://') + 'a.klaviyo.com/media/js/analytics/analytics.js';
62
+ var a = document.getElementsByTagName('script')[0]; a.parentNode.insertBefore(b, a);
63
+ })();
64
+ </script>
65
+ EOT
66
+ end
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: klaviyo
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Klaviyo Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: escape
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Ruby wrapper for the Klaviyo API
70
+ email: libraries@klaviyo.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - klaviyo.gemspec
76
+ - lib/klaviyo.rb
77
+ - lib/klaviyo/apis/campaigns.rb
78
+ - lib/klaviyo/apis/data_privacy.rb
79
+ - lib/klaviyo/apis/email_templates.rb
80
+ - lib/klaviyo/apis/lists.rb
81
+ - lib/klaviyo/apis/metrics.rb
82
+ - lib/klaviyo/apis/profiles.rb
83
+ - lib/klaviyo/apis/public.rb
84
+ - lib/klaviyo/client.rb
85
+ - lib/klaviyo/klaviyo_module.rb
86
+ - lib/rack/klaviyo.rb
87
+ homepage: https://www.klaviyo.com/
88
+ licenses: []
89
+ metadata: {}
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ requirements: []
105
+ rubygems_version: 3.0.8
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: You heard us, a Ruby wrapper for the Klaviyo API
109
+ test_files: []