Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eab152e67f | ||
|
|
d3a81f896a | ||
|
|
79ba0a7877 | ||
|
|
2c32a0049e | ||
|
|
984d604625 | ||
|
|
7cf971963f | ||
|
|
2299bee182 | ||
|
|
6debfbffe9 | ||
|
|
af631bcfc6 | ||
|
|
cbe64859a5 | ||
|
|
ae11c872f7 | ||
|
|
47d0582831 | ||
|
|
cfe64272c3 | ||
|
|
d0cebae668 |
@@ -0,0 +1,22 @@
|
|||||||
|
module Concerns::AccountLimitValidation
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
LIMIT_SCHEMA = {
|
||||||
|
'type' => 'object',
|
||||||
|
'properties' => {
|
||||||
|
'inboxes' => { 'type': 'number' },
|
||||||
|
'agents' => { 'type': 'number' },
|
||||||
|
'captain_responses' => { 'type': 'number' },
|
||||||
|
'captain_documents' => { 'type': 'number' }
|
||||||
|
},
|
||||||
|
'required' => [],
|
||||||
|
'additionalProperties' => false
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
def validate_limit_keys
|
||||||
|
errors.add(:limits, ': Invalid data') unless self[:limits].is_a? Hash
|
||||||
|
self[:limits] = {} if self[:limits].blank?
|
||||||
|
|
||||||
|
errors.add(:limits, ': Invalid data') unless JSONSchemer.schema(LIMIT_SCHEMA).valid?(self[:limits])
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
module Enterprise::Account::PlanUsageAndLimits
|
module Enterprise::Account::PlanUsageAndLimits
|
||||||
|
include Concerns::AccountLimitValidation
|
||||||
|
|
||||||
CAPTAIN_RESPONSES = 'captain_responses'.freeze
|
CAPTAIN_RESPONSES = 'captain_responses'.freeze
|
||||||
CAPTAIN_DOCUMENTS = 'captain_documents'.freeze
|
CAPTAIN_DOCUMENTS = 'captain_documents'.freeze
|
||||||
CAPTAIN_RESPONSES_USAGE = 'captain_responses_usage'.freeze
|
CAPTAIN_RESPONSES_USAGE = 'captain_responses_usage'.freeze
|
||||||
@@ -16,9 +18,17 @@ module Enterprise::Account::PlanUsageAndLimits
|
|||||||
end
|
end
|
||||||
|
|
||||||
def increment_response_usage
|
def increment_response_usage
|
||||||
current_usage = custom_attributes[CAPTAIN_RESPONSES_USAGE].to_i || 0
|
increment_sql = <<~SQL.squish
|
||||||
custom_attributes[CAPTAIN_RESPONSES_USAGE] = current_usage + 1
|
custom_attributes = jsonb_set(
|
||||||
save
|
custom_attributes,
|
||||||
|
'{#{CAPTAIN_RESPONSES_USAGE}}',
|
||||||
|
to_jsonb(COALESCE((custom_attributes->>'#{CAPTAIN_RESPONSES_USAGE}')::int, 0) + 1),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
SQL
|
||||||
|
# Skipping validations is safe: no validations on custom_attributes,
|
||||||
|
updated = self.class.where(id: id).update_all(increment_sql) # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
reload if updated.positive?
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_response_usage
|
def reset_response_usage
|
||||||
@@ -108,23 +118,4 @@ module Enterprise::Account::PlanUsageAndLimits
|
|||||||
|
|
||||||
ChatwootApp.max_limit
|
ChatwootApp.max_limit
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_limit_keys
|
|
||||||
errors.add(:limits, ': Invalid data') unless self[:limits].is_a? Hash
|
|
||||||
self[:limits] = {} if self[:limits].blank?
|
|
||||||
|
|
||||||
limit_schema = {
|
|
||||||
'type' => 'object',
|
|
||||||
'properties' => {
|
|
||||||
'inboxes' => { 'type': 'number' },
|
|
||||||
'agents' => { 'type': 'number' },
|
|
||||||
'captain_responses' => { 'type': 'number' },
|
|
||||||
'captain_documents' => { 'type': 'number' }
|
|
||||||
},
|
|
||||||
'required' => [],
|
|
||||||
'additionalProperties' => false
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.add(:limits, ': Invalid data') unless JSONSchemer.schema(limit_schema).valid?(self[:limits])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ RSpec.describe Account, type: :model do
|
|||||||
## Responses
|
## Responses
|
||||||
it 'incrementing responses updates usage_limits' do
|
it 'incrementing responses updates usage_limits' do
|
||||||
account.increment_response_usage
|
account.increment_response_usage
|
||||||
|
account.reload
|
||||||
|
|
||||||
responses_limits = account.usage_limits[:captain][:responses]
|
responses_limits = account.usage_limits[:captain][:responses]
|
||||||
|
|
||||||
@@ -78,6 +79,34 @@ RSpec.describe Account, type: :model do
|
|||||||
expect(responses_limits[:current_available]).to eq captain_limits[:startups][:responses] - 1
|
expect(responses_limits[:current_available]).to eq captain_limits[:startups][:responses] - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'handles concurrent increments without losing updates' do
|
||||||
|
concurrent_calls = 50
|
||||||
|
|
||||||
|
ready = Concurrent::CountDownLatch.new(concurrent_calls)
|
||||||
|
start = Concurrent::CountDownLatch.new(1)
|
||||||
|
|
||||||
|
threads = Array.new(concurrent_calls) do
|
||||||
|
Thread.new do
|
||||||
|
thread_count = described_class.find(account.id)
|
||||||
|
ready.count_down
|
||||||
|
start.wait
|
||||||
|
|
||||||
|
thread_count.increment_response_usage
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ready.wait
|
||||||
|
|
||||||
|
start.count_down
|
||||||
|
threads.each(&:join)
|
||||||
|
|
||||||
|
account.reload
|
||||||
|
|
||||||
|
expect(account.custom_attributes['captain_responses_usage']).to eq concurrent_calls
|
||||||
|
responses_limits = account.usage_limits[:captain][:responses]
|
||||||
|
expect(responses_limits[:consumed]).to eq concurrent_calls
|
||||||
|
expect(responses_limits[:current_available]).to eq captain_limits[:startups][:responses] - concurrent_calls
|
||||||
|
end
|
||||||
|
|
||||||
it 'reseting responses limits updates usage_limits' do
|
it 'reseting responses limits updates usage_limits' do
|
||||||
account.custom_attributes['captain_responses_usage'] = 30
|
account.custom_attributes['captain_responses_usage'] = 30
|
||||||
account.save!
|
account.save!
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ describe Enterprise::Billing::HandleStripeEventService do
|
|||||||
it 'resets captain usage on billing period renewal' do
|
it 'resets captain usage on billing period renewal' do
|
||||||
# Prime the account with some usage
|
# Prime the account with some usage
|
||||||
5.times { account.increment_response_usage }
|
5.times { account.increment_response_usage }
|
||||||
|
account.reload
|
||||||
expect(account.custom_attributes['captain_responses_usage']).to eq(5)
|
expect(account.custom_attributes['captain_responses_usage']).to eq(5)
|
||||||
|
|
||||||
# Setup for any plan
|
# Setup for any plan
|
||||||
|
|||||||
Reference in New Issue
Block a user