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,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