FEATURE: add custom fields to chat (channel/message/thread) (#29504)

This allows various extensions to store extra information the 3 most popular
chat entities

Useful for certain plugins and migrations
This commit is contained in:
Sam 2024-11-01 09:12:19 +11:00 committed by GitHub
parent 29d3b9b03e
commit e7f62ab52b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 140 additions and 11 deletions

View File

@ -4,6 +4,7 @@ module Chat
class Channel < ActiveRecord::Base class Channel < ActiveRecord::Base
include Trashable include Trashable
include TypeMappable include TypeMappable
include HasCustomFields
# TODO (martin) Remove once we are using last_message instead, # TODO (martin) Remove once we are using last_message instead,
# should be around August 2023. # should be around August 2023.

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Chat
class ChannelCustomField < ActiveRecord::Base
belongs_to :channel
end
end
# == Schema Information
#
# Table name: chat_channel_custom_fields
#
# id :bigint not null, primary key
# channel_id :bigint not null
# name :string(256) not null
# value :string(1000000)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_chat_channel_custom_fields_on_channel_id_and_name (channel_id,name) UNIQUE
#

View File

@ -4,6 +4,7 @@ module Chat
class Message < ActiveRecord::Base class Message < ActiveRecord::Base
include Trashable include Trashable
include TypeMappable include TypeMappable
include HasCustomFields
self.table_name = "chat_messages" self.table_name = "chat_messages"

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Chat
class MessageCustomField < ActiveRecord::Base
belongs_to :message
end
end
# == Schema Information
#
# Table name: chat_message_custom_fields
#
# id :bigint not null, primary key
# message_id :bigint not null
# name :string(256) not null
# value :string(1000000)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_chat_message_custom_fields_on_message_id_and_name (message_id,name) UNIQUE
#

View File

@ -5,6 +5,7 @@ module Chat
MAX_TITLE_LENGTH = 100 MAX_TITLE_LENGTH = 100
include Chat::ThreadCache include Chat::ThreadCache
include HasCustomFields
self.table_name = "chat_threads" self.table_name = "chat_threads"

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Chat
class ThreadCustomField < ActiveRecord::Base
belongs_to :thread
end
end
# == Schema Information
#
# Table name: chat_thread_custom_fields
#
# id :bigint not null, primary key
# thread_id :bigint not null
# name :string(256) not null
# value :string(1000000)
# created_at :datetime not null
# updated_at :datetime not null
#
# Indexes
#
# index_chat_thread_custom_fields_on_thread_id_and_name (thread_id,name) UNIQUE
#

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
class AddCustomFieldsToChat < ActiveRecord::Migration[7.1]
def change
create_table :chat_thread_custom_fields do |t|
t.bigint :thread_id, null: false
t.string :name, limit: 256, null: false
t.string :value, limit: 1_000_000
t.timestamps null: false
end
create_table :chat_message_custom_fields do |t|
t.bigint :message_id, null: false
t.string :name, limit: 256, null: false
t.string :value, limit: 1_000_000
t.timestamps null: false
end
create_table :chat_channel_custom_fields do |t|
t.bigint :channel_id, null: false
t.string :name, limit: 256, null: false
t.string :value, limit: 1_000_000
t.timestamps null: false
end
add_index :chat_thread_custom_fields, %i[thread_id name], unique: true
add_index :chat_message_custom_fields, %i[message_id name], unique: true
add_index :chat_channel_custom_fields, %i[channel_id name], unique: true
end
end

View File

@ -11,6 +11,14 @@ RSpec.describe Chat::Channel do
it { is_expected.to validate_length_of(:chatable_type).is_at_most(100) } it { is_expected.to validate_length_of(:chatable_type).is_at_most(100) }
it { is_expected.to validate_length_of(:type).is_at_most(100) } it { is_expected.to validate_length_of(:type).is_at_most(100) }
it "supports custom fields" do
channel.custom_fields["test"] = "test"
channel.save_custom_fields
loaded_channel = Chat::Channel.find(channel.id)
expect(loaded_channel.custom_fields["test"]).to eq("test")
expect(Chat::ChannelCustomField.first.channel.id).to eq(channel.id)
end
describe ".last_message" do describe ".last_message" do
context "when there are no last message" do context "when there are no last message" do
it "returns an instance of NullMessage" do it "returns an instance of NullMessage" do

View File

@ -5,6 +5,14 @@ describe Chat::Message do
it { is_expected.to have_many(:chat_mentions).dependent(:destroy) } it { is_expected.to have_many(:chat_mentions).dependent(:destroy) }
it "supports custom fields" do
message.custom_fields["test"] = "test"
message.save_custom_fields
loaded_message = Chat::Message.find(message.id)
expect(loaded_message.custom_fields["test"]).to eq("test")
expect(Chat::MessageCustomField.first.message.id).to eq(message.id)
end
describe "validations" do describe "validations" do
subject(:message) { described_class.new(message: "") } subject(:message) { described_class.new(message: "") }
@ -509,7 +517,7 @@ describe Chat::Message do
it "destroys upload_references" do it "destroys upload_references" do
message_1 = Fabricate(:chat_message) message_1 = Fabricate(:chat_message)
upload_reference_1 = Fabricate(:upload_reference, target: message_1) upload_reference_1 = Fabricate(:upload_reference, target: message_1)
upload_1 = Fabricate(:upload) _upload_1 = Fabricate(:upload)
message_1.destroy! message_1.destroy!

View File

@ -9,18 +9,16 @@ RSpec.describe Chat::Thread do
fab!(:thread_2) { Fabricate(:chat_thread, channel: channel) } fab!(:thread_2) { Fabricate(:chat_thread, channel: channel) }
fab!(:thread_3) { Fabricate(:chat_thread, channel: channel) } fab!(:thread_3) { Fabricate(:chat_thread, channel: channel) }
before do fab!(:thread_1_message_1) { Fabricate(:chat_message, chat_channel: channel, thread: thread_1) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_1) fab!(:thread_1_message_2) { Fabricate(:chat_message, chat_channel: channel, thread: thread_1) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_1) fab!(:thread_1_message_3) { Fabricate(:chat_message, chat_channel: channel, thread: thread_1) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_1)
Fabricate(:chat_message, chat_channel: channel, thread: thread_2) fab!(:thread_2_message_1) { Fabricate(:chat_message, chat_channel: channel, thread: thread_2) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_2) fab!(:thread_2_message_2) { Fabricate(:chat_message, chat_channel: channel, thread: thread_2) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_2) fab!(:thread_2_message_3) { Fabricate(:chat_message, chat_channel: channel, thread: thread_2) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_2) fab!(:thread_2_message_4) { Fabricate(:chat_message, chat_channel: channel, thread: thread_2) }
Fabricate(:chat_message, chat_channel: channel, thread: thread_3) fab!(:thread_3_message_1) { Fabricate(:chat_message, chat_channel: channel, thread: thread_3) }
end
describe "updating replies_count for all threads" do describe "updating replies_count for all threads" do
it "counts correctly and does not include the original message" do it "counts correctly and does not include the original message" do
@ -233,4 +231,17 @@ RSpec.describe Chat::Thread do
expect(thread.latest_not_deleted_message_id).to eq(old_message.id) expect(thread.latest_not_deleted_message_id).to eq(old_message.id)
end end
end end
describe "custom fields" do
fab!(:channel) { Fabricate(:category_channel) }
fab!(:thread) { Fabricate(:chat_thread, channel: channel) }
it "allows create and save" do
thread.custom_fields["test"] = "test"
thread.save_custom_fields
loaded_thread = Chat::Thread.find(thread.id)
expect(loaded_thread.custom_fields["test"]).to eq("test")
expect(Chat::ThreadCustomField.first.thread.id).to eq(thread.id)
end
end
end end