apisonator 2.100.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +317 -0
  3. data/Gemfile +11 -0
  4. data/Gemfile.base +65 -0
  5. data/Gemfile.lock +319 -0
  6. data/Gemfile.on_prem +1 -0
  7. data/Gemfile.on_prem.lock +297 -0
  8. data/LICENSE +202 -0
  9. data/NOTICE +15 -0
  10. data/README.md +230 -0
  11. data/Rakefile +287 -0
  12. data/apisonator.gemspec +47 -0
  13. data/app/api/api.rb +13 -0
  14. data/app/api/internal/alert_limits.rb +32 -0
  15. data/app/api/internal/application_keys.rb +49 -0
  16. data/app/api/internal/application_referrer_filters.rb +43 -0
  17. data/app/api/internal/applications.rb +77 -0
  18. data/app/api/internal/errors.rb +54 -0
  19. data/app/api/internal/events.rb +42 -0
  20. data/app/api/internal/internal.rb +104 -0
  21. data/app/api/internal/metrics.rb +40 -0
  22. data/app/api/internal/service_tokens.rb +46 -0
  23. data/app/api/internal/services.rb +58 -0
  24. data/app/api/internal/stats.rb +42 -0
  25. data/app/api/internal/usagelimits.rb +62 -0
  26. data/app/api/internal/utilization.rb +23 -0
  27. data/bin/3scale_backend +223 -0
  28. data/bin/3scale_backend_worker +26 -0
  29. data/config.ru +4 -0
  30. data/config/puma.rb +192 -0
  31. data/config/schedule.rb +9 -0
  32. data/ext/mkrf_conf.rb +64 -0
  33. data/lib/3scale/backend.rb +67 -0
  34. data/lib/3scale/backend/alert_limit.rb +56 -0
  35. data/lib/3scale/backend/alerts.rb +137 -0
  36. data/lib/3scale/backend/analytics/kinesis.rb +3 -0
  37. data/lib/3scale/backend/analytics/kinesis/adapter.rb +180 -0
  38. data/lib/3scale/backend/analytics/kinesis/exporter.rb +86 -0
  39. data/lib/3scale/backend/analytics/kinesis/job.rb +135 -0
  40. data/lib/3scale/backend/analytics/redshift.rb +3 -0
  41. data/lib/3scale/backend/analytics/redshift/adapter.rb +367 -0
  42. data/lib/3scale/backend/analytics/redshift/importer.rb +83 -0
  43. data/lib/3scale/backend/analytics/redshift/job.rb +33 -0
  44. data/lib/3scale/backend/application.rb +330 -0
  45. data/lib/3scale/backend/application_events.rb +76 -0
  46. data/lib/3scale/backend/background_job.rb +65 -0
  47. data/lib/3scale/backend/configurable.rb +20 -0
  48. data/lib/3scale/backend/configuration.rb +151 -0
  49. data/lib/3scale/backend/configuration/loader.rb +42 -0
  50. data/lib/3scale/backend/constants.rb +19 -0
  51. data/lib/3scale/backend/cors.rb +84 -0
  52. data/lib/3scale/backend/distributed_lock.rb +67 -0
  53. data/lib/3scale/backend/environment.rb +21 -0
  54. data/lib/3scale/backend/error_storage.rb +52 -0
  55. data/lib/3scale/backend/errors.rb +343 -0
  56. data/lib/3scale/backend/event_storage.rb +120 -0
  57. data/lib/3scale/backend/experiment.rb +84 -0
  58. data/lib/3scale/backend/extensions.rb +5 -0
  59. data/lib/3scale/backend/extensions/array.rb +19 -0
  60. data/lib/3scale/backend/extensions/hash.rb +26 -0
  61. data/lib/3scale/backend/extensions/nil_class.rb +13 -0
  62. data/lib/3scale/backend/extensions/redis.rb +44 -0
  63. data/lib/3scale/backend/extensions/string.rb +13 -0
  64. data/lib/3scale/backend/extensions/time.rb +110 -0
  65. data/lib/3scale/backend/failed_jobs_scheduler.rb +141 -0
  66. data/lib/3scale/backend/job_fetcher.rb +122 -0
  67. data/lib/3scale/backend/listener.rb +728 -0
  68. data/lib/3scale/backend/listener_metrics.rb +99 -0
  69. data/lib/3scale/backend/logging.rb +48 -0
  70. data/lib/3scale/backend/logging/external.rb +44 -0
  71. data/lib/3scale/backend/logging/external/impl.rb +93 -0
  72. data/lib/3scale/backend/logging/external/impl/airbrake.rb +66 -0
  73. data/lib/3scale/backend/logging/external/impl/bugsnag.rb +69 -0
  74. data/lib/3scale/backend/logging/external/impl/default.rb +18 -0
  75. data/lib/3scale/backend/logging/external/resque.rb +57 -0
  76. data/lib/3scale/backend/logging/logger.rb +18 -0
  77. data/lib/3scale/backend/logging/middleware.rb +62 -0
  78. data/lib/3scale/backend/logging/middleware/json_writer.rb +21 -0
  79. data/lib/3scale/backend/logging/middleware/text_writer.rb +60 -0
  80. data/lib/3scale/backend/logging/middleware/writer.rb +143 -0
  81. data/lib/3scale/backend/logging/worker.rb +107 -0
  82. data/lib/3scale/backend/manifest.rb +80 -0
  83. data/lib/3scale/backend/memoizer.rb +277 -0
  84. data/lib/3scale/backend/metric.rb +275 -0
  85. data/lib/3scale/backend/metric/collection.rb +91 -0
  86. data/lib/3scale/backend/oauth.rb +4 -0
  87. data/lib/3scale/backend/oauth/token.rb +26 -0
  88. data/lib/3scale/backend/oauth/token_key.rb +30 -0
  89. data/lib/3scale/backend/oauth/token_storage.rb +313 -0
  90. data/lib/3scale/backend/oauth/token_value.rb +25 -0
  91. data/lib/3scale/backend/period.rb +3 -0
  92. data/lib/3scale/backend/period/boundary.rb +107 -0
  93. data/lib/3scale/backend/period/cache.rb +28 -0
  94. data/lib/3scale/backend/period/period.rb +402 -0
  95. data/lib/3scale/backend/queue_storage.rb +16 -0
  96. data/lib/3scale/backend/rack.rb +49 -0
  97. data/lib/3scale/backend/rack/exception_catcher.rb +136 -0
  98. data/lib/3scale/backend/rack/internal_error_catcher.rb +23 -0
  99. data/lib/3scale/backend/rack/prometheus.rb +19 -0
  100. data/lib/3scale/backend/saas.rb +6 -0
  101. data/lib/3scale/backend/saas_analytics.rb +4 -0
  102. data/lib/3scale/backend/server.rb +30 -0
  103. data/lib/3scale/backend/server/falcon.rb +52 -0
  104. data/lib/3scale/backend/server/puma.rb +71 -0
  105. data/lib/3scale/backend/service.rb +317 -0
  106. data/lib/3scale/backend/service_token.rb +97 -0
  107. data/lib/3scale/backend/stats.rb +8 -0
  108. data/lib/3scale/backend/stats/aggregator.rb +170 -0
  109. data/lib/3scale/backend/stats/aggregators/base.rb +72 -0
  110. data/lib/3scale/backend/stats/aggregators/response_code.rb +58 -0
  111. data/lib/3scale/backend/stats/aggregators/usage.rb +34 -0
  112. data/lib/3scale/backend/stats/bucket_reader.rb +135 -0
  113. data/lib/3scale/backend/stats/bucket_storage.rb +108 -0
  114. data/lib/3scale/backend/stats/cleaner.rb +195 -0
  115. data/lib/3scale/backend/stats/codes_commons.rb +14 -0
  116. data/lib/3scale/backend/stats/delete_job_def.rb +60 -0
  117. data/lib/3scale/backend/stats/key_generator.rb +73 -0
  118. data/lib/3scale/backend/stats/keys.rb +104 -0
  119. data/lib/3scale/backend/stats/partition_eraser_job.rb +58 -0
  120. data/lib/3scale/backend/stats/partition_generator_job.rb +46 -0
  121. data/lib/3scale/backend/stats/period_commons.rb +34 -0
  122. data/lib/3scale/backend/stats/stats_parser.rb +141 -0
  123. data/lib/3scale/backend/stats/storage.rb +113 -0
  124. data/lib/3scale/backend/statsd.rb +14 -0
  125. data/lib/3scale/backend/storable.rb +35 -0
  126. data/lib/3scale/backend/storage.rb +40 -0
  127. data/lib/3scale/backend/storage_async.rb +4 -0
  128. data/lib/3scale/backend/storage_async/async_redis.rb +21 -0
  129. data/lib/3scale/backend/storage_async/client.rb +205 -0
  130. data/lib/3scale/backend/storage_async/pipeline.rb +79 -0
  131. data/lib/3scale/backend/storage_async/resque_extensions.rb +30 -0
  132. data/lib/3scale/backend/storage_helpers.rb +278 -0
  133. data/lib/3scale/backend/storage_key_helpers.rb +9 -0
  134. data/lib/3scale/backend/storage_sync.rb +43 -0
  135. data/lib/3scale/backend/transaction.rb +62 -0
  136. data/lib/3scale/backend/transactor.rb +177 -0
  137. data/lib/3scale/backend/transactor/limit_headers.rb +54 -0
  138. data/lib/3scale/backend/transactor/notify_batcher.rb +139 -0
  139. data/lib/3scale/backend/transactor/notify_job.rb +47 -0
  140. data/lib/3scale/backend/transactor/process_job.rb +33 -0
  141. data/lib/3scale/backend/transactor/report_job.rb +84 -0
  142. data/lib/3scale/backend/transactor/status.rb +236 -0
  143. data/lib/3scale/backend/transactor/usage_report.rb +182 -0
  144. data/lib/3scale/backend/usage.rb +63 -0
  145. data/lib/3scale/backend/usage_limit.rb +115 -0
  146. data/lib/3scale/backend/use_cases/provider_key_change_use_case.rb +60 -0
  147. data/lib/3scale/backend/util.rb +17 -0
  148. data/lib/3scale/backend/validators.rb +26 -0
  149. data/lib/3scale/backend/validators/base.rb +36 -0
  150. data/lib/3scale/backend/validators/key.rb +17 -0
  151. data/lib/3scale/backend/validators/limits.rb +57 -0
  152. data/lib/3scale/backend/validators/oauth_key.rb +15 -0
  153. data/lib/3scale/backend/validators/oauth_setting.rb +15 -0
  154. data/lib/3scale/backend/validators/redirect_uri.rb +33 -0
  155. data/lib/3scale/backend/validators/referrer.rb +60 -0
  156. data/lib/3scale/backend/validators/service_state.rb +15 -0
  157. data/lib/3scale/backend/validators/state.rb +15 -0
  158. data/lib/3scale/backend/version.rb +5 -0
  159. data/lib/3scale/backend/views/oauth_access_tokens.builder +14 -0
  160. data/lib/3scale/backend/views/oauth_app_id_by_token.builder +4 -0
  161. data/lib/3scale/backend/worker.rb +87 -0
  162. data/lib/3scale/backend/worker_async.rb +88 -0
  163. data/lib/3scale/backend/worker_metrics.rb +44 -0
  164. data/lib/3scale/backend/worker_sync.rb +32 -0
  165. data/lib/3scale/bundler_shim.rb +17 -0
  166. data/lib/3scale/prometheus_server.rb +10 -0
  167. data/lib/3scale/tasks/connectivity.rake +41 -0
  168. data/lib/3scale/tasks/helpers.rb +3 -0
  169. data/lib/3scale/tasks/helpers/environment.rb +23 -0
  170. data/lib/3scale/tasks/stats.rake +131 -0
  171. data/lib/3scale/tasks/swagger.rake +46 -0
  172. data/licenses.xml +1215 -0
  173. metadata +227 -0
@@ -0,0 +1,21 @@
1
+ module ThreeScale
2
+ module Backend
3
+ class << self
4
+ def environment
5
+ @environment ||= ENV['RACK_ENV'] || 'development'
6
+ end
7
+
8
+ def production?
9
+ environment == 'production'
10
+ end
11
+
12
+ def development?
13
+ environment == 'development'
14
+ end
15
+
16
+ def test?
17
+ environment == 'test'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
1
+ module ThreeScale
2
+ module Backend
3
+ module ErrorStorage
4
+ include StorageHelpers
5
+ extend self
6
+
7
+ PER_PAGE = 100
8
+ MAX_NUM_ERRORS = 1000
9
+
10
+ def store(service_id, error, context_info = {})
11
+ request_info = context_info[:request] || {}
12
+ storage.lpush(queue_key(service_id),
13
+ encode(code: error.code,
14
+ message: error.message,
15
+ timestamp: Time.now.getutc.to_s,
16
+ context_info: request_info))
17
+ storage.ltrim(queue_key(service_id), 0, MAX_NUM_ERRORS - 1)
18
+ end
19
+
20
+ # Pages start at 1, same as in will_paginate.
21
+ def list(service_id, options = {})
22
+ page = options[:page] || 1
23
+ per_page = options[:per_page] || PER_PAGE
24
+ range = pagination_to_range(page.to_i, per_page.to_i)
25
+
26
+ raw_items = (storage.lrange(queue_key(service_id), range.begin, range.end) || [])
27
+ raw_items.map(&method(:decode))
28
+ end
29
+
30
+ def count(service_id)
31
+ storage.llen(queue_key(service_id))
32
+ end
33
+
34
+ def delete_all(service_id)
35
+ storage.del(queue_key(service_id))
36
+ end
37
+
38
+ private
39
+
40
+ def queue_key(service_id)
41
+ "errors/service_id:#{service_id}"
42
+ end
43
+
44
+ def pagination_to_range(page, per_page)
45
+ range_start = (page - 1) * per_page
46
+ range_end = range_start + per_page - 1
47
+
48
+ range_start..range_end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,343 @@
1
+ module ThreeScale
2
+ module Backend
3
+ class Error < RuntimeError
4
+ def to_xml(options = {})
5
+ xml = Builder::XmlMarkup.new
6
+ xml.instruct! unless options[:skip_instruct]
7
+ xml.error(message, :code => code)
8
+
9
+ xml.target!
10
+ end
11
+
12
+ # Note: DON'T change this to http_status; Sinatra will pick it up and break!
13
+ def http_code
14
+ 403
15
+ end
16
+
17
+ def code
18
+ self.class.code
19
+ end
20
+
21
+ def self.code
22
+ underscore(name[/[^:]*$/])
23
+ end
24
+
25
+ def self.underscore(string)
26
+ string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
27
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
28
+ downcase
29
+ end
30
+ end
31
+
32
+ class BadRequest < Error
33
+ def initialize(msg = 'request contains syntax errors, should not be repeated without modification'.freeze)
34
+ super msg
35
+ end
36
+
37
+ def http_code
38
+ 400
39
+ end
40
+ end
41
+
42
+ class NotFound < Error
43
+ def http_code
44
+ 404
45
+ end
46
+ end
47
+
48
+ class Invalid < Error
49
+ def http_code
50
+ 422
51
+ end
52
+ end
53
+
54
+ class ApplicationKeyInvalid < Error
55
+ def initialize(key)
56
+ if key.blank?
57
+ super 'application key is missing'.freeze
58
+ else
59
+ super %(application key "#{key}" is invalid)
60
+ end
61
+ end
62
+ end
63
+
64
+ class ApplicationHasInconsistentData < Error
65
+ def initialize(id, user_key)
66
+ super %(Application id="#{id}" with user_key="#{user_key}" has inconsistent data and could not be saved)
67
+ end
68
+ end
69
+
70
+ class ApplicationNotFound < NotFound
71
+ def initialize(id = nil)
72
+ super %(application with id="#{id}" was not found)
73
+ end
74
+ end
75
+
76
+ class AccessTokenInvalid < NotFound
77
+ def initialize(id = nil)
78
+ super %(token "#{id}" is invalid: expired or never defined)
79
+ end
80
+ end
81
+
82
+ class AccessTokenAlreadyExists < Error
83
+ def initialize(id = nil)
84
+ super %(token "#{id}" already exists)
85
+ end
86
+ end
87
+
88
+ class AccessTokenStorageError < Error
89
+ def initialize(id = nil)
90
+ super %(storage error when saving token "#{id}")
91
+ end
92
+ end
93
+
94
+ class AccessTokenFormatInvalid < Invalid
95
+ def initialize
96
+ super 'token is either too big or has an invalid format'.freeze
97
+ end
98
+ end
99
+
100
+ class AccessTokenInvalidTTL < Invalid
101
+ def initialize
102
+ super 'the specified TTL should be a positive integer'.freeze
103
+ end
104
+ end
105
+
106
+ class ServiceNotActive < Error
107
+ def initialize
108
+ super 'service is not active'.freeze
109
+ end
110
+ end
111
+
112
+ class ApplicationNotActive < Error
113
+ def initialize
114
+ super 'application is not active'.freeze
115
+ end
116
+ end
117
+
118
+ class OauthNotEnabled < Error
119
+ def initialize
120
+ super 'oauth is not enabled'.freeze
121
+ end
122
+ end
123
+
124
+ class RedirectURIInvalid < Error
125
+ def initialize(uri)
126
+ super %(redirect_uri "#{uri}" is invalid)
127
+ end
128
+ end
129
+
130
+ class RedirectURLInvalid < Error
131
+ def initialize(url)
132
+ super %(redirect_url "#{url}" is invalid)
133
+ end
134
+ end
135
+
136
+ class LimitsExceeded < Error
137
+ def initialize
138
+ super 'usage limits are exceeded'.freeze
139
+ end
140
+ end
141
+
142
+ class ProviderKeyInvalid < Error
143
+ def initialize(key)
144
+ super %(provider key "#{key}" is invalid)
145
+ end
146
+ end
147
+
148
+ class ServiceIdInvalid < Error
149
+ def initialize(id)
150
+ super %(service id "#{id}" is invalid)
151
+ end
152
+ end
153
+
154
+ class MetricInvalid < NotFound
155
+ def initialize(metric_name)
156
+ super %(metric "#{metric_name}" is invalid)
157
+ end
158
+ end
159
+
160
+ class ReferrerFilterInvalid < Invalid
161
+ end
162
+
163
+ class NotValidData < Invalid
164
+ def initialize
165
+ super 'all data must be valid UTF8'.freeze
166
+ end
167
+ end
168
+
169
+ class ReferrerFiltersMissing < Error
170
+ def initialize
171
+ super 'referrer filters are missing'.freeze
172
+ end
173
+ end
174
+
175
+ class ReferrerNotAllowed < Error
176
+ def initialize(referrer)
177
+ if referrer.blank?
178
+ super 'referrer is missing'.freeze
179
+ else
180
+ super %(referrer "#{referrer}" is not allowed)
181
+ end
182
+ end
183
+ end
184
+
185
+ class RequiredParamsMissing < Invalid
186
+ def initialize
187
+ super 'missing required parameters'.freeze
188
+ end
189
+ end
190
+
191
+ class UsageValueInvalid < Error
192
+ def initialize(metric_name, value)
193
+ if !value.is_a?(String) || value.blank?
194
+ super %(usage value for metric "#{metric_name}" can not be empty)
195
+ else
196
+ super %(usage value "#{value}" for metric "#{metric_name}" is invalid)
197
+ end
198
+ end
199
+ end
200
+
201
+ class UnsupportedApiVersion < Error
202
+ end
203
+
204
+ class TransactionTimestampNotWithinRange < Error
205
+ end
206
+
207
+ class TransactionTimestampTooOld < TransactionTimestampNotWithinRange
208
+ def initialize(max_seconds)
209
+ super %(reporting transactions older than #{max_seconds} seconds is not allowed)
210
+ end
211
+ end
212
+
213
+ class TransactionTimestampTooNew < TransactionTimestampNotWithinRange
214
+ def initialize(max_seconds)
215
+ super %(reporting transactions more than #{max_seconds} seconds in the future is not allowed)
216
+ end
217
+ end
218
+
219
+ class ServiceLoadInconsistency < Error
220
+ def initialize(service_id, other_service_id)
221
+ super %(service.load_by_id with id="#{service_id}" loaded the service with id="#{other_service_id}")
222
+ end
223
+ end
224
+
225
+ class ServiceIsDefaultService < Error
226
+ def initialize(id = nil)
227
+ super %(Service id="#{id}" is the default service, cannot be removed)
228
+ end
229
+ end
230
+
231
+ class InvalidProviderKeys < Error
232
+ def initialize
233
+ super 'Provider keys are not valid, must be not nil and different'.freeze
234
+ end
235
+ end
236
+
237
+ class ProviderKeyExists < Error
238
+ def initialize(key)
239
+ super %(Provider key="#{key}" already exists)
240
+ end
241
+ end
242
+
243
+ class ProviderKeyNotFound < Error
244
+ def initialize(key)
245
+ super %(Provider key="#{key}" does not exist)
246
+ end
247
+ end
248
+
249
+ class InvalidEventType < Error
250
+ def initialize(type)
251
+ super %(Event type "#{type}" is invalid")
252
+ end
253
+ end
254
+
255
+ class ProviderKeyOrServiceTokenRequired < Error
256
+ def initialize
257
+ super 'Provider key or service token are required'.freeze
258
+ end
259
+ end
260
+
261
+ class ServiceIdMissing < Invalid
262
+ def initialize
263
+ super 'Service ID is missing'.freeze
264
+ end
265
+ end
266
+
267
+ # The name for this class stays as ServiceTokenInvalid even though the more
268
+ # correct name would be ServiceTokenOrIdInvalid to avoid breaking users.
269
+ class ServiceTokenInvalid < Error
270
+ def initialize(token, service_id)
271
+ super %(service token "#{token}" or service id "#{service_id}" is invalid)
272
+ end
273
+ end
274
+
275
+ # This is raised in these 2 situations:
276
+ # 1) The request does not contain a valid provider key nor a service ID.
277
+ # 2) The request contains a valid provider key, but does not contain a
278
+ # service ID, and the provider does not have a default service
279
+ # associated.
280
+ class ProviderKeyInvalidOrServiceMissing < Error
281
+ def initialize(provider_key)
282
+ super %(provider key "#{provider_key}" invalid and/or service ID missing)
283
+ end
284
+ end
285
+
286
+ # Legacy API support
287
+
288
+ class AuthenticationError < Error
289
+ def initialize
290
+ super 'either app_id or user_key is allowed, not both'.freeze
291
+ end
292
+ end
293
+
294
+ class UserKeyInvalid < Error
295
+ def initialize(key)
296
+ super %(user key "#{key}" is invalid)
297
+ end
298
+ end
299
+
300
+ # Bad Requests
301
+ class ContentTypeInvalid < BadRequest
302
+ def initialize(content_type)
303
+ super %(invalid Content-Type: #{content_type})
304
+ end
305
+ end
306
+
307
+ class TransactionsIsBlank < BadRequest
308
+ def initialize
309
+ super 'transactions parameter is blank'.freeze
310
+ end
311
+ end
312
+
313
+ class TransactionsFormatInvalid < BadRequest
314
+ def initialize
315
+ super 'transactions format is invalid'.freeze
316
+ end
317
+ end
318
+
319
+ class TransactionsHasNilTransaction < BadRequest
320
+ def initialize
321
+ super 'transactions has a nil transaction'.freeze
322
+ end
323
+ end
324
+
325
+ class ApplicationHasNoState < BadRequest
326
+ def initialize(id)
327
+ super %(Application with id="#{id}" has no state )
328
+ end
329
+ end
330
+
331
+ class DeleteServiceStatsValidationError < Error
332
+ def initialize(service_id, msg)
333
+ super "Delete stats job context validation error. Service: #{service_id}. Error: #{msg}"
334
+ end
335
+ end
336
+
337
+ class EndUsersNoLongerSupported < BadRequest
338
+ def initialize
339
+ super 'End-users are no longer supported, do not specify the user_id parameter'.freeze
340
+ end
341
+ end
342
+ end
343
+ end
@@ -0,0 +1,120 @@
1
+ require 'net/http'
2
+
3
+ module ThreeScale
4
+ module Backend
5
+ class EventStorage
6
+ PING_TTL = 60
7
+ EVENT_TYPES = [:first_traffic, :first_daily_traffic, :alert]
8
+
9
+ class << self
10
+ include StorageHelpers
11
+
12
+ def store(type, object)
13
+ fail InvalidEventType, type unless EVENT_TYPES.member?(type)
14
+ new_id = storage.incrby(events_id_key, 1)
15
+ event = { id: new_id, type: type, timestamp: Time.now.utc, object: object }
16
+ storage.zadd(events_queue_key, event[:id], encode(event))
17
+ end
18
+
19
+ def list
20
+ raw_events = storage.zrevrange(events_queue_key, 0, -1)
21
+ raw_events.map { |raw_event| decode_event(raw_event) }.reverse
22
+ end
23
+
24
+ def delete_range(to_id)
25
+ to_id = to_id.to_i
26
+ if to_id > 0
27
+ storage.zremrangebyscore(events_queue_key, 0, to_id)
28
+ else
29
+ 0
30
+ end
31
+ end
32
+
33
+ def delete(id)
34
+ id = id.to_i
35
+ (id > 0) ? storage.zremrangebyscore(events_queue_key, id, id) : 0
36
+ end
37
+
38
+ def size
39
+ storage.zcard(events_queue_key)
40
+ end
41
+
42
+ def ping_if_not_empty
43
+ if events_hook && pending_ping?
44
+ request_to_events_hook
45
+ true
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def events_queue_key
52
+ "events/queue".freeze
53
+ end
54
+
55
+ def events_ping_key
56
+ "events/ping".freeze
57
+ end
58
+
59
+ def events_id_key
60
+ "events/id".freeze
61
+ end
62
+
63
+ def request_to_events_hook
64
+ Net::HTTP.post_form(
65
+ events_hook_uri,
66
+ secret: events_hook_shared_secret,
67
+ )
68
+ end
69
+
70
+ def events_hook
71
+ hook = Backend.configuration.events_hook
72
+ if hook.nil? || hook.empty?
73
+ false
74
+ else
75
+ hook
76
+ end
77
+ end
78
+
79
+ def events_hook_shared_secret
80
+ Backend.configuration.events_hook_shared_secret
81
+ end
82
+
83
+ def events_hook_uri
84
+ URI(events_hook)
85
+ end
86
+
87
+ def expire_last_ping
88
+ storage.expire(events_ping_key, PING_TTL)
89
+ end
90
+
91
+ def pending_ping?
92
+ ## the queue is not empty and more than timeout has passed
93
+ ## since the front-end was notified
94
+ events_set_size, ping_key_value = storage.pipelined do
95
+ storage.zcard(events_queue_key)
96
+ storage.incr(events_ping_key)
97
+ end
98
+
99
+ return false unless ping_key_value.to_i == 1
100
+ expire_last_ping
101
+ events_set_size > 0
102
+ end
103
+
104
+ def decode_event(raw_event)
105
+ event = decode(raw_event)
106
+
107
+ # decode only symbolizes keys and parse timestamp for first level
108
+ obj = event[:object]
109
+ if obj
110
+ event[:object] = obj.symbolize_names
111
+ ts = event[:object][:timestamp]
112
+ event[:object][:timestamp] = Time.parse_to_utc(ts) if ts
113
+ end
114
+
115
+ event
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end