appnexusapi 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +71 -39
  4. data/appnexusapi.gemspec +1 -2
  5. data/lib/appnexusapi.rb +21 -10
  6. data/lib/appnexusapi/configuration.rb +12 -0
  7. data/lib/appnexusapi/connection.rb +83 -90
  8. data/lib/appnexusapi/error.rb +3 -0
  9. data/lib/appnexusapi/faraday/raise_http_error.rb +15 -1
  10. data/lib/appnexusapi/read_only_service.rb +8 -0
  11. data/lib/appnexusapi/resource.rb +19 -15
  12. data/lib/appnexusapi/service.rb +35 -48
  13. data/lib/appnexusapi/services/ad_server_service.rb +9 -0
  14. data/lib/appnexusapi/{advertiser_service.rb → services/advertiser_service.rb} +0 -0
  15. data/lib/appnexusapi/{bidder_instance_service.rb → services/bidder_instance_service.rb} +0 -6
  16. data/lib/appnexusapi/{bidder_profile_service.rb → services/bidder_profile_service.rb} +0 -6
  17. data/lib/appnexusapi/{bidder_service.rb → services/bidder_service.rb} +0 -0
  18. data/lib/appnexusapi/services/brand_service.rb +2 -0
  19. data/lib/appnexusapi/services/browser_service.rb +2 -0
  20. data/lib/appnexusapi/{campaign_service.rb → services/campaign_service.rb} +0 -0
  21. data/lib/appnexusapi/services/category_service.rb +5 -0
  22. data/lib/appnexusapi/{content_category_service.rb → services/content_category_service.rb} +0 -1
  23. data/lib/appnexusapi/{creative_service.rb → services/creative_service.rb} +0 -0
  24. data/lib/appnexusapi/{creative_template_service.rb → services/creative_template_service.rb} +1 -6
  25. data/lib/appnexusapi/services/device_model_service.rb +2 -0
  26. data/lib/appnexusapi/{domain_list_service.rb → services/domain_list_service.rb} +0 -0
  27. data/lib/appnexusapi/services/inventory_attribute_service.rb +2 -0
  28. data/lib/appnexusapi/{inventory_source_service.rb → services/inventory_source_service.rb} +0 -0
  29. data/lib/appnexusapi/services/language_service.rb +2 -0
  30. data/lib/appnexusapi/{line_item_service.rb → services/line_item_service.rb} +0 -0
  31. data/lib/appnexusapi/services/log_level_data_download_service.rb +13 -0
  32. data/lib/appnexusapi/services/log_level_data_service.rb +105 -0
  33. data/lib/appnexusapi/services/media_type_service.rb +2 -0
  34. data/lib/appnexusapi/{member_service.rb → services/member_service.rb} +0 -0
  35. data/lib/appnexusapi/{object_limit_service.rb → services/object_limit_service.rb} +0 -0
  36. data/lib/appnexusapi/{operating_system_extended_service.rb → services/operating_system_extended_service.rb} +1 -6
  37. data/lib/appnexusapi/services/operating_system_service.rb +2 -0
  38. data/lib/appnexusapi/{payment_rule_service.rb → services/payment_rule_service.rb} +0 -0
  39. data/lib/appnexusapi/{placement_service.rb → services/placement_service.rb} +0 -0
  40. data/lib/appnexusapi/services/platform_member_service.rb +2 -0
  41. data/lib/appnexusapi/{profile_service.rb → services/profile_service.rb} +0 -0
  42. data/lib/appnexusapi/{publisher_service.rb → services/publisher_service.rb} +0 -0
  43. data/lib/appnexusapi/{segment_service.rb → services/segment_service.rb} +0 -2
  44. data/lib/appnexusapi/{site_service.rb → services/site_service.rb} +0 -0
  45. data/lib/appnexusapi/services/technical_attribute_service.rb +2 -0
  46. data/lib/appnexusapi/{tiny_tag_service.rb → services/tiny_tag_service.rb} +0 -6
  47. data/lib/appnexusapi/{user_service.rb → services/user_service.rb} +0 -0
  48. data/lib/appnexusapi/version.rb +1 -1
  49. data/spec/connection_spec.rb +46 -10
  50. data/spec/fixtures/vcr/content_category_crud.yml +188 -69
  51. data/spec/fixtures/vcr/log_level_data_service_download.yml +213 -0
  52. data/spec/integration/content_category_spec.rb +4 -0
  53. data/spec/{creative_service_spec.rb → integration/creative_service_spec.rb} +0 -0
  54. data/spec/integration/log_level_data_service_spec.rb +13 -0
  55. data/spec/{object_limit_service_spec.rb → integration/object_limit_service_spec.rb} +0 -0
  56. data/spec/integration/placement_spec.rb +0 -1
  57. data/spec/spec_helper.rb +2 -3
  58. metadata +52 -94
  59. data/lib/appnexusapi/ad_server_resource.rb +0 -2
  60. data/lib/appnexusapi/ad_server_service.rb +0 -20
  61. data/lib/appnexusapi/advertiser_resource.rb +0 -2
  62. data/lib/appnexusapi/bidder_instance_resource.rb +0 -2
  63. data/lib/appnexusapi/bidder_profile_resource.rb +0 -2
  64. data/lib/appnexusapi/bidder_resource.rb +0 -2
  65. data/lib/appnexusapi/brand_resource.rb +0 -2
  66. data/lib/appnexusapi/brand_service.rb +0 -8
  67. data/lib/appnexusapi/browser_resource.rb +0 -2
  68. data/lib/appnexusapi/browser_service.rb +0 -8
  69. data/lib/appnexusapi/campaign_resource.rb +0 -2
  70. data/lib/appnexusapi/category_resource.rb +0 -2
  71. data/lib/appnexusapi/category_service.rb +0 -12
  72. data/lib/appnexusapi/content_category_resource.rb +0 -2
  73. data/lib/appnexusapi/creative_resource.rb +0 -2
  74. data/lib/appnexusapi/creative_template_resource.rb +0 -2
  75. data/lib/appnexusapi/device_model_resource.rb +0 -2
  76. data/lib/appnexusapi/device_model_service.rb +0 -6
  77. data/lib/appnexusapi/domain_list_resource.rb +0 -2
  78. data/lib/appnexusapi/inventory_attribute_resource.rb +0 -2
  79. data/lib/appnexusapi/inventory_attribute_service.rb +0 -8
  80. data/lib/appnexusapi/inventory_source_resource.rb +0 -2
  81. data/lib/appnexusapi/language_resource.rb +0 -2
  82. data/lib/appnexusapi/language_service.rb +0 -8
  83. data/lib/appnexusapi/line_item_resource.rb +0 -2
  84. data/lib/appnexusapi/log_level_data_download_service.rb +0 -68
  85. data/lib/appnexusapi/log_level_data_resource.rb +0 -19
  86. data/lib/appnexusapi/log_level_data_service.rb +0 -26
  87. data/lib/appnexusapi/media_type_resource.rb +0 -2
  88. data/lib/appnexusapi/media_type_service.rb +0 -8
  89. data/lib/appnexusapi/member_resource.rb +0 -2
  90. data/lib/appnexusapi/object_limit_resource.rb +0 -2
  91. data/lib/appnexusapi/operating_system_extended_resource.rb +0 -2
  92. data/lib/appnexusapi/operating_system_resource.rb +0 -2
  93. data/lib/appnexusapi/operating_system_service.rb +0 -8
  94. data/lib/appnexusapi/payment_rule_resource.rb +0 -2
  95. data/lib/appnexusapi/placement_resource.rb +0 -2
  96. data/lib/appnexusapi/platform_member_resource.rb +0 -2
  97. data/lib/appnexusapi/platform_member_service.rb +0 -8
  98. data/lib/appnexusapi/profile_resource.rb +0 -2
  99. data/lib/appnexusapi/publisher_resource.rb +0 -2
  100. data/lib/appnexusapi/segment_resource.rb +0 -2
  101. data/lib/appnexusapi/site_resource.rb +0 -2
  102. data/lib/appnexusapi/technical_attribute_resource.rb +0 -2
  103. data/lib/appnexusapi/technical_attribute_service.rb +0 -8
  104. data/lib/appnexusapi/tiny_tag_resource.rb +0 -2
  105. data/lib/appnexusapi/user_resource.rb +0 -2
@@ -0,0 +1,8 @@
1
+ module AppnexusApi
2
+ class ReadOnlyService < Service
3
+ def initialize(connection)
4
+ @read_only = true
5
+ super(connection)
6
+ end
7
+ end
8
+ end
@@ -1,39 +1,43 @@
1
1
  class AppnexusApi::Resource
2
2
 
3
- attr_reader :dbg_info
3
+ attr_reader :dbg_info, :raw_json
4
4
 
5
5
  def initialize(json, service, dbg_info = nil)
6
- @json = json
6
+ @raw_json = json
7
7
  @service = service
8
8
  @dbg_info = dbg_info
9
9
  end
10
10
 
11
- def update(route_params={}, body_params={})
12
- resource = @service.update(id, route_params, body_params)
13
- @json = resource.raw_json
11
+ def update(route_params = {}, body_params = {})
12
+ @raw_json = @service.update(id, route_params, body_params).raw_json
14
13
  self
15
14
  end
16
15
 
17
- def delete(route_params={})
18
- @service.delete(id, route_params)
16
+ # If you have modified the @raw_json hash in place, you can just do resource.save
17
+ def save
18
+ @service.update(id, {}, @raw_json).raw_json
19
+ self
19
20
  end
20
21
 
21
- def raw_json
22
- @json
22
+ def delete(route_params = {})
23
+ @service.delete(id, route_params)
23
24
  end
24
25
 
25
26
  def method_missing(sym, *args, &block)
26
- if @json.respond_to?(sym)
27
- @json.send(sym, *args, &block)
28
- elsif @json.has_key?(sym.to_s)
29
- return @json[sym.to_s]
27
+ if @raw_json.respond_to?(sym)
28
+ @raw_json.send(sym, *args, &block)
29
+ elsif @raw_json.key?(sym.to_s)
30
+ @raw_json[sym.to_s]
30
31
  else
31
32
  super(sym, *args, &block)
32
33
  end
33
34
  end
34
35
 
35
- def to_s
36
- @json.inspect
36
+ def respond_to_missing?(method_name, include_private = false)
37
+ @raw_json.respond_to?(method_name) || @raw_json.key?(method_name.to_s) || super
37
38
  end
38
39
 
40
+ def to_s
41
+ @raw_json.inspect
42
+ end
39
43
  end
@@ -7,7 +7,7 @@ class AppnexusApi::Service
7
7
 
8
8
  def name
9
9
  @name ||= begin
10
- str = self.class.name.split("::").last.gsub("Service", "")
10
+ str = self.class.name.split('::').last.sub(/Service\z/, '')
11
11
  str.gsub(/(.)([A-Z])/, '\1_\2').downcase
12
12
  end
13
13
  end
@@ -16,13 +16,6 @@ class AppnexusApi::Service
16
16
  name + 's'
17
17
  end
18
18
 
19
- def resource_class
20
- @resource_class ||= begin
21
- resource_name = name.capitalize.gsub(/(_(.))/) { |c| $2.upcase }
22
- AppnexusApi.const_get(resource_name + "Resource")
23
- end
24
- end
25
-
26
19
  def uri_name
27
20
  name.gsub('_', '-')
28
21
  end
@@ -35,20 +28,12 @@ class AppnexusApi::Service
35
28
  uri_name
36
29
  end
37
30
 
38
- def get(params={})
39
- return_response = params.delete(:return_response) || false
40
- params = {
41
- "num_elements" => DEFAULT_NUMBER_OF_ELEMENTS,
42
- "start_element" => 0
43
- }.merge(params)
44
- response = @connection.get(uri_suffix, params).body['response']
45
- if return_response
46
- response
47
- else
48
- parse_response(response)
49
- end
31
+ def get(params = {})
32
+ params = { 'num_elements' => DEFAULT_NUMBER_OF_ELEMENTS, 'start_element' => 0 }.merge(params)
33
+ parse_response(@connection.get(uri_suffix, params).body['response'])
50
34
  end
51
35
 
36
+ # Page through all available elements automatically
52
37
  def get_all(params = {})
53
38
  responses = []
54
39
  last_responses = get(params)
@@ -61,54 +46,56 @@ class AppnexusApi::Service
61
46
  responses
62
47
  end
63
48
 
64
- def create(route_params={}, body={})
65
- raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
66
- body = { uri_name => body }
49
+ def create(route_params = {}, body = {})
50
+ check_read_only!
67
51
  route = @connection.build_url(uri_suffix, route_params)
68
- response = @connection.post(route, body).body['response']
69
- if response['error_id']
70
- response.delete('dbg')
71
- raise AppnexusApi::BadRequest.new(response.inspect)
72
- end
52
+ response = @connection.post(route, { uri_name => body }).body['response']
53
+ validate_response!(response)
54
+
73
55
  parse_response(response).first
74
56
  end
75
57
 
76
- def update(id, route_params={}, body={})
77
- raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
78
- body = { uri_name => body }
79
- route = @connection.build_url(uri_suffix, route_params.merge("id" => id))
80
- response = @connection.put(route, body).body['response']
81
- if response['error_id']
82
- response.delete('dbg')
83
- raise AppnexusApi::BadRequest.new(response.inspect)
84
- end
58
+ def update(id, route_params = {}, body = {})
59
+ check_read_only!
60
+ route = @connection.build_url(uri_suffix, route_params.merge('id' => id))
61
+ response = @connection.put(route, { uri_name => body }).body['response']
62
+ validate_response!(response)
63
+
85
64
  parse_response(response).first
86
65
  end
87
66
 
88
67
  def delete(id, route_params)
89
- raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
90
- route = @connection.build_url(uri_suffix, route_params.merge("id" => id))
68
+ check_read_only!
69
+ route = @connection.build_url(uri_suffix, route_params.merge('id' => id))
91
70
  response = @connection.delete(route).body['response']
92
- if response['error_id']
93
- response.delete('dbg')
94
- raise AppnexusApi::BadRequest.new(response.inspect)
95
- end
71
+ validate_response!(response)
72
+
96
73
  response
97
74
  end
98
75
 
76
+ private
77
+
78
+ def check_read_only!
79
+ raise(AppnexusApi::NotImplemented, "Service is read-only.") if @read_only
80
+ end
81
+
82
+ def validate_response!(response)
83
+ return unless response['error_id']
84
+
85
+ response.delete('dbg')
86
+ raise AppnexusApi::BadRequest.new(response.inspect)
87
+ end
88
+
99
89
  def parse_response(response)
100
90
  case key = resource_name(response)
101
91
  when plural_name, plural_uri_name
102
- response[key].map do |json|
103
- resource_class.new(json, self, response['dbg'])
104
- end
92
+ response[key].map { |json| AppnexusApi::Resource.new(json, self, response['dbg']) }
105
93
  when name, uri_name
106
- [resource_class.new(response[key], self, response['dbg'])]
94
+ [AppnexusApi::Resource.new(response[key], self, response['dbg'])]
107
95
  end
108
96
  end
109
97
 
110
98
  def resource_name(response)
111
99
  [plural_name, plural_uri_name, name, uri_name].detect { |n| response.key?(n) }
112
100
  end
113
-
114
101
  end
@@ -0,0 +1,9 @@
1
+ class AppnexusApi::AdServerService < AppnexusApi::ReadOnlyService
2
+ def name
3
+ "adserver"
4
+ end
5
+
6
+ def uri_suffix
7
+ "ad-server"
8
+ end
9
+ end
@@ -1,5 +1,4 @@
1
1
  class AppnexusApi::BidderInstanceService < AppnexusApi::Service
2
-
3
2
  def initialize(connection, bidder_id)
4
3
  @bidder_id = bidder_id
5
4
  super(connection)
@@ -9,10 +8,6 @@ class AppnexusApi::BidderInstanceService < AppnexusApi::Service
9
8
  "instance"
10
9
  end
11
10
 
12
- def resource_class
13
- AppnexusApi::BidderInstanceResource
14
- end
15
-
16
11
  def uri_suffix
17
12
  "bidder-instance/#{@bidder_id}"
18
13
  end
@@ -20,5 +15,4 @@ class AppnexusApi::BidderInstanceService < AppnexusApi::Service
20
15
  def delete(id)
21
16
  raise AppnexusApi::NotImplemented, "To remove an instance, please set it to inactive."
22
17
  end
23
-
24
18
  end
@@ -1,5 +1,4 @@
1
1
  class AppnexusApi::BidderProfileService < AppnexusApi::Service
2
-
3
2
  def initialize(connection, bidder_id)
4
3
  @bidder_id = bidder_id
5
4
  super(connection)
@@ -9,10 +8,6 @@ class AppnexusApi::BidderProfileService < AppnexusApi::Service
9
8
  "profile"
10
9
  end
11
10
 
12
- def resource_class
13
- AppnexusApi::BidderProfileResource
14
- end
15
-
16
11
  def uri_suffix
17
12
  "profile/#{@bidder_id}"
18
13
  end
@@ -20,5 +15,4 @@ class AppnexusApi::BidderProfileService < AppnexusApi::Service
20
15
  def delete(id)
21
16
  raise AppnexusApi::NotImplemented
22
17
  end
23
-
24
18
  end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::BrandService < AppnexusApi::ReadOnlyService
2
+ end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::BrowserService < AppnexusApi::ReadOnlyService
2
+ end
@@ -0,0 +1,5 @@
1
+ class AppnexusApi::CategoryService < AppnexusApi::ReadOnlyService
2
+ def plural_name
3
+ "categories"
4
+ end
5
+ end
@@ -1,5 +1,4 @@
1
1
  class AppnexusApi::ContentCategoryService < AppnexusApi::Service
2
-
3
2
  def initialize(connection)
4
3
  super(connection)
5
4
  end
@@ -3,16 +3,11 @@ class AppnexusApi::CreativeTemplateService < AppnexusApi::Service
3
3
  "template"
4
4
  end
5
5
 
6
- def resource_class
7
- AppnexusApi::CreativeTemplateResource
8
- end
9
-
10
6
  def uri_suffix
11
- "template"
7
+ name
12
8
  end
13
9
 
14
10
  def delete(id)
15
11
  raise AppnexusApi::NotImplemented
16
12
  end
17
-
18
13
  end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::DeviceModelService < AppnexusApi::ReadOnlyService
2
+ end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::InventoryAttributeService < AppnexusApi::ReadOnlyService
2
+ end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::LanguageService < AppnexusApi::ReadOnlyService
2
+ end
@@ -0,0 +1,13 @@
1
+ require 'appnexusapi/services/log_level_data_service'
2
+
3
+ # TODO: Remove this class in v2.0
4
+ module AppnexusApi
5
+ class LogLevelDataDownloadService < LogLevelDataService
6
+ extend Gem::Deprecate
7
+
8
+ def initialize(connection, options = {})
9
+ super
10
+ end
11
+ deprecate :initialize, 'LogLevelDataService#initialize', 2017, 6
12
+ end
13
+ end
@@ -0,0 +1,105 @@
1
+ module AppnexusApi
2
+ class LogLevelDataService < AppnexusApi::ReadOnlyService
3
+ DEFAULT_FEED = 'standard_feed'.freeze
4
+ DOWNLOAD_URI = 'siphon-download'.freeze
5
+ RETRY_DOWNLOAD_PARAMS = {
6
+ base_interval: 30,
7
+ tries: 20,
8
+ max_elapsed_time: 3600,
9
+ on: [AppnexusApi::Error, ::Faraday::Error::ClientError],
10
+ on_retry: Proc.new do |exception, tries|
11
+ AppnexusApi.config.logger.warn("Retrying after #{exception.class}: #{tries} attempts.")
12
+ end
13
+ }.freeze
14
+
15
+ def initialize(connection, options = {})
16
+ @downloaded_files_path = options[:downloaded_files_path] || '.'
17
+ @siphon_name = options[:siphon_name] || DEFAULT_FEED
18
+ super(connection)
19
+ end
20
+
21
+ def download_new_files_since(time = nil)
22
+ since(time).map { |siphon| download_resource(siphon) }
23
+ end
24
+
25
+ def uri_name
26
+ 'siphon'
27
+ end
28
+
29
+ def since(time = nil)
30
+ params = {}
31
+ params[:siphon_name] = @siphon_name if @siphon_name
32
+ params[:updated_since] = time.strftime('%Y_%m_%d_%H') if time
33
+ siphons = get(params) || {}
34
+
35
+ # Anything with the same name and hour but with a newer timestamp is a republished replacement for an older file.
36
+ # When this happens appnexus is supposed to set the checksum for the old file to NULL but they do not always
37
+ # actually do this, so we have to manually reject invalid entries.
38
+ siphons.reject do |siphon|
39
+ (siphons - [siphon]).any? { |s| s.name == siphon.name && s.hour == siphon.hour && s.timestamp > siphon.timestamp }
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ # Parameter is a LogLevelDataResource FKA a "Siphon"
46
+ # Downloads a gzipped file
47
+ # Returns an array of paths to downloaded files
48
+ def download_resource(siphon)
49
+ fail 'Missing necessary information!' unless siphon.name && siphon.hour && siphon.timestamp && siphon.splits
50
+
51
+ download_params = siphon.splits.map do |split_part|
52
+ # In the case of files that were later replaced, there should be no checksum and they shouldn't be downloaded
53
+ next nil if split_part['checksum'].nil? || split_part['checksum'].empty?
54
+
55
+ {
56
+ split_part: split_part['part'],
57
+ siphon_name: siphon.name,
58
+ timestamp: siphon.timestamp,
59
+ hour: siphon.hour,
60
+ checksum: split_part['checksum']
61
+ }
62
+ end.compact
63
+
64
+ download_params.map do |params|
65
+ uri = URI.parse(@connection.get(DOWNLOAD_URI, params.reject { |k, v| k == :checksum }).headers['location'])
66
+ filename = File.join(@downloaded_files_path, "#{params[:siphon_name]}_#{params[:hour]}_#{params[:split_part]}.gz")
67
+
68
+ Retriable.retriable(RETRY_DOWNLOAD_PARAMS) do
69
+ download_file(uri, filename)
70
+ calculated_checksum = Digest::MD5.hexdigest(File.read(filename))
71
+ if calculated_checksum != params[:checksum]
72
+ error_message = "Calculated checksum of #{calculated_checksum} doesn't match API provided checksum #{params[:checksum]}"
73
+ AppnexusApi.config.logger.fatal(error_message)
74
+ raise BadChecksumException, error_message
75
+ end
76
+ end
77
+
78
+ filename
79
+ end
80
+ end
81
+
82
+ def download_file(uri, filename)
83
+ AppnexusApi.config.logger.debug("Starting HTTP download for: #{uri.to_s}...")
84
+ http_object = Net::HTTP.new(uri.host, uri.port)
85
+ http_object.use_ssl = true if uri.scheme == 'https'
86
+
87
+ begin
88
+ http_object.start do |http|
89
+ request = Net::HTTP::Get.new(uri.request_uri)
90
+ http.read_timeout = 500
91
+
92
+ http.request(request) do |response|
93
+ open(filename, 'wb') do |io|
94
+ response.read_body do |chunk|
95
+ io.write(chunk)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ AppnexusApi.config.logger.info("Stored download of #{uri.to_s} as #{filename}")
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,2 @@
1
+ class AppnexusApi::MediaTypeService < AppnexusApi::ReadOnlyService
2
+ end