This upgrades Chatwoot to Rails 7.2.3.1 while retaining the current Rails 7.0 framework defaults, so the runtime upgrade can be deployed and observed independently from default-behavior changes. ## What changed - Upgrade Rails and the compatible dependency set to Rails 7.2.3.1. - Keep `config.load_defaults 7.0` for a staged, lower-risk rollout. - Replace the unmaintained Azure Active Storage fork with the maintained `azure-blob` adapter while preserving the `microsoft` service name. - Pin Sidekiq 7.3.10 with `connection_pool` 2.x after validating scheduled-job execution against Redis. - Update Rails 7.2 compatibility surfaces in Active Record, strong parameters, migrations, storage, and tests. - Add read-only production preflight checks, an opt-in Active Storage smoke script, a deployment runbook, and the full Rails 7.2/8.0/8.1 assessment. ## How to test 1. Sign in and verify the dashboard and conversation UI load normally. 2. Open the agent-management modal and confirm agent data is rendered. 3. Create an API inbox and wait for the asynchronous deletion flow to complete. 4. Open Super Admin pages, including instance status and account-user management. 5. Upload and download an attachment using the configured Active Storage service. 6. Confirm recurring Sidekiq Cron jobs register and execute after startup. ## Rollout Follow `docs/rails_upgrades/7_2.md` for pre-deploy checks, deployment order, smoke tests, monitoring, and rollback. Run `bundle exec rails runner script/rails_upgrade/preflight.rb` against a production-equivalent environment before rollout. ## Tracking - [CW-5863 — Upgrade Rails to 8+](https://linear.app/chatwoot/issue/CW-5863/upgrade-rails-to-8) - [Rails 7.2 to 8.1 upgrade and production rollout plan](https://linear.app/chatwoot/document/chatwoot-rails-72-to-81-upgrade-and-production-rollout-plan-44e9f4964cb2) --------- Co-authored-by: Shivam Mishra <scm.mymail@gmail.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
142 lines
3.6 KiB
Ruby
142 lines
3.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'faker'
|
|
require 'active_support/testing/time_helpers'
|
|
|
|
class Seeders::Reports::MessageCreator
|
|
include ActiveSupport::Testing::TimeHelpers
|
|
|
|
MESSAGES_PER_CONVERSATION = 5
|
|
|
|
def initialize(account:, agents:, conversation:)
|
|
@account = account
|
|
@agents = agents
|
|
@conversation = conversation
|
|
end
|
|
|
|
def create_messages
|
|
message_count = rand(MESSAGES_PER_CONVERSATION..(MESSAGES_PER_CONVERSATION + 5))
|
|
first_agent_reply = true
|
|
|
|
message_count.times do |i|
|
|
message = create_single_message(i)
|
|
first_agent_reply = handle_reply_tracking(message, i, first_agent_reply)
|
|
end
|
|
end
|
|
|
|
def create_single_message(index)
|
|
is_incoming = index.even?
|
|
add_realistic_delay(index, is_incoming) if index.positive?
|
|
create_message(is_incoming)
|
|
end
|
|
|
|
def handle_reply_tracking(message, index, first_agent_reply)
|
|
return first_agent_reply if index.even? # Skip incoming messages
|
|
|
|
handle_agent_reply_events(message, first_agent_reply)
|
|
false # No longer first reply after any agent message
|
|
end
|
|
|
|
private
|
|
|
|
def add_realistic_delay(_message_index, is_incoming)
|
|
delay = calculate_message_delay(is_incoming)
|
|
travel(delay)
|
|
end
|
|
|
|
def calculate_message_delay(is_incoming)
|
|
if is_incoming
|
|
# Customer response time: 1 minute to 4 hours
|
|
rand((1.minute)..(4.hours))
|
|
elsif business_hours_active?(Time.current)
|
|
# Agent response time varies by business hours
|
|
rand((30.seconds)..(30.minutes))
|
|
else
|
|
rand((1.hour)..(8.hours))
|
|
end
|
|
end
|
|
|
|
def create_message(is_incoming)
|
|
if is_incoming
|
|
create_incoming_message
|
|
else
|
|
create_outgoing_message
|
|
end
|
|
end
|
|
|
|
def create_incoming_message
|
|
@conversation.messages.create!(
|
|
account: @account,
|
|
inbox: @conversation.inbox,
|
|
message_type: :incoming,
|
|
content: generate_message_content,
|
|
sender: @conversation.contact
|
|
)
|
|
end
|
|
|
|
def create_outgoing_message
|
|
sender = @conversation.assignee || @agents.sample
|
|
|
|
@conversation.messages.create!(
|
|
account: @account,
|
|
inbox: @conversation.inbox,
|
|
message_type: :outgoing,
|
|
content: generate_message_content,
|
|
sender: sender
|
|
)
|
|
end
|
|
|
|
def generate_message_content
|
|
Faker::Lorem.paragraph(sentence_count: rand(1..5))
|
|
end
|
|
|
|
def handle_agent_reply_events(message, is_first_reply)
|
|
if is_first_reply
|
|
trigger_first_reply_event(message)
|
|
else
|
|
trigger_reply_event(message)
|
|
end
|
|
end
|
|
|
|
def business_hours_active?(time)
|
|
weekday = time.wday
|
|
hour = time.hour
|
|
weekday.between?(1, 5) && hour.between?(9, 17)
|
|
end
|
|
|
|
def trigger_first_reply_event(message)
|
|
event_data = {
|
|
message: message,
|
|
conversation: message.conversation
|
|
}
|
|
|
|
ReportingEventListener.instance.first_reply_created(
|
|
Events::Base.new('first_reply_created', Time.current, event_data)
|
|
)
|
|
end
|
|
|
|
def trigger_reply_event(message)
|
|
waiting_since = calculate_waiting_since(message)
|
|
|
|
event_data = {
|
|
message: message,
|
|
conversation: message.conversation,
|
|
waiting_since: waiting_since
|
|
}
|
|
|
|
ReportingEventListener.instance.reply_created(
|
|
Events::Base.new('reply_created', Time.current, event_data)
|
|
)
|
|
end
|
|
|
|
def calculate_waiting_since(message)
|
|
last_customer_message = message.conversation.messages
|
|
.where(message_type: :incoming)
|
|
.where('created_at < ?', message.created_at)
|
|
.order(:created_at)
|
|
.last
|
|
|
|
last_customer_message&.created_at || message.conversation.created_at
|
|
end
|
|
end
|