-
Notifications
You must be signed in to change notification settings - Fork 26
Feature/delete service stats endpoint #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bd1de7b
22abc6f
dfadb8c
5731a55
c07f12b
bab866b
e841458
9516ec3
165a89d
8d6bdc9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| module ThreeScale | ||
| module Backend | ||
| module API | ||
| internal_api '/services/:service_id/stats' do | ||
| before do | ||
| respond_with_404('service not found') unless Service.exists?(params[:service_id]) | ||
| end | ||
|
|
||
| delete '' do |service_id| | ||
| delete_stats_job_attrs = api_params Stats::DeleteJobDef | ||
| delete_stats_job_attrs[:service_id] = service_id | ||
| delete_stats_job_attrs[:from] = delete_stats_job_attrs[:from].to_i | ||
| delete_stats_job_attrs[:to] = delete_stats_job_attrs[:to].to_i | ||
| begin | ||
| Stats::DeleteJobDef.new(delete_stats_job_attrs).run_async | ||
|
eguzki marked this conversation as resolved.
|
||
| rescue DeleteServiceStatsValidationError => e | ||
| [400, headers, { status: :error, error: e.message }.to_json] | ||
| else | ||
| { status: :to_be_deleted }.to_json | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| require '3scale/backend/stats/aggregator' | ||
| require '3scale/backend/stats/partition_generator_job' | ||
| require '3scale/backend/stats/delete_job_def' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| module ThreeScale | ||
| module Backend | ||
| module Stats | ||
| class DeleteJobDef | ||
|
eguzki marked this conversation as resolved.
|
||
| ATTRIBUTES = %i[service_id applications metrics users from to context_info].freeze | ||
| private_constant :ATTRIBUTES | ||
| attr_reader(*ATTRIBUTES) | ||
|
|
||
| def self.attribute_names | ||
| ATTRIBUTES | ||
| end | ||
|
|
||
| def initialize(params = {}) | ||
| ATTRIBUTES.each do |key| | ||
| instance_variable_set("@#{key}".to_sym, params[key]) unless params[key].nil? | ||
| end | ||
| validate | ||
| end | ||
|
|
||
| def run_async | ||
| Resque.enqueue(PartitionGeneratorJob, Time.now.getutc.to_f, service_id, applications, | ||
| metrics, users, from, to, context_info) | ||
| end | ||
|
|
||
| def to_json | ||
| to_hash.to_json | ||
| end | ||
|
|
||
| def to_hash | ||
| Hash[ATTRIBUTES.collect { |key| [key, send(key)] }] | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def validate | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot to mention that we should probably put some kind of limit on the |
||
| # from and to valid epoch times | ||
| raise_validation_error('from field not integer') unless from.is_a? Integer | ||
| raise_validation_error('from field is zero') if from.zero? | ||
| raise_validation_error('to field not integer') unless to.is_a? Integer | ||
| raise_validation_error('to field is zero') if to.zero? | ||
| raise_validation_error('from < to fields') if Time.at(to) < Time.at(from) | ||
| # application is array | ||
| raise_validation_error('applications field') unless applications.is_a? Array | ||
| raise_validation_error('applications values') unless applications.all? do |x| | ||
| x.is_a?(String) || x.is_a?(Integer) | ||
| end | ||
| # metrics is array | ||
| raise_validation_error('metrics field') unless metrics.is_a? Array | ||
| raise_validation_error('metrics values') unless metrics.all? do |x| | ||
| x.is_a?(String) || x.is_a?(Integer) | ||
| end | ||
| # users is array | ||
| raise_validation_error('users field') unless users.is_a? Array | ||
| raise_validation_error('users values') unless users.all? do |x| | ||
| x.is_a?(String) || x.is_a?(Integer) | ||
| end | ||
| end | ||
|
|
||
| def raise_validation_error(msg) | ||
| raise DeleteServiceStatsValidationError.new(service_id, msg) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| module ThreeScale | ||
| module Backend | ||
| module Stats | ||
| class PartitionGeneratorJob < BackgroundJob | ||
| # low priority queue | ||
| @queue = :main | ||
|
|
||
| class << self | ||
| def perform_logged(_enqueue_time, service_id, applications, metrics, users, from, to, context_info = {}) end | ||
|
|
||
| private | ||
|
|
||
| def enqueue_time | ||
| @args[0] | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| require_relative '../../../spec_helpers/acceptance_spec_helper' | ||
|
|
||
| resource 'Stats (prefix: /services/:service_id/stats)' do | ||
| header 'Accept', 'application/json' | ||
| header 'Content-Type', 'application/json' | ||
|
|
||
| let(:existing_service_id) { '10000' } | ||
| let(:service_id) { existing_service_id } | ||
| let(:provider_key) { 'statsfoo' } | ||
| let(:applications) { %w[1 2 3] } | ||
| let(:metrics) { %w[10 20 30] } | ||
| let(:users) { %w[100 200 300] } | ||
| let(:from) { Time.new(2002, 10, 31).to_i } | ||
| let(:to) { Time.new(2003, 10, 31).to_i } | ||
| let(:req_body) do | ||
| { | ||
| deletejobdef: { | ||
| applications: applications, | ||
| metrics: metrics, | ||
| users: users, | ||
| from: from, | ||
| to: to | ||
| } | ||
| } | ||
| end | ||
| # From and To fields are sent as string, even though they are integers in req_body | ||
| let(:raw_post) { req_body } | ||
|
|
||
| before do | ||
| ThreeScale::Backend::Service.save!(provider_key: provider_key, id: existing_service_id) | ||
| end | ||
|
|
||
| delete '/services/:service_id/stats' do | ||
| parameter :service_id, 'Service ID', required: true | ||
|
|
||
| context 'PartitionGeneratorJob is enqueued' do | ||
| before do | ||
| ResqueSpec.reset! | ||
| end | ||
|
|
||
| example_request 'Deleting stats' do | ||
| expect(status).to eq 200 | ||
| expect(response_json['status']).to eq 'to_be_deleted' | ||
| expect(ThreeScale::Backend::Stats::PartitionGeneratorJob).to have_queued(anything, | ||
| existing_service_id, | ||
| applications, | ||
| metrics, users, | ||
| from, to, nil) | ||
| end | ||
| end | ||
|
|
||
| context 'service does not exist' do | ||
| let(:service_id) { existing_service_id + 'foo' } | ||
|
|
||
| example_request 'Deleting stats' do | ||
| expect(status).to eq 404 | ||
| end | ||
| end | ||
|
|
||
| context 'invalid param sent' do | ||
| let(:from) { 'adfsadfasd' } | ||
|
|
||
| example_request 'Deleting stats' do | ||
| expect(status).to eq 400 | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| require_relative '../../spec_helper' | ||
|
|
||
| RSpec.shared_examples 'job hash is correct' do | ||
| it 'has service_id' do | ||
| expect(job).to include(service_id: service_id) | ||
| end | ||
|
|
||
| it 'has applications' do | ||
| expect(job).to include(applications: applications) | ||
| end | ||
|
|
||
| it 'has metrics' do | ||
| expect(job).to include(metrics: metrics) | ||
| end | ||
|
|
||
| it 'has users' do | ||
| expect(job).to include(users: users) | ||
| end | ||
|
|
||
| it 'has from' do | ||
| expect(job).to include(from: from) | ||
| end | ||
|
|
||
| it 'has to' do | ||
| expect(job).to include(to: to) | ||
| end | ||
| end | ||
|
|
||
| RSpec.shared_examples 'validation error' do | ||
| it 'raise validation error' do | ||
| expect { subject }.to raise_error(ThreeScale::Backend::DeleteServiceStatsValidationError) | ||
| end | ||
| end | ||
|
|
||
| RSpec.describe ThreeScale::Backend::Stats::DeleteJobDef do | ||
| let(:service_id) { 'some_service_id' } | ||
| let(:applications) { %w[1 2 3] } | ||
| let(:metrics) { %w[10 20 30] } | ||
| let(:users) { %w[100 200 300] } | ||
| let(:from) { Time.new(2002, 10, 31).to_i } | ||
| let(:to) { Time.new(2003, 10, 31).to_i } | ||
| let(:params) do | ||
| { | ||
| service_id: service_id, | ||
| applications: applications, | ||
| metrics: metrics, | ||
| users: users, | ||
| from: from, | ||
| to: to | ||
| } | ||
| end | ||
| subject { described_class.new params } | ||
|
|
||
| context '#initialize' do | ||
| context 'happy path' do | ||
| it 'does not raise' do should_not be_nil end | ||
| end | ||
|
|
||
| context 'from field is nil' do | ||
| let(:from) { nil } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'from field is string' do | ||
| let(:from) { '12345' } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'from field is zero' do | ||
| let(:from) { 0 } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'to field is nil' do | ||
| let(:to) { nil } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'to field is string' do | ||
| let(:to) { '12345' } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'to field is zero' do | ||
| let(:to) { 0 } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'to field happens before from field' do | ||
| let(:from) { Time.new(2005, 10, 31).to_i } | ||
| let(:to) { Time.new(2003, 10, 31).to_i } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'applicatoins field is nil' do | ||
| let(:applications) { nil } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'applicatoins field is not array' do | ||
| let(:applications) { 3 } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'applicatoins field constains bad element' do | ||
| let(:applications) { ['3', {}, '4'] } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'metrics field is nil' do | ||
| let(:metrics) { nil } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'metrics field is not array' do | ||
| let(:metrics) { 3 } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'metrics field constains element bad string' do | ||
| let(:metrics) { ['3', [], '4'] } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'users field is nil' do | ||
| let(:users) { nil } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'users field is not array' do | ||
| let(:users) { 3 } | ||
| include_examples 'validation error' | ||
| end | ||
|
|
||
| context 'users field constains element bad string' do | ||
| let(:users) { ['3', [], '4'] } | ||
| include_examples 'validation error' | ||
| end | ||
| end | ||
|
|
||
| context '#run_async' do | ||
| before do | ||
| ResqueSpec.reset! | ||
| end | ||
|
|
||
| it 'partition generator job is queued' do | ||
| subject.run_async | ||
| expect(ThreeScale::Backend::Stats::PartitionGeneratorJob).to have_queued(anything, | ||
| service_id, | ||
| applications, | ||
| metrics, users, | ||
| from, to, nil) | ||
| end | ||
| end | ||
|
|
||
| context '#to_json' do | ||
| let(:job) { JSON.parse(subject.to_json, symbolize_names: true) } | ||
| include_examples 'job hash is correct' | ||
| end | ||
|
|
||
| context '#to_hash' do | ||
| let(:job) { subject.to_hash } | ||
| include_examples 'job hash is correct' | ||
| end | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.