diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 1a2c75fc..d5c36618 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -6,11 +6,32 @@ class ApplicationMailer < ActionMailer::Base register_interceptor ActionMailerConfigsInterceptor + register_observer ActionMailerEventsObserver + before_action do ActiveStorage::Current.url_options = Docuseal.default_url_options end + after_action :set_message_metadata + after_action :set_message_uuid + def default_url_options Docuseal.default_url_options end + + def set_message_metadata + message.instance_variable_set(:@message_metadata, @message_metadata) + end + + def set_message_uuid + message['X-Message-Uuid'] = SecureRandom.uuid + end + + def assign_message_metadata(tag, record) + @message_metadata = { + 'tag' => tag, + 'record_id' => record.id, + 'record_type' => record.class.name + } + end end diff --git a/app/mailers/submitter_mailer.rb b/app/mailers/submitter_mailer.rb index 868242fc..50cadcb1 100644 --- a/app/mailers/submitter_mailer.rb +++ b/app/mailers/submitter_mailer.rb @@ -26,6 +26,8 @@ class SubmitterMailer < ApplicationMailer DEFAULT_INVITATION_SUBJECT end + assign_message_metadata('submitter_invitation', @submitter) + mail( to: @submitter.friendly_name, from: from_address_for_submitter(submitter), @@ -58,6 +60,8 @@ class SubmitterMailer < ApplicationMailer %(#{submitter.submission.template.name} has been completed by #{submitters}) end + assign_message_metadata('submitter_completed', @submitter) + mail(from: from_address_for_submitter(submitter), to: to || (user.role == 'integration' ? user.friendly_name.sub(/\+\w+@/, '@') : user.friendly_name), subject:) @@ -89,6 +93,8 @@ class SubmitterMailer < ApplicationMailer 'Your document copy' end + assign_message_metadata('submitter_documents_copy', @submitter) + mail(from: from_address_for_submitter(submitter), to: to || @submitter.friendly_name, reply_to: @submitter.preferences['reply_to'].presence || diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 45e21e94..ff1df049 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -6,6 +6,8 @@ class UserMailer < ApplicationMailer @user = user @token = @user.send(:set_reset_password_token) + assign_message_metadata('user_invitation', @user) + mail(to: @user.friendly_name, subject: 'You are invited to DocuSeal') end diff --git a/app/models/account.rb b/app/models/account.rb index 929a37c6..6e8d1718 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -31,6 +31,7 @@ class Account < ApplicationRecord has_many :submissions, dependent: :destroy has_many :submitters, through: :submissions has_many :account_linked_accounts, dependent: :destroy + has_many :email_events, dependent: :destroy has_many :account_testing_accounts, -> { testing }, dependent: :destroy, class_name: 'AccountLinkedAccount', inverse_of: :account diff --git a/app/models/email_event.rb b/app/models/email_event.rb new file mode 100644 index 00000000..5b8b3862 --- /dev/null +++ b/app/models/email_event.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: email_events +# +# id :bigint not null, primary key +# data :text not null +# email :string not null +# emailable_type :string not null +# event_datetime :datetime not null +# event_type :string not null +# tag :string not null +# created_at :datetime not null +# account_id :bigint not null +# emailable_id :bigint not null +# message_id :string not null +# +# Indexes +# +# index_email_events_on_account_id (account_id) +# index_email_events_on_emailable (emailable_type,emailable_id) +# index_email_events_on_message_id (message_id) +# +# Foreign Keys +# +# fk_rails_... (account_id => accounts.id) +# +class EmailEvent < ApplicationRecord + belongs_to :emailable, polymorphic: true + belongs_to :account + + attribute :data, :string, default: -> { {} } + + serialize :data, coder: JSON + + before_validation :maybe_set_account, on: :create + + def maybe_set_account + self.account ||= emailable.account + end +end diff --git a/db/migrate/20240604070648_create_email_events.rb b/db/migrate/20240604070648_create_email_events.rb new file mode 100644 index 00000000..4031ce8e --- /dev/null +++ b/db/migrate/20240604070648_create_email_events.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class CreateEmailEvents < ActiveRecord::Migration[7.1] + def change + create_table :email_events do |t| + t.references :account, null: false, foreign_key: true, index: true + t.references :emailable, polymorphic: true, index: true, null: false + t.string :message_id, null: false, index: true + t.string :tag, null: false + t.string :event_type, null: false + t.string :email, null: false + t.text :data, null: false + t.datetime :event_datetime, null: false + t.datetime :created_at, null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 5ee025a9..86642211 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_04_28_112400) do +ActiveRecord::Schema[7.1].define(version: 2024_06_04_070648) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -98,6 +98,22 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_28_112400) do t.index ["submitter_id"], name: "index_document_generation_events_on_submitter_id" end + create_table "email_events", force: :cascade do |t| + t.bigint "account_id", null: false + t.string "emailable_type", null: false + t.bigint "emailable_id", null: false + t.string "message_id", null: false + t.string "tag", null: false + t.string "event_type", null: false + t.string "email", null: false + t.text "data", null: false + t.datetime "event_datetime", null: false + t.datetime "created_at", null: false + t.index ["account_id"], name: "index_email_events_on_account_id" + t.index ["emailable_type", "emailable_id"], name: "index_email_events_on_emailable" + t.index ["message_id"], name: "index_email_events_on_message_id" + end + create_table "email_messages", force: :cascade do |t| t.string "uuid", null: false t.bigint "author_id", null: false @@ -279,6 +295,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_28_112400) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "document_generation_events", "submitters" + add_foreign_key "email_events", "accounts" add_foreign_key "email_messages", "accounts" add_foreign_key "email_messages", "users", column: "author_id" add_foreign_key "encrypted_configs", "accounts" diff --git a/lib/action_mailer_events_observer.rb b/lib/action_mailer_events_observer.rb new file mode 100644 index 00000000..c7786bda --- /dev/null +++ b/lib/action_mailer_events_observer.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module ActionMailerEventsObserver + module_function + + def delivered_email(mail) + data = mail.instance_variable_get(:@message_metadata) + + return if data.blank? + + tag, emailable_id, emailable_type = data.values_at('tag', 'record_id', 'record_type') + + return if tag.blank? || emailable_type.blank? || emailable_id.blank? + + message_id = fetch_message_id(mail) + + all_emails(mail).each do |email| + EmailEvent.create!( + tag:, + message_id:, + emailable_id:, + emailable_type:, + event_type: :send, + email:, + data: { method: mail.delivery_method.class.name.underscore }, + event_datetime: Time.current + ) + end + rescue StandardError => e + Rollbar.error(e) if defined?(Rollbar) + + raise if Rails.env.local? + end + + def fetch_message_id(mail) + mail['X-Message-Uuid']&.value || SecureRandom.uuid + end + + def all_emails(mail) + mail.to.to_a + mail.cc.to_a + mail.bcc.to_a + end +end diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index 5cae5d7a..a4afd691 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -25,6 +25,7 @@ module Submissions '▪' => '-', '✔️' => 'V', '✔' => 'V', + '✓' => 'V', '✅' => 'V' }.freeze @@ -32,6 +33,7 @@ module Submissions '▪' => :bullet, '✔️' => :V, '✔' => :V, + '✓' => :V, '✅' => :V }.freeze