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