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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +317 -0
- data/Gemfile +11 -0
- data/Gemfile.base +65 -0
- data/Gemfile.lock +319 -0
- data/Gemfile.on_prem +1 -0
- data/Gemfile.on_prem.lock +297 -0
- data/LICENSE +202 -0
- data/NOTICE +15 -0
- data/README.md +230 -0
- data/Rakefile +287 -0
- data/apisonator.gemspec +47 -0
- data/app/api/api.rb +13 -0
- data/app/api/internal/alert_limits.rb +32 -0
- data/app/api/internal/application_keys.rb +49 -0
- data/app/api/internal/application_referrer_filters.rb +43 -0
- data/app/api/internal/applications.rb +77 -0
- data/app/api/internal/errors.rb +54 -0
- data/app/api/internal/events.rb +42 -0
- data/app/api/internal/internal.rb +104 -0
- data/app/api/internal/metrics.rb +40 -0
- data/app/api/internal/service_tokens.rb +46 -0
- data/app/api/internal/services.rb +58 -0
- data/app/api/internal/stats.rb +42 -0
- data/app/api/internal/usagelimits.rb +62 -0
- data/app/api/internal/utilization.rb +23 -0
- data/bin/3scale_backend +223 -0
- data/bin/3scale_backend_worker +26 -0
- data/config.ru +4 -0
- data/config/puma.rb +192 -0
- data/config/schedule.rb +9 -0
- data/ext/mkrf_conf.rb +64 -0
- data/lib/3scale/backend.rb +67 -0
- data/lib/3scale/backend/alert_limit.rb +56 -0
- data/lib/3scale/backend/alerts.rb +137 -0
- data/lib/3scale/backend/analytics/kinesis.rb +3 -0
- data/lib/3scale/backend/analytics/kinesis/adapter.rb +180 -0
- data/lib/3scale/backend/analytics/kinesis/exporter.rb +86 -0
- data/lib/3scale/backend/analytics/kinesis/job.rb +135 -0
- data/lib/3scale/backend/analytics/redshift.rb +3 -0
- data/lib/3scale/backend/analytics/redshift/adapter.rb +367 -0
- data/lib/3scale/backend/analytics/redshift/importer.rb +83 -0
- data/lib/3scale/backend/analytics/redshift/job.rb +33 -0
- data/lib/3scale/backend/application.rb +330 -0
- data/lib/3scale/backend/application_events.rb +76 -0
- data/lib/3scale/backend/background_job.rb +65 -0
- data/lib/3scale/backend/configurable.rb +20 -0
- data/lib/3scale/backend/configuration.rb +151 -0
- data/lib/3scale/backend/configuration/loader.rb +42 -0
- data/lib/3scale/backend/constants.rb +19 -0
- data/lib/3scale/backend/cors.rb +84 -0
- data/lib/3scale/backend/distributed_lock.rb +67 -0
- data/lib/3scale/backend/environment.rb +21 -0
- data/lib/3scale/backend/error_storage.rb +52 -0
- data/lib/3scale/backend/errors.rb +343 -0
- data/lib/3scale/backend/event_storage.rb +120 -0
- data/lib/3scale/backend/experiment.rb +84 -0
- data/lib/3scale/backend/extensions.rb +5 -0
- data/lib/3scale/backend/extensions/array.rb +19 -0
- data/lib/3scale/backend/extensions/hash.rb +26 -0
- data/lib/3scale/backend/extensions/nil_class.rb +13 -0
- data/lib/3scale/backend/extensions/redis.rb +44 -0
- data/lib/3scale/backend/extensions/string.rb +13 -0
- data/lib/3scale/backend/extensions/time.rb +110 -0
- data/lib/3scale/backend/failed_jobs_scheduler.rb +141 -0
- data/lib/3scale/backend/job_fetcher.rb +122 -0
- data/lib/3scale/backend/listener.rb +728 -0
- data/lib/3scale/backend/listener_metrics.rb +99 -0
- data/lib/3scale/backend/logging.rb +48 -0
- data/lib/3scale/backend/logging/external.rb +44 -0
- data/lib/3scale/backend/logging/external/impl.rb +93 -0
- data/lib/3scale/backend/logging/external/impl/airbrake.rb +66 -0
- data/lib/3scale/backend/logging/external/impl/bugsnag.rb +69 -0
- data/lib/3scale/backend/logging/external/impl/default.rb +18 -0
- data/lib/3scale/backend/logging/external/resque.rb +57 -0
- data/lib/3scale/backend/logging/logger.rb +18 -0
- data/lib/3scale/backend/logging/middleware.rb +62 -0
- data/lib/3scale/backend/logging/middleware/json_writer.rb +21 -0
- data/lib/3scale/backend/logging/middleware/text_writer.rb +60 -0
- data/lib/3scale/backend/logging/middleware/writer.rb +143 -0
- data/lib/3scale/backend/logging/worker.rb +107 -0
- data/lib/3scale/backend/manifest.rb +80 -0
- data/lib/3scale/backend/memoizer.rb +277 -0
- data/lib/3scale/backend/metric.rb +275 -0
- data/lib/3scale/backend/metric/collection.rb +91 -0
- data/lib/3scale/backend/oauth.rb +4 -0
- data/lib/3scale/backend/oauth/token.rb +26 -0
- data/lib/3scale/backend/oauth/token_key.rb +30 -0
- data/lib/3scale/backend/oauth/token_storage.rb +313 -0
- data/lib/3scale/backend/oauth/token_value.rb +25 -0
- data/lib/3scale/backend/period.rb +3 -0
- data/lib/3scale/backend/period/boundary.rb +107 -0
- data/lib/3scale/backend/period/cache.rb +28 -0
- data/lib/3scale/backend/period/period.rb +402 -0
- data/lib/3scale/backend/queue_storage.rb +16 -0
- data/lib/3scale/backend/rack.rb +49 -0
- data/lib/3scale/backend/rack/exception_catcher.rb +136 -0
- data/lib/3scale/backend/rack/internal_error_catcher.rb +23 -0
- data/lib/3scale/backend/rack/prometheus.rb +19 -0
- data/lib/3scale/backend/saas.rb +6 -0
- data/lib/3scale/backend/saas_analytics.rb +4 -0
- data/lib/3scale/backend/server.rb +30 -0
- data/lib/3scale/backend/server/falcon.rb +52 -0
- data/lib/3scale/backend/server/puma.rb +71 -0
- data/lib/3scale/backend/service.rb +317 -0
- data/lib/3scale/backend/service_token.rb +97 -0
- data/lib/3scale/backend/stats.rb +8 -0
- data/lib/3scale/backend/stats/aggregator.rb +170 -0
- data/lib/3scale/backend/stats/aggregators/base.rb +72 -0
- data/lib/3scale/backend/stats/aggregators/response_code.rb +58 -0
- data/lib/3scale/backend/stats/aggregators/usage.rb +34 -0
- data/lib/3scale/backend/stats/bucket_reader.rb +135 -0
- data/lib/3scale/backend/stats/bucket_storage.rb +108 -0
- data/lib/3scale/backend/stats/cleaner.rb +195 -0
- data/lib/3scale/backend/stats/codes_commons.rb +14 -0
- data/lib/3scale/backend/stats/delete_job_def.rb +60 -0
- data/lib/3scale/backend/stats/key_generator.rb +73 -0
- data/lib/3scale/backend/stats/keys.rb +104 -0
- data/lib/3scale/backend/stats/partition_eraser_job.rb +58 -0
- data/lib/3scale/backend/stats/partition_generator_job.rb +46 -0
- data/lib/3scale/backend/stats/period_commons.rb +34 -0
- data/lib/3scale/backend/stats/stats_parser.rb +141 -0
- data/lib/3scale/backend/stats/storage.rb +113 -0
- data/lib/3scale/backend/statsd.rb +14 -0
- data/lib/3scale/backend/storable.rb +35 -0
- data/lib/3scale/backend/storage.rb +40 -0
- data/lib/3scale/backend/storage_async.rb +4 -0
- data/lib/3scale/backend/storage_async/async_redis.rb +21 -0
- data/lib/3scale/backend/storage_async/client.rb +205 -0
- data/lib/3scale/backend/storage_async/pipeline.rb +79 -0
- data/lib/3scale/backend/storage_async/resque_extensions.rb +30 -0
- data/lib/3scale/backend/storage_helpers.rb +278 -0
- data/lib/3scale/backend/storage_key_helpers.rb +9 -0
- data/lib/3scale/backend/storage_sync.rb +43 -0
- data/lib/3scale/backend/transaction.rb +62 -0
- data/lib/3scale/backend/transactor.rb +177 -0
- data/lib/3scale/backend/transactor/limit_headers.rb +54 -0
- data/lib/3scale/backend/transactor/notify_batcher.rb +139 -0
- data/lib/3scale/backend/transactor/notify_job.rb +47 -0
- data/lib/3scale/backend/transactor/process_job.rb +33 -0
- data/lib/3scale/backend/transactor/report_job.rb +84 -0
- data/lib/3scale/backend/transactor/status.rb +236 -0
- data/lib/3scale/backend/transactor/usage_report.rb +182 -0
- data/lib/3scale/backend/usage.rb +63 -0
- data/lib/3scale/backend/usage_limit.rb +115 -0
- data/lib/3scale/backend/use_cases/provider_key_change_use_case.rb +60 -0
- data/lib/3scale/backend/util.rb +17 -0
- data/lib/3scale/backend/validators.rb +26 -0
- data/lib/3scale/backend/validators/base.rb +36 -0
- data/lib/3scale/backend/validators/key.rb +17 -0
- data/lib/3scale/backend/validators/limits.rb +57 -0
- data/lib/3scale/backend/validators/oauth_key.rb +15 -0
- data/lib/3scale/backend/validators/oauth_setting.rb +15 -0
- data/lib/3scale/backend/validators/redirect_uri.rb +33 -0
- data/lib/3scale/backend/validators/referrer.rb +60 -0
- data/lib/3scale/backend/validators/service_state.rb +15 -0
- data/lib/3scale/backend/validators/state.rb +15 -0
- data/lib/3scale/backend/version.rb +5 -0
- data/lib/3scale/backend/views/oauth_access_tokens.builder +14 -0
- data/lib/3scale/backend/views/oauth_app_id_by_token.builder +4 -0
- data/lib/3scale/backend/worker.rb +87 -0
- data/lib/3scale/backend/worker_async.rb +88 -0
- data/lib/3scale/backend/worker_metrics.rb +44 -0
- data/lib/3scale/backend/worker_sync.rb +32 -0
- data/lib/3scale/bundler_shim.rb +17 -0
- data/lib/3scale/prometheus_server.rb +10 -0
- data/lib/3scale/tasks/connectivity.rake +41 -0
- data/lib/3scale/tasks/helpers.rb +3 -0
- data/lib/3scale/tasks/helpers/environment.rb +23 -0
- data/lib/3scale/tasks/stats.rake +131 -0
- data/lib/3scale/tasks/swagger.rake +46 -0
- data/licenses.xml +1215 -0
- 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
|