From 9a051e0f89064d526c36c23fb991aed3013f716e Mon Sep 17 00:00:00 2001 From: Zeke Gabrielse Date: Wed, 1 May 2024 07:47:37 -0500 Subject: [PATCH 1/3] fix nil log resource in development seeds --- db/seeds/development.rb | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/db/seeds/development.rb b/db/seeds/development.rb index 5c0bb87b99..64a43f77c6 100644 --- a/db/seeds/development.rb +++ b/db/seeds/development.rb @@ -288,10 +288,21 @@ def srand(range) = rand < 0.9 ? brand(range.begin..(range.begin + range.end * 0. route.required_parts.reduce({}) { _1.merge(_2 => SecureRandom.uuid) }, ) - resource = route.requirements[:controller].classify.split('::').last.safe_constantize + # Select a random record + resource = if klass = route.requirements[:controller].classify.split('::').last.safe_constantize + next unless klass < ActiveRecord::Base + + scope = klass < Accountable ? klass.where(account:) : klass.all + record = scope.offset((rand() * scope.count).floor) # random row + .limit(1) + .take + + record + end + environment = resource.try(:environment) - admin = account.admins.for_environment(environment, strict: true).sample - requestor = if resource.respond_to?(:role) && rand(0..1).zero? + admin = account.admins.for_environment(environment, strict: true).take + requestor = if rand(0..1).zero? && resource.respond_to?(:role) resource else admin @@ -316,15 +327,17 @@ def srand(range) = rand < 0.9 ? brand(range.begin..(range.begin + range.end * 0. account:, ) - event_log = EventLog.create!( - event_type_id: event_types.sample, - idempotency_key: SecureRandom.hex, - whodunnit: requestor, - environment:, - resource:, - request_log:, - account:, - ) + unless resource.nil? + EventLog.create!( + event_type_id: event_types.sample, + idempotency_key: SecureRandom.hex, + whodunnit: requestor, + environment:, + resource:, + request_log:, + account:, + ) + end end end rescue ActiveRecord::RecordNotSaved => e From 2aa0b0f46d501eef5a6db33c7ad0a71924e51dc1 Mon Sep 17 00:00:00 2001 From: Zeke Gabrielse Date: Wed, 1 May 2024 07:48:48 -0500 Subject: [PATCH 2/3] add debug flag to show full backtraces --- config/initializers/backtrace_silencers.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 390ce9393a..f2bb4df45a 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -5,5 +5,5 @@ # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } -# # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. +Rails.backtrace_cleaner.remove_silencers! if ENV.key?('DEBUG') From 5dde81538f833bb334f962bc30cba0e3efe425e6 Mon Sep 17 00:00:00 2001 From: Zeke Gabrielse Date: Wed, 1 May 2024 08:15:16 -0500 Subject: [PATCH 3/3] denormalize event type for request and event logs --- app/models/concerns/denormalizable.rb | 6 +- app/models/event_log.rb | 4 ++ app/models/request_log.rb | 4 ++ ...4919_add_event_type_event_to_event_logs.rb | 7 ++ ...26_add_event_type_event_to_request_logs.rb | 8 +++ db/schema.rb | 5 +- spec/models/event_log_spec.rb | 64 +++++++++++++++++++ spec/models/request_log_spec.rb | 32 ++++++++++ 8 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20240501124919_add_event_type_event_to_event_logs.rb create mode 100644 db/migrate/20240501124926_add_event_type_event_to_request_logs.rb diff --git a/app/models/concerns/denormalizable.rb b/app/models/concerns/denormalizable.rb index 5aa271d65b..b6443c14fa 100644 --- a/app/models/concerns/denormalizable.rb +++ b/app/models/concerns/denormalizable.rb @@ -49,7 +49,7 @@ def instrument_denormalized_attribute_from(attribute_name, from:, prefix:) after_initialize -> { write_denormalized_attribute_from_schrodingers_record(association_name, attribute_name, prefixed_attribute_name) }, if: -> { send(:"#{reflection.foreign_key}_changed?") || send(:"#{reflection.name}_changed?") }, unless: :persisted? before_validation -> { write_denormalized_attribute_from_schrodingers_record(association_name, attribute_name, prefixed_attribute_name) }, if: -> { send(:"#{reflection.foreign_key}_changed?") || send(:"#{reflection.name}_changed?") }, on: :create - before_update -> { write_denormalized_attribute_from_persisted_record(association_name, attribute_name, prefixed_attribute_name) }, if: -> { send(:"#{reflection.foreign_key}_changed?") || send(:"#{reflection.name}_changed?") } + before_update -> { write_denormalized_attribute_from_persisted_record(association_name, attribute_name, prefixed_attribute_name) }, if: -> { send(:"#{reflection.foreign_key}_changed?") || send(:"#{reflection.name}_changed?") } # Make sure validation fails if our denormalized column is modified directly validate -> { validate_denormalized_attribute_from_persisted_record(association_name, attribute_name, prefixed_attribute_name) }, if: :"#{prefixed_attribute_name}_changed?", on: :update @@ -75,11 +75,11 @@ def instrument_denormalized_attribute_to(attribute_name, to:, prefix:) if reflection.collection? after_initialize -> { write_denormalized_attribute_to_unpersisted_relation(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_changed?", unless: :persisted? before_validation -> { write_denormalized_attribute_to_unpersisted_relation(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_changed?", on: :create - after_update -> { write_denormalized_attribute_to_persisted_relation(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_previously_changed?" + after_save -> { write_denormalized_attribute_to_persisted_relation(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_previously_changed?" else after_initialize -> { write_denormalized_attribute_to_unpersisted_record(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_changed?", unless: :persisted? before_validation -> { write_denormalized_attribute_to_unpersisted_record(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_changed?", on: :create - after_update -> { write_denormalized_attribute_to_persisted_record(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_previously_changed?" + after_save -> { write_denormalized_attribute_to_persisted_record(association_name, prefixed_attribute_name, attribute_name) }, if: :"#{attribute_name}_previously_changed?" end else raise ArgumentError, "invalid :to association: #{to.inspect}" diff --git a/app/models/event_log.rb b/app/models/event_log.rb index f0f17739ef..d4619ead7b 100644 --- a/app/models/event_log.rb +++ b/app/models/event_log.rb @@ -2,6 +2,7 @@ class EventLog < ApplicationRecord include Keygen::EE::ProtectedClass[entitlements: %i[event_logs]] + include Denormalizable include Environmental include Accountable include DateRangeable @@ -22,6 +23,9 @@ class EventLog < ApplicationRecord has_environment has_account + denormalizes :event, from: :event_type, prefix: :event_type + denormalizes :event_type_id, to: :request_log + # NOTE(ezekg) Would love to add a default instead of this, but alas, # the table is too big and it would break everything. before_create -> { self.created_date ||= (created_at || Date.current) } diff --git a/app/models/request_log.rb b/app/models/request_log.rb index c1067d2dcd..f8ec03ebd2 100644 --- a/app/models/request_log.rb +++ b/app/models/request_log.rb @@ -2,6 +2,7 @@ class RequestLog < ApplicationRecord include Keygen::EE::ProtectedClass[entitlements: %i[request_logs]] + include Denormalizable include Environmental include Accountable include DateRangeable @@ -11,12 +12,15 @@ class RequestLog < ApplicationRecord belongs_to :requestor, polymorphic: true, optional: true belongs_to :resource, polymorphic: true, optional: true + belongs_to :event_type, optional: true has_one :event_log, inverse_of: :request_log has_environment has_account + denormalizes :event, from: :event_type, prefix: :event_type + # NOTE(ezekg) Would love to add a default instead of this, but alas, # the table is too big and it would break everything. before_create -> { self.created_date ||= (created_at || Date.current) } diff --git a/db/migrate/20240501124919_add_event_type_event_to_event_logs.rb b/db/migrate/20240501124919_add_event_type_event_to_event_logs.rb new file mode 100644 index 0000000000..ec0aa6ab45 --- /dev/null +++ b/db/migrate/20240501124919_add_event_type_event_to_event_logs.rb @@ -0,0 +1,7 @@ +class AddEventTypeEventToEventLogs < ActiveRecord::Migration[7.1] + verbose! + + def change + add_column :event_logs, :event_type_event, :string, null: true + end +end diff --git a/db/migrate/20240501124926_add_event_type_event_to_request_logs.rb b/db/migrate/20240501124926_add_event_type_event_to_request_logs.rb new file mode 100644 index 0000000000..7df58f83b9 --- /dev/null +++ b/db/migrate/20240501124926_add_event_type_event_to_request_logs.rb @@ -0,0 +1,8 @@ +class AddEventTypeEventToRequestLogs < ActiveRecord::Migration[7.1] + verbose! + + def change + add_column :request_logs, :event_type_event, :string, null: true + add_column :request_logs, :event_type_id, :uuid, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 46bd49f813..470e382bf1 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_24_041244) do +ActiveRecord::Schema[7.1].define(version: 2024_05_01_124926) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" enable_extension "pg_stat_statements" @@ -110,6 +110,7 @@ t.datetime "updated_at", null: false t.uuid "environment_id" t.date "created_date" + t.string "event_type_event" t.index ["account_id", "created_at"], name: "index_event_logs_on_account_id_and_created_at", order: { created_at: :desc } t.index ["account_id", "created_date"], name: "index_event_logs_on_account_id_and_created_date", order: { created_date: :desc } t.index ["environment_id"], name: "index_event_logs_on_environment_id" @@ -695,6 +696,8 @@ t.jsonb "response_headers" t.float "run_time" t.float "queue_time" + t.string "event_type_event" + t.uuid "event_type_id" t.index ["account_id", "created_at"], name: "index_request_logs_on_account_id_and_created_at" t.index ["account_id", "created_date"], name: "index_request_logs_on_account_id_and_created_date", order: { created_date: :desc } t.index ["environment_id"], name: "index_request_logs_on_environment_id" diff --git a/spec/models/event_log_spec.rb b/spec/models/event_log_spec.rb index 222bc13499..2a5978b4ee 100644 --- a/spec/models/event_log_spec.rb +++ b/spec/models/event_log_spec.rb @@ -4,6 +4,70 @@ require 'spec_helper' describe EventLog, type: :model do + let(:account) { create(:account) } + it_behaves_like :environmental it_behaves_like :accountable + + describe '#event_type=' do + let(:event_type) { create(:event_type) } + + context 'on build' do + it 'should denormalize event from event type' do + event_log = build(:event_log, event_type:, account:) + + expect(event_log.event_type_event).to eq event_type.event + expect(event_log.event_type_id).to eq event_type.id + end + + it 'should denormalize event type to request log' do + request_log = build(:request_log, account:) + event_log = build(:event_log, request_log:, event_type:, account:) + + expect(request_log.event_type_event).to be_nil + expect(request_log.event_type_id).to eq event_type.id + end + end + + context 'on create' do + it 'should denormalize event from event type' do + event_log = create(:event_log, event_type:, account:) + + expect(event_log.event_type_event).to eq event_type.event + expect(event_log.event_type_id).to eq event_type.id + end + + it 'should denormalize event type to request log' do + request_log = create(:request_log, account:) + event_log = create(:event_log, request_log:, event_type:, account:) + + request_log.reload + + expect(request_log.event_type_event).to eq event_type.event + expect(request_log.event_type_id).to eq event_type.id + end + end + + context 'on update' do + it 'should denormalize event from event type' do + event_log = create(:event_log, account:) + + event_log.update!(event_type:) + + expect(event_log.event_type_event).to eq event_type.event + expect(event_log.event_type_id).to eq event_type.id + end + + it 'should denormalize event type to request log' do + request_log = create(:request_log, account:) + event_log = create(:event_log, request_log:, account:) + + event_log.update!(event_type:) + request_log.reload + + expect(request_log.event_type_event).to eq event_type.event + expect(request_log.event_type_id).to eq event_type.id + end + end + end end diff --git a/spec/models/request_log_spec.rb b/spec/models/request_log_spec.rb index 69fa872a42..f976289a2e 100644 --- a/spec/models/request_log_spec.rb +++ b/spec/models/request_log_spec.rb @@ -4,6 +4,38 @@ require 'spec_helper' describe RequestLog, type: :model do + let(:account) { create(:account) } + it_behaves_like :environmental it_behaves_like :accountable + + describe '#event_type=' do + let(:event_type) { create(:event_type) } + + context 'on build' do + it 'should denormalize event from event type' do + request_log = build(:request_log, event_type:, account:) + + expect(request_log.event_type_event).to eq event_type.event + end + end + + context 'on create' do + it 'should denormalize event from event type' do + request_log = create(:request_log, event_type:, account:) + + expect(request_log.event_type_event).to eq event_type.event + end + end + + context 'on update' do + it 'should denormalize event from event type' do + request_log = create(:request_log, account:) + + request_log.update!(event_type:) + + expect(request_log.event_type_event).to eq event_type.event + end + end + end end