bluekai 0.0.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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +44 -0
  8. data/Rakefile +2 -0
  9. data/bluekai.gemspec +28 -0
  10. data/circle.yml +3 -0
  11. data/lib/bluekai.rb +7 -0
  12. data/lib/bluekai/client.rb +407 -0
  13. data/lib/bluekai/error.rb +4 -0
  14. data/lib/bluekai/version.rb +3 -0
  15. data/spec/fixtures/signatures/1952b4f61dbd2187b5589dfc390af2ee985ca6f43020081d420f5d9ca025532e +1 -0
  16. data/spec/fixtures/signatures/26d52d2e4c2d42ba54ec6b4de9e67d9b566aeb8224c28261d1d133b7fb3c3436 +1 -0
  17. data/spec/fixtures/signatures/2db01e685f73149994efc9824c10fa91e704fb7256d748c4305101513444f49f +1 -0
  18. data/spec/fixtures/signatures/391112d6698e9864c1d9b37f92ab7e6db690a7e84949378ee272b011f3b82fc0 +1 -0
  19. data/spec/fixtures/signatures/3c33aa44e5fbea304ffda0390ac2e413a02d187d5425f67c3edab34684b2b18f +1 -0
  20. data/spec/fixtures/signatures/691325444d07bf08ce72b95283f8a8fd74e19f6963263e5367772b7633cbeab0 +1 -0
  21. data/spec/fixtures/signatures/78f40f967bf9cb1ae030412e7c28267a8050926a538226c685fb2dec6f23b44a +1 -0
  22. data/spec/fixtures/signatures/847da153b01c0ea4e3beeeb00f5552c3dd773227ee5aeaf5c9ff9a620adae324 +1 -0
  23. data/spec/fixtures/signatures/a49b4d9ac5b30c91f95a907b887f9b8eaf5c7856b844c49d2b937c00d88af63d +1 -0
  24. data/spec/fixtures/signatures/a6d578d433ca6f7c84ce9da14c415b3d302bf1da2b87f7666c637419fb784f57 +1 -0
  25. data/spec/fixtures/signatures/d30077840edb2b580c1c9a4a6659989b8342cfdc439ceef0c984672849288243 +1 -0
  26. data/spec/fixtures/signatures/d3c90cbaf307901dff229713b703217d7460f3c50400aa981ac20088f58343ad +1 -0
  27. data/spec/fixtures/vcr_cassettes/Bluekai_Client/_ping/when_we_get_a_pong_from_bluekai/.yml +30 -0
  28. data/spec/fixtures/vcr_cassettes/Bluekai_Client/creates_a_category.yml +49 -0
  29. data/spec/fixtures/vcr_cassettes/Bluekai_Client/creates_a_rule.yml +57 -0
  30. data/spec/fixtures/vcr_cassettes/Bluekai_Client/lists_10_phint_rules.yml +194 -0
  31. data/spec/fixtures/vcr_cassettes/Bluekai_Client/lists_4_categories.yml +3569 -0
  32. data/spec/fixtures/vcr_cassettes/Bluekai_Client/lists_Bluekai_taxonomy_nodes.yml +161267 -0
  33. data/spec/fixtures/vcr_cassettes/Bluekai_Client/performs_a_ping.yml +36 -0
  34. data/spec/fixtures/vcr_cassettes/Bluekai_Client/reads_a_category_and_its_reach.yml +2459 -0
  35. data/spec/fixtures/vcr_cassettes/Bluekai_Client/reads_a_rule.yml +56 -0
  36. data/spec/fixtures/vcr_cassettes/Bluekai_Client/updates_a_category.yml +48 -0
  37. data/spec/fixtures/vcr_cassettes/Bluekai_Client/updates_a_rule.yml +57 -0
  38. data/spec/lib/bluekai/client_integration_spec.rb +153 -0
  39. data/spec/lib/bluekai/client_spec.rb +19 -0
  40. data/spec/spec_helper.rb +108 -0
  41. metadata +193 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 68f6e5115ea82ddcda1d0ad4780f27fd0b33d73e
4
+ data.tar.gz: acd51bbeef54aac2ce754571fbb6fe3cd592707a
5
+ SHA512:
6
+ metadata.gz: 8460428509ffdac8f0b0994f4be95fa89982e967d67d7342a5b9a796729e6672f2aa879e1b247798b65a8afabff27be2e3f757da0e4b8a97fd76127c44843b80
7
+ data.tar.gz: e35bbd450af7dc97de6489f679c8fbfa6415e483d967dcb322088d49db60f757a816aa42ccf98276588d64588fc8b4226ea1a46dcc32ec59d1a7c631ee8a6b72
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+
6
+ group :development, :test do
7
+ gem 'rubocop-ci', github: 'ad2games/rubocop-ci'
8
+ gem "codeclimate-test-reporter", group: :test, require: nil
9
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 ad2games GmbH
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # Bluekai
2
+ [![Gem Version](http://img.shields.io/gem/v/bluekai.svg)](http://rubygems.org/gems/bluekai)
3
+ [![Circle CI](https://circleci.com/gh/ad2games/bluekai.png?style=shield&circle-token=323d6ce1376f1473cab4474d89f8fc7287446595)](https://circleci.com/gh/ad2games/bluekai)
4
+ [![Code Climate](https://codeclimate.com/repos/5523e6d8695680516e00144d/badges/40648ea53c768dd15de5/gpa.svg)](https://codeclimate.com/repos/5523e6d8695680516e00144d/feed)
5
+ [![Test Coverage](https://codeclimate.com/repos/5523e6d8695680516e00144d/badges/40648ea53c768dd15de5/coverage.svg)](https://codeclimate.com/repos/5523e6d8695680516e00144d/feed)
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'bluekai'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install bluekai
21
+
22
+ ## Usage
23
+
24
+ Before you start, please ensure the following variables are correctly set in your environment: `ENV['BLUEKAI_DOMAIN']`, `ENV['BLUEKAI_API_USER_KEY']`, `ENV['BLUEKAI_API_PRIVATE_KEY']` and `ENV['BLUEKAI_PARTNER_ID']`
25
+
26
+
27
+ List all self-classification categories
28
+ ```ruby
29
+ Bluekai::Client.new.category_list({})
30
+ ```
31
+ Read category parameters including estimated reach (estimated number of unique users
32
+ based on 30-day inventory) on desktop
33
+
34
+ ```ruby
35
+ Bluekai::Client.new.category_read({category_id: 421426, stats: 'true', device_type: 'desktop'})
36
+ ```
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it ( https://github.com/[my-github-username]/bluekai/fork )
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop-ci'
data/bluekai.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bluekai/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bluekai"
8
+ spec.version = Bluekai::VERSION
9
+ spec.authors = ["ad2games GmbH"]
10
+ spec.email = ["developers@ad2games.com"]
11
+ spec.summary = "Simple client for the BlueKai API"
12
+ spec.description = "Simple client for the BlueKai API (services.bluekai.com)"
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'httparty'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.6"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency 'vcr'
26
+ spec.add_development_dependency 'webmock'
27
+ spec.add_development_dependency 'rspec'
28
+ end
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ pre:
3
+ - bundle exec rake rubocop
data/lib/bluekai.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'bluekai/version'
2
+ require 'bluekai/client'
3
+ require 'bluekai/error'
4
+
5
+ module Bluekai
6
+ # Your code goes here...
7
+ end
@@ -0,0 +1,407 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'httparty'
4
+ require 'cgi'
5
+
6
+ module Bluekai
7
+ # A simple BlueKai client
8
+ class Client
9
+ attr_reader :domain, :api_user_key
10
+
11
+ def initialize(opts = {})
12
+ @domain = opts.fetch(:domain, ENV['BLUEKAI_DOMAIN']) ||
13
+ fail(Error, 'BlueKai domain missing')
14
+ @api_user_key = opts.fetch(:api_user_key, ENV['BLUEKAI_API_USER_KEY']) ||
15
+ fail(Error, 'BlueKai API user key missing')
16
+ @api_private_key = opts.fetch(:api_private_key, ENV['BLUEKAI_API_PRIVATE_KEY']) ||
17
+ fail(Error, 'BlueKai API private key missing')
18
+ @partner_id = opts.fetch(:partner_id, ENV['BLUEKAI_PARTNER_ID']) ||
19
+ fail(Error, 'BlueKai PartnerID missing')
20
+ @opts = opts
21
+ end
22
+
23
+ def ping
24
+ request('GET', '/Services/WS/Ping', {}).to_i == 200 rescue false
25
+ end
26
+
27
+ # Public: Lists categories in the BlueKai taxonomy. API definition
28
+ # can be found here https://kb.bluekai.com/display/PD/Taxonomy+API
29
+ #
30
+ # parentId - integer
31
+ # fullPath - {0,1}
32
+ # bkSize - {0,1} Enter 1 to include the inventory of unique users in
33
+ # the BlueKai network for each category.
34
+ # intlDataCountryId - {-1..24} for country index see
35
+ # (https://kb.bluekai.com/display/PD/Taxonomy+API)
36
+ # device_code - {0 = Desktop + Mobile,1 = Desktop, 2 = Mobile}
37
+ # showBuyable - {0,1}
38
+ # showLeafStatus - {0,1}
39
+ # description - {0,1}
40
+ # vertical - {0,1}
41
+ # showReceivedAudienceCategories - {0,1}
42
+ # showCategoryPriceAtDate - {'YYYY-MM-DD'}
43
+ #
44
+ # Returns array of taxonomy nodes.
45
+ def taxonomy(query = {})
46
+ request('GET', '/Services/WS/Taxonomy', query)[:nodeList]
47
+ end
48
+
49
+ ####
50
+ #### Categories
51
+ ####
52
+ #### API definition can be found here
53
+ #### https://kb.bluekai.com/display/PD/Self-Classification+Category+API
54
+ ####
55
+
56
+ # Public: Lists self classification categories in private taxonomy
57
+ #
58
+ # name:string - Returns all self-classification categories based on the specified
59
+ # name (whole or partial). The name is case-insensitive.
60
+ #
61
+ # offset:integer - Specify the starting index from which to return
62
+ # the self-classification categories.
63
+ #
64
+ # size:integer - Specifies the maximum number of categories to be included in
65
+ # the response. This filter requires the offset filter to be specified.
66
+ #
67
+ # parent_id:integer - Returns all self-classification categories
68
+ # based on the ID of the specified parent category.
69
+ #
70
+ # sort_by:string - Enter 'name' or 'id' to sort the returned self-classification categories in
71
+ # alphabetical or numerical order (based on categoryId)
72
+ #
73
+ # sorting_order:string - Enter 'asc' or 'desc' to list the returned
74
+ # self-classification categories in ascending
75
+ # or descending order based on the category
76
+ # name or categoryId.
77
+ #
78
+ # Returns array of category hashes
79
+ def category_list(query = {})
80
+ query = { sort_by: 'name',
81
+ sorting_order: 'asc' }.merge(query)
82
+ request('GET', '/Services/WS/classificationCategories', query)[:categories]
83
+ end
84
+
85
+ # Public: returns the self-classification category
86
+ # specified by the category_id
87
+ #
88
+ # category_id:integer - MANDATORY The unique ID assigned to the
89
+ # self-classification category to be retrieved
90
+ #
91
+ # stats:string - {'True','False'} Returns the reach (estimated
92
+ # number of unique users based on 30-day
93
+ # inventory) for the self-classification category.
94
+ #
95
+ # device_type:string - reach for the self-classification category
96
+ # based on the specified device, which may either
97
+ # be 'all', 'desktop', or 'mobile'
98
+ #
99
+ # intl_code - returns the reach for the self-classification category
100
+ # based on the specified country. The default country
101
+ # is ALL. You may enter one of the following country
102
+ # codes: ALL, US, AU, CA, GB, GER, ESP, NL, MX, IT,
103
+ # FR, BR, AR, RU, NZ, JP, CL, CN.
104
+ #
105
+ # Returns: A hash of Bluekai private category data
106
+ def category_read(query)
107
+ fail 'no category_id found in hash' unless query.key?(:category_id)
108
+ category_id = query.delete(:category_id)
109
+ request('GET', "/Services/WS/classificationCategories/#{category_id}", query)
110
+ end
111
+
112
+ # Public: Creates a new self-classification category
113
+ #
114
+ # name:string - Name of the self-classification category
115
+ #
116
+ # parent_id:integer - Unique ID of the parent node for the
117
+ # self-classification category.
118
+ #
119
+ # description:string - Description of uUser attribute
120
+ # represented by this category.
121
+ #
122
+ # analytics_excluded:string - {'True','False'} Specify whether the
123
+ # self-classification category is to be excluded
124
+ # from Audience Analytics reports. This property
125
+ # is false by default.
126
+ #
127
+ # navigation_only:string - {'True','False'} Specify whether the
128
+ # self-classification category functions
129
+ # exclusively as a parent node that cannot be
130
+ # selected. This property is false by default.
131
+ #
132
+ # mutex_children:string - {'True','False'} Specify whether to limit
133
+ # the number of the category's child nodes
134
+ # that can be added to an audience segment to one.
135
+ # This property is false by default.
136
+ #
137
+ # notes:string - (Optional) Enter any notes to be associated with
138
+ # this self-classification category.
139
+ #
140
+ # Example body hash = {name: 'a new category',
141
+ # parent_id: '2342', description: 'an example category',
142
+ # analytics_excluded: 'false', navigation_only: 'false',
143
+ # mutex_children: 'false', notes: 'Just an API test' }
144
+ #
145
+ # Returns: hash of created category parameters including its category_id
146
+ def category_create(body)
147
+ request('POST', '/Services/WS/classificationCategories', {}, body)
148
+ end
149
+
150
+ # Public: Updates a given self-classification category
151
+ #
152
+ # category_id:integer - The unique ID assigned to the self-classification
153
+ # category to be updated
154
+ #
155
+ # name:string - Name of the self-classification category
156
+ #
157
+ # parent_id:integer - Unique ID of the parent node for the
158
+ # self-classification category.
159
+ #
160
+ # description:string - Description of uUser attribute
161
+ # represented by this category.
162
+ #
163
+ # analytics_excluded:string - {'True','False'} Specify whether the
164
+ # self-classification category is to be excluded
165
+ # from Audience Analytics reports. This property
166
+ # is false by default.
167
+ #
168
+ # navigation_only:string - {'True','False'} Specify whether the
169
+ # self-classification category functions
170
+ # exclusively as a parent node that cannot be
171
+ # selected. This property is false by default.
172
+ #
173
+ # mutex_children:string - {'True','False'} Specify whether to limit
174
+ # the number of the category's child nodes
175
+ # that can be added to an audience segment to one.
176
+ # This property is false by default.
177
+ #
178
+ # notes:string - (Optional) Enter any notes to be associated with
179
+ # this self-classification category.
180
+ #
181
+ # Example body hash = {category_id: 1234, name: 'a chaged category',
182
+ # parent_id: '2342', description: 'an example category',
183
+ # analytics_exclued: 'false', navigation_only: 'false',
184
+ # mutex_children: 'false', notes: 'Just an API test' }
185
+ #
186
+ # Returns: hash of updated category parameters
187
+ def category_update(category_id, body)
188
+ request('PUT', "/Services/WS/classificationCategories/#{category_id}", {}, body)
189
+ end
190
+
191
+ ####
192
+ #### Classification Rules
193
+ #### API definition can be found here
194
+ #### https://kb.bluekai.com/display/PD/Self-Classification+Rule+API
195
+ ####
196
+
197
+ # Public: List the self-classification rules in your private
198
+ # taxonomy
199
+ #
200
+ # sort_by:string - Enter 'status', 'id', 'created_at', 'updated_at',
201
+ # or 'type' to sort the returned self-classification
202
+ # rules by the specified option.
203
+ #
204
+ # sorting_order:string - Enter 'asc' or 'desc' to list the returned
205
+ # self-classification rules in ascending or descending
206
+ # order based on the specified sort option.
207
+ #
208
+ # ids:integer - Returns the self-classification rule matching the specified
209
+ # rule ID, or returns all the self-classification rules matching
210
+ # the specified list of rule IDs. Syntax for passing multiple rule IDs:
211
+ # ruleId1&id=ruleId2 Example: 123&id=125
212
+ #
213
+ # type:enum - Enter 'phint' or 'url' to return only the phint or
214
+ # URL-based self-classification rules.
215
+ #
216
+ # site_ids:string - Returns all the self-classification rules under the
217
+ # specified site ID or list of site IDs. Syntax for
218
+ # passing multiple site IDs: site_id_1&site_ids=site_id_2
219
+ # Example: 1234&site_ids=1235
220
+ #
221
+ # category_ids:string - Returns all the self-classification rules used
222
+ # to map the specified catgeory ID or list of category IDs.
223
+ # Syntax for passing multiple category
224
+ # IDs: category_id_1&category_ids=category_id_2
225
+ # Example: 1234&category_ids=1235
226
+ #
227
+ # offset:integer - Specify the starting index from which to return the
228
+ # self-classification rules.
229
+ #
230
+ # size:integer - Specify the maximum number of rules to be included in
231
+ # the response. This filter requires the offset filter
232
+ # to be specified.
233
+ #
234
+ # created_date_range:string - Returns all the self-classification rules
235
+ # created within the specified list of dates.
236
+ # Syntax: YYYY-MM-DD&created_date_range=YYYY-MM-DD
237
+ # Example: 2014-01-01&created_date_range=2014-31-01
238
+ #
239
+ # updated_date_range:string - Returns all the self-classification rules updated
240
+ # within the specified list of dates.
241
+ # Syntax: YYYY-MM-DD&updated_date_range=YYYY-MM-DD
242
+ # Example: 2014-01-01&updated_date_range=2014-31-01
243
+ #
244
+ # status:string - Enter 'Active' or 'Creating' to return the
245
+ # self-classification rules based on the specified status referrer
246
+ # boolean Returns all URL-based self-classification rules that
247
+ # classify the site URL (False) or the referrer URL (True) in
248
+ # the collected URL.
249
+ #
250
+ # exact:boolean - Returns all URL-based self-classification rules that classify
251
+ # an exact URL (True) or a top-level URL (False) in the collected URL.
252
+ # Returns: hash of Bluekai rules
253
+ def rule_list(query)
254
+ request('GET', '/Services/WS/classificationRules', query)[:rules]
255
+ end
256
+
257
+ # Public: Reads a self-classification rule
258
+ #
259
+ # rule_id:integer - The unique ID assigned to the
260
+ # self-classification rule to be retrieved
261
+ #
262
+ # Returns: hash of Blukkai rule parameters
263
+ def rule_read(rule_id)
264
+ request('GET', "/Services/WS/classificationRules/#{rule_id}", {})
265
+ end
266
+
267
+ # Public: Creates a new self-classification rule
268
+ #
269
+ # name:string - Enter a string specifying the name of the self-classification rule.
270
+ #
271
+ # type:string - {'phint', 'url'} Specify the type of classification rule.
272
+ #
273
+ # phints:[{phint}] - If you are creating a phint-based rule,
274
+ # enter a list of your phint definitions.
275
+ # Each phint requires the following properties:
276
+ # key - The phint key operator - The criteria
277
+ # used for determining how the phint value
278
+ # is applied ('is' or 'contains')
279
+ # value - The full or partial phint value,
280
+ # depending on the specified operator.
281
+ #
282
+ # urls:[string(s)] - Provide a list of your URL definitions
283
+ # if you are creating for URL-based rules.
284
+ #
285
+ # referrer:string - {'True','False'} If you are creating a
286
+ # URL-based rule, specify whether the URL to
287
+ # be classified is the site URL (False) or
288
+ # the referrer URL (True).
289
+ #
290
+ # exact:string - {'True','False'} If you are creating a
291
+ # URL-based rule, specify whether the URL collected
292
+ # from your site must match the URL in your
293
+ # rule (True) or match a top-level URL (False) so
294
+ # that you can target users visiting the child pages
295
+ # without specifying them.
296
+ #
297
+ # partner_id:integer - Enter the unique ID assigned to your BlueKai DMP seat.
298
+ #
299
+ # site_ids (optional):[interger(s)] - Enter a list of containers/site IDs to which
300
+ # the self-classification rule applies. If
301
+ # you do not include this parameter, the
302
+ # rule is applicable to ALL the
303
+ # container/site IDs in your seat.
304
+ #
305
+ # category_ids:[integer(s)] - a list of category IDs to which
306
+ # the self-classification rule applies.
307
+ #
308
+ # JSON example for Phint-based self-classification rule
309
+ # {
310
+ # "name": "Phint Example",
311
+ # "type": "phint",
312
+ # "phints": [
313
+ # {
314
+ # "key": "x",
315
+ # "value": "123",
316
+ # "operator": "is"
317
+ # }
318
+ # ],
319
+ # "partner_id": 123,
320
+ # "site_ids": [1234],
321
+ # "category_ids": [12345]
322
+ # }
323
+ #
324
+ # JSON example for URL-based self-classiifcation rule
325
+ # {
326
+ # "name": "URL Example",
327
+ # "type": "url",
328
+ # "urls": ["http://shop.yoursite.com"],
329
+ # "referrer": false,
330
+ # "exact": false,
331
+ # "partner_id": 123,
332
+ # "site_ids": [1234],
333
+ # "category_ids": [123456]
334
+ # }
335
+ # Returns: hash of created self-classification rule
336
+ def rule_create(body)
337
+ body = { partner_id: @partner_id }.merge(body)
338
+ request('POST', '/Services/WS/classificationRules', {}, body)
339
+ end
340
+
341
+ # Public: Update a self-classification rule
342
+ #
343
+ # rule_id:integer (MANDATORY) - id of classification rule to be updated
344
+ #
345
+ # for other parameters refer to documentation of rule_create(body)
346
+ #
347
+ # Returns: hash of updated self-classification rule
348
+ def rule_update(rule_id, body)
349
+ body = { partner_id: @partner_id }.merge(body)
350
+ request('PUT', "/Services/WS/classificationRules/#{rule_id}", {}, body)
351
+ end
352
+
353
+ private
354
+
355
+ def request(method, path, query, body = nil)
356
+ method.upcase!
357
+ signature = sign(method, path, query_values(query), body_sorted(body))
358
+
359
+ url = "https://#{domain}#{path}?#{query_url_formatted(query)}\
360
+ bkuid=#{api_user_key}&bksig=#{signature}"
361
+
362
+ response = case method
363
+ when 'GET'
364
+ HTTParty.get(url)
365
+ when 'POST'
366
+ HTTParty.post(url, body: body_sorted(body), headers: json_headers)
367
+ when 'PUT'
368
+ HTTParty.put(url, body: body_sorted(body), headers: json_headers)
369
+ else
370
+ fail ArgumentError, "request method '#{method}' not supported"
371
+ end.response
372
+ fail "HTTP Request Error: #{response.body}" if response.code != '200'
373
+ return response.code if response.body == '' || response.body.nil?
374
+ JSON.parse(response.body, symbolize_names: true)
375
+ end
376
+
377
+ def body_sorted(body)
378
+ # sort hash according to keys for correct signature computation
379
+ return body.sort_by { |key, _value| key.to_s }.to_h.to_json if body
380
+ end
381
+
382
+ def json_headers
383
+ {
384
+ 'Accept' => 'application/json',
385
+ 'Content-type' => 'application/json'
386
+ }
387
+ end
388
+
389
+ def query_values(query)
390
+ return nil unless query
391
+ query.values.join
392
+ end
393
+
394
+ def query_url_formatted(query)
395
+ return nil unless query
396
+ query.map { |k, v| "#{k}=#{v}" }.join('&') + '&'
397
+ end
398
+
399
+ # HMAC-SHA256(Secret key, HTTP_METHOD + URI_PATH + QUERY_ARG_VALUES + POST_DATA)
400
+ def sign(method, path, query, body)
401
+ string_to_sign = method + path + query.to_s + body.to_s
402
+ digest = OpenSSL::Digest.new('sha256')
403
+ hmac = OpenSSL::HMAC.digest(digest, @api_private_key, string_to_sign)
404
+ CGI.escape(Base64.strict_encode64(hmac))
405
+ end
406
+ end
407
+ end