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