adknowledge 0.0.3
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +114 -0
- data/Rakefile +6 -0
- data/adknowledge.gemspec +33 -0
- data/lib/adknowledge/config.rb +9 -0
- data/lib/adknowledge/integrated.rb +193 -0
- data/lib/adknowledge/performance.rb +255 -0
- data/lib/adknowledge/version.rb +3 -0
- data/lib/adknowledge.rb +9 -0
- data/lib/faraday_middleware/adknowledge.rb +33 -0
- data/spec/adknowledge/integrated_spec.rb +378 -0
- data/spec/adknowledge/performance_spec.rb +320 -0
- data/spec/adknowledge_spec.rb +0 -0
- data/spec/cassettes/map_multi_success.yml +68 -0
- data/spec/cassettes/map_single_success.yml +51 -0
- data/spec/cassettes/performance.yml +41 -0
- data/spec/spec_helper.rb +16 -0
- metadata +284 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
Adknowledge <a id='top'></a>
|
2
|
+
===========
|
3
|
+
|
4
|
+
A very Ruby client library for [Adknowledge](http://www.adknowledge.com) APIs
|
5
|
+
|
6
|
+
Right now it supports two API end-points:
|
7
|
+
* Integrated - pulls down creatives for recipients using ADK's integrated API
|
8
|
+
* Performance - report on product performance
|
9
|
+
|
10
|
+
Integrated
|
11
|
+
----------
|
12
|
+
|
13
|
+
Mapping content for recipients is super easy:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# Start with an array of hashes for your recipients...
|
17
|
+
recipients = [
|
18
|
+
{ recipient: '004c58927df600d73d58c817bafc2155',
|
19
|
+
list: 9250,
|
20
|
+
domain: 'hotmail.com',
|
21
|
+
countrycode: 'US',
|
22
|
+
state: 'MO'
|
23
|
+
},
|
24
|
+
{ recipient: 'a2a8c7a5ce7c4249663803c7d040401f',
|
25
|
+
list: 9250,
|
26
|
+
domain: 'mail.com',
|
27
|
+
countrycode: 'CA'
|
28
|
+
}
|
29
|
+
]
|
30
|
+
|
31
|
+
# Specify your auth token:
|
32
|
+
Adknowledge.token = "your token here"
|
33
|
+
|
34
|
+
# Create an Integrated object
|
35
|
+
mapper = Adknowledge::Integrated.new \
|
36
|
+
domain: 'www.mydomain.com',
|
37
|
+
subid: 1234,
|
38
|
+
recipients: recipients
|
39
|
+
|
40
|
+
# Make stuff happen
|
41
|
+
mapper.map!
|
42
|
+
|
43
|
+
# Get yer results
|
44
|
+
mapper.each do |recipient|
|
45
|
+
# your hash now has extra stuff
|
46
|
+
# like:
|
47
|
+
puts recipient['creative']['subject']
|
48
|
+
puts recipient['creative']['body']
|
49
|
+
end
|
50
|
+
|
51
|
+
# Further short-cut with selections for mapped/errored
|
52
|
+
mapper.mapped_recipients.each do |recipient|
|
53
|
+
# All of these were successful
|
54
|
+
end
|
55
|
+
|
56
|
+
mapper.errored_recipients.each do |recipient|
|
57
|
+
# All of these errored :(
|
58
|
+
puts recipient['error']['num']
|
59
|
+
puts recipient['error']['str']
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
[Back to top](#top)
|
64
|
+
Performance
|
65
|
+
-----------
|
66
|
+
|
67
|
+
We also give you a nice ActiveRecord-inspired query interface
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# Specify your auth token:
|
71
|
+
Adknowledge.token = "your token here"
|
72
|
+
|
73
|
+
# Create a query object
|
74
|
+
perf = Adknowlege::Performance.new
|
75
|
+
|
76
|
+
perf.where(start_date: 1, domain_group: 'AOL Group').
|
77
|
+
select(:revenue, :paid_clicks).
|
78
|
+
group_by(:subid, :report_date)
|
79
|
+
|
80
|
+
perf.each do |row|
|
81
|
+
# do something with the results
|
82
|
+
end
|
83
|
+
```
|
84
|
+
|
85
|
+
Supports the following query options:
|
86
|
+
* Select - Measures that you'd like to report
|
87
|
+
* Where - Filter criteria
|
88
|
+
* Group By - Dimensions to aggregate on
|
89
|
+
* Pivot - Advanced Pivot options
|
90
|
+
* No Cache - disable ADK default caching (60 seconds)
|
91
|
+
* Display All - Display dimensions even if they've been filtered
|
92
|
+
* Full - Return all days in the date range even if values are 0
|
93
|
+
* Sort - Column index to sort on
|
94
|
+
* Limit - Limit query to a number of rows
|
95
|
+
|
96
|
+
For more details see the specs, and [ADK documentation](https://publisher.adknowledge.com/help/documentation/chapter/data-pull-api)
|
97
|
+
|
98
|
+
[Back to top](#top)
|
99
|
+
TODO
|
100
|
+
----
|
101
|
+
There are several things currently unfinished:
|
102
|
+
* Error handling when requests are unsuccessful
|
103
|
+
* Options/convenience methods for parsing pivot query results
|
104
|
+
* Add an end-point for "lookup" API
|
105
|
+
|
106
|
+
How to Contribute
|
107
|
+
-----------------
|
108
|
+
* Fork this repository on Github
|
109
|
+
* Run the test suite - FYI: Auth tokens have been removed to protect the innocent
|
110
|
+
* Add code and tests
|
111
|
+
* Submit a pull request
|
112
|
+
* I'll merge if it looks good & passes
|
113
|
+
* Rinse
|
114
|
+
* Repeat
|
data/Rakefile
ADDED
data/adknowledge.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "adknowledge/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "adknowledge"
|
7
|
+
s.version = Adknowledge::VERSION
|
8
|
+
s.authors = ["Aaron Spiegel"]
|
9
|
+
s.email = ["spiegela@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Adknowledge API Tools}
|
12
|
+
s.description = %q{A collection of web-api helpers and parsing utils for Adknowlege's APIs}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
s.add_development_dependency "pry"
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "webmock", "1.10"
|
23
|
+
s.add_development_dependency "vcr"
|
24
|
+
s.add_runtime_dependency "rake"
|
25
|
+
s.add_runtime_dependency "activesupport"
|
26
|
+
s.add_runtime_dependency "oj"
|
27
|
+
s.add_runtime_dependency "ox"
|
28
|
+
s.add_runtime_dependency "multi_xml"
|
29
|
+
s.add_runtime_dependency "addressable"
|
30
|
+
s.add_runtime_dependency "faraday"
|
31
|
+
s.add_runtime_dependency "faraday_middleware"
|
32
|
+
s.add_runtime_dependency "faraday_middleware-parse_oj"
|
33
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'ox'
|
3
|
+
require 'addressable/uri'
|
4
|
+
require 'faraday_middleware/response/parse_xml'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
|
7
|
+
module Adknowledge
|
8
|
+
class Integrated
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
attr_reader :request, :recipients
|
12
|
+
attr_accessor :idomain, :cdomain, :subid, :test
|
13
|
+
|
14
|
+
delegate :[], to: :recipients
|
15
|
+
|
16
|
+
URL = 'http://integrated.adstation.com'
|
17
|
+
API_VER = '1.3'
|
18
|
+
|
19
|
+
VALID_FIELDS = [
|
20
|
+
:recipient, :list, :domain, :subid, :sendingdomain, :sendingip,
|
21
|
+
:numberofrecipients, :redirect, :countrycode, :metrocode, :state,
|
22
|
+
:postalcode, :gender, :dayofbirth, :monthofbirth, :yearofbirth
|
23
|
+
]
|
24
|
+
|
25
|
+
MANDATORY_FIELDS = [ :recipient, :list, :domain ]
|
26
|
+
|
27
|
+
# Create integrated query object
|
28
|
+
#
|
29
|
+
# @param parameters
|
30
|
+
# @option [Symbol] domain set both click-domain and image-domain to the
|
31
|
+
# same domain name for the request
|
32
|
+
# @option [Symbol] cdomain set the click-domain for the request
|
33
|
+
# @option [Symbol] idomain set the image-domain for the request
|
34
|
+
# @option [Symbol] subid set the subid for the request
|
35
|
+
# @option [Symbol] test set the test flag for the request
|
36
|
+
def initialize params={}
|
37
|
+
@records = []
|
38
|
+
@mapped = false
|
39
|
+
params.each do |k,v|
|
40
|
+
send("#{k}=", v)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set the array of recipients to map
|
45
|
+
#
|
46
|
+
# @param [Array] recipients an array of hashes containing recipient details
|
47
|
+
# @return [String] prepared XML request for recipient array
|
48
|
+
def recipients= recipient_hashes
|
49
|
+
@recipients = recipient_hashes
|
50
|
+
doc = Ox::Document.new version: '1.0'
|
51
|
+
req = Ox::Element.new 'request'
|
52
|
+
doc << Ox::Instruct.new('xml version="1.0" encoding="UTF-8"') << req
|
53
|
+
recipient_hashes.each do |recipient_hash|
|
54
|
+
email_hash = recipient_hash.select{|x| VALID_FIELDS.include? x}
|
55
|
+
req << email_xml(email_hash)
|
56
|
+
end
|
57
|
+
@request = Ox.dump(doc, indent: 0, with_instruct: true).gsub(/\n/, '')
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Map content for specified recipients
|
62
|
+
#
|
63
|
+
# @return [Boolean] map attempt submitted
|
64
|
+
def map!
|
65
|
+
unless Adknowledge.token
|
66
|
+
raise ArgumentError, 'Adknowledge token required to perform queries'
|
67
|
+
end
|
68
|
+
|
69
|
+
merge_recipients! query_result
|
70
|
+
@mapped = true
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return all successfully mapped recipients
|
74
|
+
#
|
75
|
+
# @return [Array] mapped recipients
|
76
|
+
def mapped_recipients
|
77
|
+
return [] unless mapped?
|
78
|
+
recipients.select{|r| r['success']}
|
79
|
+
end
|
80
|
+
|
81
|
+
# Return all errored recipients
|
82
|
+
#
|
83
|
+
# @return [Array] errored recipients
|
84
|
+
def errored_recipients
|
85
|
+
return [] unless mapped?
|
86
|
+
recipients.select{|r| ! r['success']}
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set both click-domain and image-domain
|
90
|
+
#
|
91
|
+
# @param [Symbol] domain
|
92
|
+
# @return [Symbol] domain
|
93
|
+
def domain= dom
|
94
|
+
self.cdomain = self.idomain = dom
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return the query params that will be sent to Adknowledge integrated API
|
98
|
+
#
|
99
|
+
# @return [Hash] query params
|
100
|
+
def query_params
|
101
|
+
{ token: Adknowledge.token,
|
102
|
+
idomain: idomain,
|
103
|
+
cdomain: cdomain,
|
104
|
+
request: request,
|
105
|
+
subid: subid,
|
106
|
+
test: test ? 1 : 0
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return confirmation if the mapping query has been run yet
|
111
|
+
#
|
112
|
+
# @return [Boolean] mapped?
|
113
|
+
def mapped?
|
114
|
+
@mapped
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def merge_recipients! results
|
120
|
+
case results['email']
|
121
|
+
when Array
|
122
|
+
results['email'].each{ |record| merge_recipient! record, true }
|
123
|
+
when Hash
|
124
|
+
merge_recipient! results['email'], true
|
125
|
+
end
|
126
|
+
|
127
|
+
case results['error']
|
128
|
+
when Array
|
129
|
+
results['error'].each { |record| merge_recipient! record, false }
|
130
|
+
when Hash
|
131
|
+
merge_recipient! results['error'], false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def merge_recipient! record, success
|
136
|
+
fields = success ? get_email(record) : get_error(record)
|
137
|
+
recipients.find do |recipient|
|
138
|
+
record["recipient"] == recipient[:recipient]
|
139
|
+
end.merge! fields
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_email record
|
143
|
+
record.merge 'success' => true
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_error record
|
147
|
+
{ 'success' => false,
|
148
|
+
'error' => record.select{|k,v| %w[str num].include? k}
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def query_result
|
153
|
+
conn.post do |req|
|
154
|
+
req.headers = headers
|
155
|
+
req.body = post_body
|
156
|
+
req.url API_VER
|
157
|
+
end.body
|
158
|
+
end
|
159
|
+
|
160
|
+
def conn
|
161
|
+
@conn ||= Faraday.new(:url => URL) do |b|
|
162
|
+
b.adapter Faraday.default_adapter
|
163
|
+
b.response :adknowledge
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def headers
|
168
|
+
{:Accepts => 'application/xml', :'Accept-Encoding' => 'gzip'}
|
169
|
+
end
|
170
|
+
|
171
|
+
def post_body
|
172
|
+
uri = Addressable::URI.new
|
173
|
+
uri.query_values = query_params
|
174
|
+
uri.query
|
175
|
+
end
|
176
|
+
|
177
|
+
def email_xml email_hash
|
178
|
+
unless (MANDATORY_FIELDS - email_hash.keys).empty?
|
179
|
+
raise ArgumentError, 'One or more mandatory fields were not submitted'
|
180
|
+
end
|
181
|
+
e = Ox::Element.new(:email)
|
182
|
+
email_hash.each do |field, value|
|
183
|
+
e << field_xml(field, value)
|
184
|
+
end
|
185
|
+
e
|
186
|
+
end
|
187
|
+
|
188
|
+
def field_xml field, value
|
189
|
+
Ox::Element.new(field) << value.to_s
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'faraday_middleware/parse_oj'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
|
6
|
+
module Adknowledge
|
7
|
+
class Performance
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
attr_reader :measures, :dimensions, :filter, :sort_option, :pivot_options,
|
11
|
+
:records, :options
|
12
|
+
|
13
|
+
delegate :[], to: :records
|
14
|
+
|
15
|
+
URL = 'http://api.publisher.adknowledge.com/performance'
|
16
|
+
|
17
|
+
VALID_MEASURES = [
|
18
|
+
:revenue, :schedules, :clicks, :paid_clicks, :valid_clicks,
|
19
|
+
:invalid_clicks, :test_clicks, :domestic_paid_clicks,
|
20
|
+
:domestic_unpaid_clicks, :foreign_paid_clicks, :foreign_unpaid_clicks,
|
21
|
+
:foreign_clicks, :badip_clicks, :badagent_clicks, :badreferrer_clicks,
|
22
|
+
:ecpm, :epc, :source_expense, :source_profit, :affiliate_percent,
|
23
|
+
:gross_revenue, :ppc, :adjustments, :promotions, :referrals,
|
24
|
+
:expense_accruals, :adjustment_accruals, :promotion_accruals,
|
25
|
+
:referral_accruals, :accruals, :total_payment, :sent_amount,
|
26
|
+
:domain_group, :source_account_name, :records
|
27
|
+
]
|
28
|
+
|
29
|
+
VALID_DIMENSIONS = [
|
30
|
+
:product_guid, :report_date, :report_hour, :report_30min, :report_15min,
|
31
|
+
:is_accrued, :revenue_type, :source_product_guid, :list_id, :product_id,
|
32
|
+
:source_account_name, :domain_group_id, :domain_group, :report_time,
|
33
|
+
:subid, :country_cd, :accrual_date, :suppress_date, :suppress_md5,
|
34
|
+
:suppress_type
|
35
|
+
]
|
36
|
+
|
37
|
+
VALID_FILTERS = [:start_date, :end_date] + VALID_DIMENSIONS
|
38
|
+
|
39
|
+
VALID_PIVOT_KEYS = [:pivot, :sum, :count]
|
40
|
+
|
41
|
+
DEFAULT_FILTER = {product_id: '2', product_guid: '*'}
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@measures = {}
|
45
|
+
@dimensions = {}
|
46
|
+
@filter = DEFAULT_FILTER.dup
|
47
|
+
@options = {}
|
48
|
+
@pivot_options = {}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Iterate the query results. Runs the query if it hasn't been already.
|
52
|
+
#
|
53
|
+
# @param [Block] block
|
54
|
+
def each
|
55
|
+
if block_given?
|
56
|
+
records.each do |doc|
|
57
|
+
yield doc
|
58
|
+
end
|
59
|
+
else
|
60
|
+
to_enum
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Specify the measure(s) to select in the query
|
65
|
+
#
|
66
|
+
# @param [Array] selection(s)
|
67
|
+
# @return [Adknowledge::Performance] query object
|
68
|
+
def select *selection
|
69
|
+
selection = selection.map{|x| x.to_sym} # handle strings & symbols equally
|
70
|
+
unless (selection - VALID_MEASURES).empty?
|
71
|
+
raise ArgumentError, 'Invalid measurement selection'
|
72
|
+
end
|
73
|
+
@measures.merge! Hash[selection.zip([1] * selection.count)]
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Specify the dimension(s) to group measures by
|
78
|
+
#
|
79
|
+
# @param [Array] grouping(s)
|
80
|
+
# @return [Adknowledge::Performance] query object
|
81
|
+
def group_by *groupings
|
82
|
+
groupings = groupings.map{|x| x.to_sym} # handle strings & symbols equally
|
83
|
+
unless (groupings - VALID_DIMENSIONS).empty?
|
84
|
+
raise ArgumentError, 'Invalid dimension grouping'
|
85
|
+
end
|
86
|
+
@dimensions.merge! Hash[groupings.zip([1] * groupings.count)]
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Specify the filter criteria to limit query by
|
91
|
+
#
|
92
|
+
# @param [Hash] criteria
|
93
|
+
# @return [Adknowledge::Performance] query object
|
94
|
+
def where criteria
|
95
|
+
unless(criteria.keys - VALID_FILTERS).empty?
|
96
|
+
raise ArgumentError, 'Invalid filter criteria'
|
97
|
+
end
|
98
|
+
criteria.each{|k,v| criteria[k] = v.to_s}
|
99
|
+
@filter.merge! criteria
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Specify a number of results to retrun
|
104
|
+
#
|
105
|
+
# @param [Integer] limit
|
106
|
+
# @return [Adknowledge::Performance] query object
|
107
|
+
def limit limit
|
108
|
+
unless limit.is_a? Fixnum
|
109
|
+
raise ArgumentError, 'Limit must be an integer'
|
110
|
+
end
|
111
|
+
@options[:limit] = limit.to_s
|
112
|
+
self
|
113
|
+
end
|
114
|
+
|
115
|
+
# Specify the column index to sort by
|
116
|
+
#
|
117
|
+
# @param [Integer] sort_option
|
118
|
+
# @return [Adknowledge::Performance] query object
|
119
|
+
def sort sort_option
|
120
|
+
unless sort_option.is_a? Fixnum
|
121
|
+
raise ArgumentError, 'Sort option must be an integer'
|
122
|
+
end
|
123
|
+
@sort_option = sort_option.to_s
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Specify whether to display the full set even if entries are 0
|
128
|
+
#
|
129
|
+
# @param [Boolean] full
|
130
|
+
# @return [Adknowledge::Performance] query object
|
131
|
+
def full full
|
132
|
+
unless !!full == full #Boolean check
|
133
|
+
raise ArgumentError, 'Full option must be a boolean'
|
134
|
+
end
|
135
|
+
@options[:full] = full ? '1' : '0'
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
# Disable caching of queries. By default queries are cached for 60 seconds
|
140
|
+
#
|
141
|
+
# @param [Boolean] nocache
|
142
|
+
# @return [Adknowledge::Performance] query object
|
143
|
+
def nocache nocache
|
144
|
+
unless !!nocache == nocache #Boolean check
|
145
|
+
raise ArgumentError, 'NoCache option must be a boolean'
|
146
|
+
end
|
147
|
+
@options[:nocache] = nocache ? '1' : '0'
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
# Force query to show filtered dimensions to be shown
|
152
|
+
#
|
153
|
+
# @param [Boolean] display_all
|
154
|
+
# @return [Adknowledge::Performance] query object
|
155
|
+
def display_all display_all
|
156
|
+
unless !!display_all == display_all #Boolean check
|
157
|
+
raise ArgumentError, "display_all option must be a boolean"
|
158
|
+
end
|
159
|
+
@options[:all] = display_all ? '1' : '0'
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
# Specify pivot options
|
164
|
+
#
|
165
|
+
# @param [Object] pivot Existing grouped field (as symbol) or Hash of options
|
166
|
+
# @return [Adknowledge::Performance] query object
|
167
|
+
def pivot pivot_opt
|
168
|
+
case pivot_opt
|
169
|
+
when Symbol
|
170
|
+
unless valid_pivot_values.include? pivot_opt
|
171
|
+
raise ArgumentError, 'Pivotted field must be a grouped dimension'
|
172
|
+
end
|
173
|
+
@pivot_options[:pivot] = pivot_opt.to_s
|
174
|
+
when Hash
|
175
|
+
unless (pivot_opt.values - VALID_MEASURES).empty?
|
176
|
+
raise ArgumentError, 'Pivotted value must be a measurement'
|
177
|
+
end
|
178
|
+
unless (pivot_opt.keys - [:sum, :count]).empty?
|
179
|
+
raise ArgumentError, 'Pivot must be sum or count'
|
180
|
+
end
|
181
|
+
@pivot_options = pivot_opt
|
182
|
+
else
|
183
|
+
raise ArgumentError, 'Pivot options must be a symbol or hash'
|
184
|
+
end
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# Displays the query parameters passed to Adknowledge performance API
|
189
|
+
#
|
190
|
+
# @return [Hash] query parameters
|
191
|
+
def query_params
|
192
|
+
p = base_params.merge(filter_params).
|
193
|
+
merge(options_params).
|
194
|
+
merge(pivot_params)
|
195
|
+
p.merge!(measures: measures_param) unless measures.empty?
|
196
|
+
p.merge!(dimensions: dimensions_param) unless dimensions.empty?
|
197
|
+
p.merge!(sort: sort_option) if sort_option
|
198
|
+
p
|
199
|
+
end
|
200
|
+
|
201
|
+
# Return the query result records
|
202
|
+
#
|
203
|
+
# @return [Array] query result records
|
204
|
+
def records
|
205
|
+
unless Adknowledge.token
|
206
|
+
raise ArgumentError, 'Adknowledge token required to perform queries'
|
207
|
+
end
|
208
|
+
results.body['data'] if results.body.has_key?('data')
|
209
|
+
end
|
210
|
+
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def base_params
|
215
|
+
{token: Adknowledge.token}
|
216
|
+
end
|
217
|
+
|
218
|
+
def results
|
219
|
+
@results ||= conn.get do |req|
|
220
|
+
req.url '/performance.json', query_params
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def conn
|
225
|
+
@conn ||= Faraday.new(:url => URL) do |b|
|
226
|
+
b.response :oj
|
227
|
+
b.adapter Faraday.default_adapter
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def valid_pivot_values
|
232
|
+
dimensions.keys + ['*']
|
233
|
+
end
|
234
|
+
|
235
|
+
def measures_param
|
236
|
+
measures.keys.join(',')
|
237
|
+
end
|
238
|
+
|
239
|
+
def dimensions_param
|
240
|
+
dimensions.keys.join(',')
|
241
|
+
end
|
242
|
+
|
243
|
+
def options_params
|
244
|
+
options
|
245
|
+
end
|
246
|
+
|
247
|
+
def filter_params
|
248
|
+
filter
|
249
|
+
end
|
250
|
+
|
251
|
+
def pivot_params
|
252
|
+
pivot_options
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
data/lib/adknowledge.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module FaradayMiddleware
|
5
|
+
class Adknowledge < Faraday::Response::Middleware
|
6
|
+
dependency 'multi_xml'
|
7
|
+
dependency 'zlib'
|
8
|
+
|
9
|
+
def on_complete env
|
10
|
+
encoding = env[:response_headers]['content-encoding'].to_s.downcase
|
11
|
+
|
12
|
+
return unless env[:body].is_a? String
|
13
|
+
|
14
|
+
case encoding
|
15
|
+
when 'gzip'
|
16
|
+
env[:body] = Zlib::GzipReader.new(StringIO.new(env[:body]), encoding: 'ASCII-8BIT').read
|
17
|
+
env[:response_headers].delete 'content-encoding'
|
18
|
+
when 'deflate'
|
19
|
+
env[:body] = Zlib::Inflate.inflate env[:body]
|
20
|
+
env[:response_headers].delete 'content-encoding'
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
env[:body] = ::MultiXml.parse(env[:body])['result']
|
25
|
+
rescue Faraday::Error::ParsingError
|
26
|
+
env[:body]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Faraday::Response.register_middleware adknowledge: FaradayMiddleware::Adknowledge
|