apisonator 2.100.0

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 (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,83 @@
1
+ module ThreeScale
2
+ module Backend
3
+ module Analytics
4
+ module Redshift
5
+
6
+ # The main responsibility of this class is to schedule jobs that import
7
+ # events that are stored in S3 into Redshift.
8
+ # We know that the distributed locking algorithm that we are using
9
+ # guarantees that two jobs will not be running at the same time except
10
+ # in some corner cases, like in the case of a failure of one of the Redis
11
+ # masters. However, this is not a problem in our case. If two Redshift
12
+ # jobs run at the same time, they will try to import the same S3 paths
13
+ # from Redshift. This is not a problem because the import method that
14
+ # we use ensures that we do not import duplicates into Redshift.
15
+ # Check the Redshift::Adapter class for more details on this.
16
+ class Importer
17
+ TTL_JOB_RUNNING_KEY_SEC = 60*60
18
+ private_constant :TTL_JOB_RUNNING_KEY_SEC
19
+
20
+ REDSHIFT_ENABLED_KEY = 'redshift:enabled'.freeze
21
+ private_constant :REDSHIFT_ENABLED_KEY
22
+
23
+ class << self
24
+ def schedule_job
25
+ if enabled? && Backend.production?
26
+ lock_key = dist_lock.lock
27
+ if lock_key
28
+ Resque.enqueue(Job, lock_key, Time.now.utc.to_f)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Returns a UTC time that represents the hour when the newest events
34
+ # imported in Redshift were generated or nil if nothing has been
35
+ # imported.
36
+ def latest_imported_events_time
37
+ latest_timestamp = db_adapter.latest_timestamp_read
38
+ return nil if latest_timestamp.nil?
39
+ DateTime.parse(latest_timestamp).to_time.utc
40
+ end
41
+
42
+ def consistent_data?
43
+ db_adapter.consistent_data?
44
+ end
45
+
46
+ def enable
47
+ storage.set(REDSHIFT_ENABLED_KEY, '1')
48
+ end
49
+
50
+ def disable
51
+ storage.del(REDSHIFT_ENABLED_KEY)
52
+ end
53
+
54
+ def enabled?
55
+ storage.get(REDSHIFT_ENABLED_KEY).to_i == 1
56
+ end
57
+
58
+ # To be called by from a Redshift job once it exits so other jobs can run
59
+ def job_finished(lock_key)
60
+ dist_lock.unlock if lock_key == dist_lock.current_lock_key
61
+ end
62
+
63
+ private
64
+
65
+ def storage
66
+ Backend::Storage.instance
67
+ end
68
+
69
+ def dist_lock
70
+ @dist_lock ||= DistributedLock.new(self.name,
71
+ TTL_JOB_RUNNING_KEY_SEC,
72
+ storage)
73
+ end
74
+
75
+ def db_adapter
76
+ Adapter
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,33 @@
1
+ module ThreeScale
2
+ module Backend
3
+ module Analytics
4
+ module Redshift
5
+ class Job < BackgroundJob
6
+ @queue = :stats
7
+
8
+ class << self
9
+ def perform_logged(lock_key, _enqueue_time)
10
+ begin
11
+ latest_time_inserted = Adapter.insert_pending_events
12
+ ok = true
13
+ msg = job_ok_msg(latest_time_inserted)
14
+ rescue Exception => e
15
+ ok = false
16
+ msg = e.message
17
+ end
18
+
19
+ Importer.job_finished(lock_key)
20
+ [ok, msg]
21
+ end
22
+
23
+ private
24
+
25
+ def job_ok_msg(time_utc)
26
+ "Events imported correctly. Latest ones are from: #{time_utc.to_s}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,330 @@
1
+ module ThreeScale
2
+ module Backend
3
+ class Application
4
+ include Storable
5
+
6
+ # list of attributes to be fetched from storage
7
+ ATTRIBUTES = [:state, :plan_id, :plan_name, :redirect_url,
8
+ :user_required].freeze
9
+ private_constant :ATTRIBUTES
10
+
11
+ attr_accessor :service_id, :id, *ATTRIBUTES
12
+ attr_writer :metric_names
13
+
14
+ def to_hash
15
+ {
16
+ service_id: service_id,
17
+ id: id,
18
+ state: state,
19
+ plan_id: plan_id,
20
+ plan_name: plan_name,
21
+ redirect_url: redirect_url,
22
+ user_required: user_required
23
+ }
24
+ end
25
+
26
+ def update(attributes)
27
+ attributes.each do |attr, val|
28
+ public_send("#{attr}=", val)
29
+ end
30
+ self
31
+ end
32
+
33
+ class << self
34
+ include Memoizer::Decorator
35
+
36
+ def attribute_names
37
+ (ATTRIBUTES + %i[service_id id metric_names].freeze).freeze
38
+ end
39
+
40
+ def load(service_id, id)
41
+ return nil unless service_id and id
42
+ values = storage.mget(storage_key(service_id, id, :state),
43
+ storage_key(service_id, id, :plan_id),
44
+ storage_key(service_id, id, :plan_name),
45
+ storage_key(service_id, id, :redirect_url),
46
+ storage_key(service_id, id, :user_required))
47
+ state, plan_id, plan_name, redirect_url, user_required = values
48
+
49
+ # save a network call by just checking state here for existence
50
+ return nil unless state
51
+
52
+ ## the default value is false
53
+ user_required = user_required.to_i > 0
54
+
55
+ new(service_id: service_id,
56
+ id: id,
57
+ state: state.to_sym,
58
+ plan_id: plan_id,
59
+ plan_name: plan_name,
60
+ user_required: user_required,
61
+ redirect_url: redirect_url)
62
+ end
63
+ memoize :load
64
+
65
+ def load!(service_id, app_id)
66
+ load(service_id, app_id) or raise ApplicationNotFound, app_id
67
+ end
68
+ memoize :load!
69
+
70
+ def load_id_by_key(service_id, key)
71
+ storage.get(id_by_key_storage_key(service_id, key))
72
+ end
73
+ memoize :load_id_by_key
74
+
75
+ def save_id_by_key(service_id, key, id)
76
+ raise ApplicationHasInconsistentData.new(id, key) if [service_id, id, key].any?(&:blank?)
77
+ storage.set(id_by_key_storage_key(service_id, key), id).tap do
78
+ Memoizer.memoize(Memoizer.build_key(self, :load_id_by_key, service_id, key), id)
79
+ end
80
+ end
81
+
82
+ def delete_id_by_key(service_id, key)
83
+ storage.del(id_by_key_storage_key(service_id, key)).tap do
84
+ Memoizer.clear(Memoizer.build_key(self, :load_id_by_key, service_id, key))
85
+ end
86
+ end
87
+
88
+ def load_by_id_or_user_key!(service_id, app_id, user_key)
89
+ with_app_id_from_params service_id, app_id, user_key do |appid|
90
+ load service_id, appid
91
+ end
92
+ end
93
+
94
+ def extract_id!(service_id, app_id, user_key, access_token)
95
+ with_app_id_from_params service_id, app_id, user_key, access_token do |appid|
96
+ exists? service_id, appid and appid
97
+ end
98
+ end
99
+
100
+ def exists?(service_id, id)
101
+ storage.exists(storage_key(service_id, id, :state))
102
+ end
103
+ memoize :exists?
104
+
105
+ def delete(service_id, id)
106
+ raise ApplicationNotFound, id unless exists?(service_id, id)
107
+ delete_data service_id, id
108
+ clear_cache service_id, id
109
+ OAuth::Token::Storage.remove_tokens(service_id, id)
110
+ end
111
+
112
+ def delete_data(service_id, id)
113
+ storage.pipelined do
114
+ delete_set(service_id, id)
115
+ delete_attributes(service_id, id)
116
+ end
117
+ end
118
+
119
+ def clear_cache(service_id, id)
120
+ params = [service_id, id]
121
+ keys = Memoizer.build_keys_for_class(self,
122
+ load: params,
123
+ load!: params,
124
+ exists?: params)
125
+ Memoizer.clear keys
126
+ end
127
+
128
+ def applications_set_key(service_id)
129
+ encode_key("service_id:#{service_id}/applications")
130
+ end
131
+
132
+ def save(attributes)
133
+ application = new(attributes)
134
+ application.save
135
+ application
136
+ end
137
+
138
+ def storage_key(service_id, id, attribute)
139
+ encode_key("application/service_id:#{service_id}/id:#{id}/#{attribute}")
140
+ end
141
+
142
+ private
143
+
144
+ def id_by_key_storage_key(service_id, key)
145
+ encode_key("application/service_id:#{service_id}/key:#{key}/id")
146
+ end
147
+
148
+ def delete_set(service_id, id)
149
+ storage.srem(applications_set_key(service_id), id)
150
+ end
151
+
152
+ def delete_attributes(service_id, id)
153
+ storage.del(
154
+ ATTRIBUTES.map do |f|
155
+ storage_key(service_id, id, f)
156
+ end
157
+ )
158
+ end
159
+
160
+ def with_app_id_from_params(service_id, app_id, user_key, access_token = nil)
161
+ if app_id
162
+ raise AuthenticationError unless user_key.nil?
163
+ elsif user_key
164
+ app_id = load_id_by_key(service_id, user_key)
165
+ raise UserKeyInvalid, user_key if app_id.nil?
166
+ elsif access_token
167
+ app_id, * = OAuth::Token::Storage.get_credentials access_token, service_id
168
+ else
169
+ raise ApplicationNotFound
170
+ end
171
+
172
+ yield app_id or raise ApplicationNotFound, app_id
173
+ end
174
+ end
175
+
176
+ def user_required?
177
+ @user_required
178
+ end
179
+
180
+ def save
181
+ raise ApplicationHasNoState.new(id) if !state
182
+
183
+ storage.pipelined do
184
+ persist_attributes
185
+ persist_set
186
+ end
187
+
188
+ self.class.clear_cache(service_id, id)
189
+
190
+ Memoizer.memoize(Memoizer.build_key(self.class, :exists?, service_id, id), state)
191
+ end
192
+
193
+ def invalidate_cache(cmds, cache_key)
194
+ # cache key cannot be just cache_key.
195
+ # Command must be added to avoid collisions between different ops over same key
196
+ cmds.each { |cmd| Memoizer.clear(Memoizer.build_key(self.class, cmd, cache_key)) }
197
+ end
198
+
199
+ def storage_key(attribute)
200
+ self.class.storage_key(service_id, id, attribute)
201
+ end
202
+
203
+ def applications_set_key(service_id)
204
+ self.class.applications_set_key(service_id)
205
+ end
206
+
207
+ def metric_names
208
+ @metric_names ||= {}
209
+ end
210
+
211
+ def metric_name(metric_id)
212
+ metric_names[metric_id] ||= Metric.load_name(service_id, metric_id)
213
+ end
214
+
215
+ # Sets @metric_names with the names of all the metrics for which there is
216
+ # a usage limit that applies to the app, and returns it.
217
+ def load_metric_names
218
+ metric_ids = usage_limits.map(&:metric_id)
219
+ @metric_names = Metric.load_all_names(service_id, metric_ids)
220
+ end
221
+
222
+ def usage_limits
223
+ @usage_limits ||= UsageLimit.load_all(service_id, plan_id)
224
+ end
225
+
226
+ def active?
227
+ state == :active
228
+ end
229
+
230
+ #
231
+ # KEYS
232
+ #
233
+
234
+ def keys
235
+ # We memoize with self.class to avoid caching the result for specific
236
+ # instances as opposed to the combination of service_id and app_id.
237
+ db_key = storage_key(:keys)
238
+ key = Memoizer.build_key(self.class, :smembers, db_key)
239
+ Memoizer.memoize_block(key) do
240
+ storage.smembers(db_key)
241
+ end
242
+ end
243
+
244
+ # Create new application key and add it to the list of keys of this app.
245
+ # If value is nil, generates new random key, otherwise uses the given
246
+ # value as the new key.
247
+ def create_key(value = nil)
248
+ db_key = storage_key(:keys)
249
+ invalidate_cache([:smembers, :scard], db_key)
250
+ value ||= SecureRandom.hex(16)
251
+ storage.sadd(db_key, value)
252
+ value
253
+ end
254
+
255
+ def delete_key(value)
256
+ db_key = storage_key(:keys)
257
+ invalidate_cache([:smembers, :scard, :sismember], db_key)
258
+ storage.srem(db_key, value)
259
+ end
260
+
261
+ def has_keys?
262
+ db_key = storage_key(:keys)
263
+ key = Memoizer.build_key(self.class, :scard, db_key)
264
+ Memoizer.memoize_block(key) do
265
+ storage.scard(db_key).to_i > 0
266
+ end
267
+ end
268
+
269
+ def has_no_keys?
270
+ !has_keys?
271
+ end
272
+
273
+ def has_key?(value)
274
+ db_key = storage_key(:keys)
275
+ key = Memoizer.build_key(self.class, :sismember, db_key, value)
276
+ Memoizer.memoize_block(key) do
277
+ storage.sismember(db_key, value)
278
+ end
279
+ end
280
+
281
+ #
282
+ # REFERRER FILTER
283
+ #
284
+
285
+ def referrer_filters
286
+ db_key = storage_key(:referrer_filters)
287
+ key = Memoizer.build_key(self.class, :smembers, db_key)
288
+ Memoizer.memoize_block(key) do
289
+ storage.smembers(db_key)
290
+ end
291
+ end
292
+
293
+ def create_referrer_filter(value)
294
+ raise ReferrerFilterInvalid, "referrer filter can't be blank" if value.blank?
295
+ db_key = storage_key(:referrer_filters)
296
+ invalidate_cache([:smembers, :scard], db_key)
297
+ storage.sadd(db_key, value)
298
+ value
299
+ end
300
+
301
+ def delete_referrer_filter(value)
302
+ db_key = storage_key(:referrer_filters)
303
+ invalidate_cache([:smembers, :scard], db_key)
304
+ storage.srem(db_key, value)
305
+ end
306
+
307
+ def has_referrer_filters?
308
+ db_key = storage_key(:referrer_filters)
309
+ key = Memoizer.build_key(self.class, :scard, db_key)
310
+ Memoizer.memoize_block(key) do
311
+ storage.scard(db_key).to_i > 0
312
+ end
313
+ end
314
+
315
+ private
316
+
317
+ def persist_attributes
318
+ storage.set(storage_key(:state), state.to_s) if state
319
+ storage.set(storage_key(:plan_id), plan_id) if plan_id
320
+ storage.set(storage_key(:plan_name), plan_name) if plan_name
321
+ storage.set(storage_key(:user_required), user_required? ? 1 : 0)
322
+ storage.set(storage_key(:redirect_url), redirect_url) if redirect_url
323
+ end
324
+
325
+ def persist_set
326
+ storage.sadd(applications_set_key(service_id), id)
327
+ end
328
+ end
329
+ end
330
+ end
@@ -0,0 +1,76 @@
1
+ require '3scale/backend/storage'
2
+ require '3scale/backend/event_storage'
3
+ require '3scale/backend/stats/keys'
4
+
5
+ module ThreeScale
6
+ module Backend
7
+ class ApplicationEvents
8
+
9
+ class << self
10
+ include Backend::StorageKeyHelpers
11
+ end
12
+
13
+ DAILY_KEY_TTL = 172_800
14
+
15
+ Error = Class.new StandardError
16
+ class PingFailed < Error
17
+ def initialize(e)
18
+ super "Application event ping failed: #{e.class} - #{e.message}"
19
+ end
20
+ end
21
+
22
+ # ping the frontend if any event is pending for processing
23
+ def self.ping
24
+ EventStorage.ping_if_not_empty
25
+ rescue => e
26
+ raise PingFailed.new(e)
27
+ end
28
+
29
+ def self.generate(applications)
30
+ return if applications.nil? || applications.empty?
31
+ applications.each do |application|
32
+ service_id = application[:service_id]
33
+ application_id = application[:application_id]
34
+
35
+ first_traffic(service_id, application_id)
36
+ first_daily_traffic(service_id, application_id)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def self.first_traffic(service_id, application_id)
43
+ key = Stats::Keys.applications_key_prefix(
44
+ Stats::Keys.service_key_prefix(service_id)
45
+ )
46
+ if storage.sadd(key, encode_key(application_id))
47
+ EventStorage.store(:first_traffic,
48
+ { service_id: service_id,
49
+ application_id: application_id,
50
+ timestamp: Time.now.utc.to_s })
51
+ end
52
+ end
53
+
54
+ def self.first_daily_traffic(service_id, application_id)
55
+ timestamp = Time.now.utc
56
+ day_key = Period::Boundary.day_start(timestamp).to_compact_s
57
+ daily_key = "daily_traffic/service:#{service_id}/" \
58
+ "cinstance:#{application_id}/#{day_key}"
59
+
60
+ Memoizer.memoize_block(daily_key) do
61
+ if storage.incr(daily_key) == 1
62
+ storage.expire(daily_key, DAILY_KEY_TTL)
63
+ EventStorage.store(:first_daily_traffic,
64
+ { service_id: service_id,
65
+ application_id: application_id,
66
+ timestamp: timestamp.to_s })
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.storage
72
+ Storage.instance
73
+ end
74
+ end
75
+ end
76
+ end