Browse Source

Experimental code commit

Introduces wisper, resque jobs to process long-runing tasks
CLDC-122-experimental-background-job-lettings-log-import-with-report
Mo Seedat 2 years ago
parent
commit
7d61c5706d
  1. 13
      Gemfile
  2. 37
      Gemfile.lock
  3. 1
      Procfile.dev
  4. 3
      Rakefile
  5. 41
      app/controllers/lettings_test_controller.rb
  6. 4
      app/events/base.rb
  7. 6
      app/events/import.rb
  8. 2
      app/helpers/lettings_test_helper.rb
  9. 7
      app/jobs/cleanup_job.rb
  10. 16
      app/jobs/lettings_log_import_job.rb
  11. 48
      app/listeners/lettings_log_import_listener.rb
  12. 9
      app/models/lettings_log.rb
  13. 2
      app/models/logs_import.rb
  14. 95
      app/services/imports/lettings_logs_import_service.rb
  15. 1
      app/views/lettings_test/index.html.erb
  16. 12
      config/environments/development.rb
  17. 2
      config/environments/staging.rb
  18. 8
      config/environments/test.rb
  19. 3
      config/initializers/rack_profiler.rb
  20. 7
      config/initializers/wisper.rb
  21. 2
      config/routes.rb
  22. 18
      db/migrate/20220928002015_logs_imports.rb
  23. 21
      db/schema.rb
  24. BIN
      dump.rdb
  25. 11
      spec/services/imports/lettings_logs_import_service_spec.rb

13
Gemfile

@ -58,6 +58,9 @@ gem "sentry-ruby"
gem "possessive" gem "possessive"
# Strip whitespace from active record attributes # Strip whitespace from active record attributes
gem "auto_strip_attributes" gem "auto_strip_attributes"
# Background job processing
gem 'resque', '~> 2.4'
gem 'wisper', '~> 2.0'
group :development, :test do group :development, :test do
# Check gems for known vulnerabilities # Check gems for known vulnerabilities
@ -76,7 +79,6 @@ group :development do
# Display performance information such as SQL time and flame graphs for each request in your browser. # Display performance information such as SQL time and flame graphs for each request in your browser.
# Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md # Can be configured to work on production as well see: https://github.com/MiniProfiler/rack-mini-profiler/blob/master/README.md
gem "erb_lint", require: false gem "erb_lint", require: false
gem "rack-mini-profiler", "~> 2.0"
gem "rubocop-govuk", "4.3.0", require: false gem "rubocop-govuk", "4.3.0", require: false
gem "rubocop-performance", require: false gem "rubocop-performance", require: false
gem "rubocop-rails", require: false gem "rubocop-rails", require: false
@ -92,6 +94,15 @@ group :test do
gem "simplecov", require: false gem "simplecov", require: false
gem "timecop", "~> 0.9.4" gem "timecop", "~> 0.9.4"
gem "webmock", require: false gem "webmock", require: false
gem 'wisper-rspec', require: false
end
group :development, :test do
gem "rack-mini-profiler"
gem "flamegraph"
gem "stackprof"
gem "bullet"
gem "memory_profiler"
end end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem # Windows does not include zoneinfo files, so bundle the tzinfo-data gem

37
Gemfile.lock

@ -111,6 +111,9 @@ GEM
bootsnap (1.13.0) bootsnap (1.13.0)
msgpack (~> 1.2) msgpack (~> 1.2)
builder (3.2.4) builder (3.2.4)
bullet (7.0.3)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundler-audit (0.9.1) bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3) bundler (>= 1.2.0, < 3)
thor (~> 1.0) thor (~> 1.0)
@ -167,6 +170,7 @@ GEM
faker (2.23.0) faker (2.23.0)
i18n (>= 1.8.11, < 2) i18n (>= 1.8.11, < 2)
ffi (1.15.5) ffi (1.15.5)
flamegraph (0.9.5)
globalid (1.0.0) globalid (1.0.0)
activesupport (>= 5.0) activesupport (>= 5.0)
govuk-components (3.2.1) govuk-components (3.2.1)
@ -206,10 +210,15 @@ GEM
mini_mime (>= 0.1.1) mini_mime (>= 0.1.1)
marcel (1.0.2) marcel (1.0.2)
matrix (0.4.2) matrix (0.4.2)
memory_profiler (1.0.0)
method_source (1.0.0) method_source (1.0.0)
mini_mime (1.1.2) mini_mime (1.1.2)
minitest (5.16.3) minitest (5.16.3)
mono_logger (1.1.1)
msgpack (1.5.6) msgpack (1.5.6)
multi_json (1.15.0)
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
net-imap (0.2.3) net-imap (0.2.3)
digest digest
net-protocol net-protocol
@ -273,6 +282,8 @@ GEM
rack (>= 1.0, < 3) rack (>= 1.0, < 3)
rack-mini-profiler (2.3.4) rack-mini-profiler (2.3.4)
rack (>= 1.2.0) rack (>= 1.2.0)
rack-protection (3.0.1)
rack
rack-test (2.0.2) rack-test (2.0.2)
rack (>= 1.3) rack (>= 1.3)
rails (7.0.3.1) rails (7.0.3.1)
@ -312,12 +323,19 @@ GEM
redis-client (>= 0.7.4) redis-client (>= 0.7.4)
redis-client (0.8.0) redis-client (0.8.0)
connection_pool connection_pool
redis-namespace (1.9.0)
redis (>= 4)
regexp_parser (2.5.0) regexp_parser (2.5.0)
request_store (1.5.1) request_store (1.5.1)
rack (>= 1.4) rack (>= 1.4)
responders (3.0.1) responders (3.0.1)
actionpack (>= 5.0) actionpack (>= 5.0)
railties (>= 5.0) railties (>= 5.0)
resque (2.4.0)
mono_logger (~> 1.0)
multi_json (~> 1.0)
redis-namespace (~> 1.6)
sinatra (>= 0.9.2)
rexml (3.2.5) rexml (3.2.5)
roo (2.9.0) roo (2.9.0)
nokogiri (~> 1) nokogiri (~> 1)
@ -387,17 +405,25 @@ GEM
simplecov_json_formatter (~> 0.1) simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3) simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
sinatra (3.0.1)
mustermann (~> 3.0)
rack (~> 2.2, >= 2.2.4)
rack-protection (= 3.0.1)
tilt (~> 2.0)
smart_properties (1.17.0) smart_properties (1.17.0)
stackprof (0.2.21)
stimulus-rails (1.1.0) stimulus-rails (1.1.0)
railties (>= 6.0.0) railties (>= 6.0.0)
strscan (3.0.4) strscan (3.0.4)
thor (1.2.1) thor (1.2.1)
tilt (2.0.11)
timecop (0.9.5) timecop (0.9.5)
timeout (0.3.0) timeout (0.3.0)
tzinfo (2.0.5) tzinfo (2.0.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uk_postcode (2.1.8) uk_postcode (2.1.8)
unicode-display_width (2.2.0) unicode-display_width (2.2.0)
uniform_notifier (1.16.0)
view_component (2.69.0) view_component (2.69.0)
activesupport (>= 5.0.0, < 8.0) activesupport (>= 5.0.0, < 8.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
@ -417,6 +443,8 @@ GEM
websocket-driver (0.7.5) websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0) websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5) websocket-extensions (0.1.5)
wisper (2.0.1)
wisper-rspec (1.1.0)
xpath (3.2.0) xpath (3.2.0)
nokogiri (~> 1.8) nokogiri (~> 1.8)
zeitwerk (2.6.0) zeitwerk (2.6.0)
@ -432,6 +460,7 @@ DEPENDENCIES
auto_strip_attributes auto_strip_attributes
aws-sdk-s3 aws-sdk-s3
bootsnap (>= 1.4.4) bootsnap (>= 1.4.4)
bullet
bundler-audit bundler-audit
byebug byebug
capybara capybara
@ -442,12 +471,14 @@ DEPENDENCIES
erb_lint erb_lint
factory_bot_rails factory_bot_rails
faker faker
flamegraph
govuk-components govuk-components
govuk_design_system_formbuilder govuk_design_system_formbuilder
govuk_markdown govuk_markdown
jsbundling-rails jsbundling-rails
json-schema json-schema
listen (~> 3.3) listen (~> 3.3)
memory_profiler
notifications-ruby-client notifications-ruby-client
overcommit (>= 0.37.0) overcommit (>= 0.37.0)
paper_trail paper_trail
@ -459,9 +490,10 @@ DEPENDENCIES
pry-byebug pry-byebug
puma (~> 5.0) puma (~> 5.0)
rack-attack rack-attack
rack-mini-profiler (~> 2.0) rack-mini-profiler
rails (~> 7.0.2) rails (~> 7.0.2)
redis redis
resque (~> 2.4)
roo roo
rspec-rails rspec-rails
rubocop-govuk (= 4.3.0) rubocop-govuk (= 4.3.0)
@ -471,6 +503,7 @@ DEPENDENCIES
sentry-rails sentry-rails
sentry-ruby sentry-ruby
simplecov simplecov
stackprof
stimulus-rails stimulus-rails
timecop (~> 0.9.4) timecop (~> 0.9.4)
tzinfo-data tzinfo-data
@ -478,6 +511,8 @@ DEPENDENCIES
view_component view_component
web-console (>= 4.1.0) web-console (>= 4.1.0)
webmock webmock
wisper (~> 2.0)
wisper-rspec
RUBY VERSION RUBY VERSION
ruby 3.1.2p20 ruby 3.1.2p20

1
Procfile.dev

@ -1,2 +1,3 @@
QUEUE=* rake resque:work
web: bin/rails server -p 3000 web: bin/rails server -p 3000
js: yarn build --watch js: yarn build --watch

3
Rakefile

@ -2,5 +2,8 @@
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative "config/application" require_relative "config/application"
require 'resque/tasks'
task 'resque:setup' => :environment
Rails.application.load_tasks Rails.application.load_tasks

41
app/controllers/lettings_test_controller.rb

@ -0,0 +1,41 @@
require 'nokogiri'
require 'securerandom'
require 'fileutils'
class LettingsTestController < ApplicationController
def index
folder = '/Users/mohseeadmin/development-meta/CORE/CLDC-1222'
stash_folder = Time.now.to_i.to_s
generate_fixtures(folder, stash_folder, 50)
LettingsLog.connection.truncate(LettingsLog.table_name)
Imports::LettingsLogsImportService.new(Storage::S3Service).local_load("#{folder}/#{stash_folder}")
end
def node(xml_document, namespace, field)
xml_document.at_xpath("//#{namespace}:#{field}")
end
def generate_fixtures(folder, stash_folder, num_files)
#folder = '/Users/mohseeadmin/development-meta/CORE/CLDC-1222/'
canonical_logfiles = %w[canonical_logfile1.xml canonical_logfile2.xml canonical_logfile3.xml]
FileUtils.mkdir_p("#{folder}/#{stash_folder}")
(1..num_files).each do |i|
xml_document = Nokogiri::XML(File.read("#{folder.chomp('/')}/#{canonical_logfiles.sample}"))
document_id = node(xml_document, 'meta', 'document-id')
new_guid = SecureRandom.uuid.to_s
document_id.content = new_guid
File.open("#{folder.chomp('/')}/#{stash_folder}/#{new_guid}.xml", 'w') { |f| f.puts xml_document }
end
end
end

4
app/events/base.rb

@ -0,0 +1,4 @@
module Events
class Base
end
end

6
app/events/import.rb

@ -0,0 +1,6 @@
module Import
STARTED = :events_import_started
ITEM_PROCESSED = :events_import_item_processed
FINISHED = :events_import_finished
end

2
app/helpers/lettings_test_helper.rb

@ -0,0 +1,2 @@
module LettingsTestHelper
end

7
app/jobs/cleanup_job.rb

@ -0,0 +1,7 @@
class CleanupJob < ApplicationJob
queue_as :default
def perform(*args)
# Do something later
end
end

16
app/jobs/lettings_log_import_job.rb

@ -0,0 +1,16 @@
class LettingsLogImportJob < ApplicationJob
include Wisper::Publisher
self.queue_name_prefix = '_lettings_logs'
queue_as :default
def perform(run_id, xml_document)
puts "PERFORMING RUN: #{run_id} WITH XML DOC: #{xml_document}"
#Wisper.subscribe(LettingsLogImportListener.new, prefix: :on)
processor = Imports::LettingsLogsImportProcessor.new(xml_document)
broadcast(::Import::ITEM_PROCESSED, run_id, processor)
end
end

48
app/listeners/lettings_log_import_listener.rb

@ -0,0 +1,48 @@
class LettingsLogImportListener
# include Wisper::Publisher
def on_events_import_started(run_id)
puts "LettingsLogs::ImportListener STARTING RUN -> #{run_id}"
end
def on_events_import_finished(run_id)
puts "LettingsLogs::ImportListener FINISHED RUN -> #{run_id}"
end
def on_events_import_item_processed(run_id, processor)
puts "LettingsLogs::ImportListener ITEM PROCESSED -> #{run_id} old_id: #{processor.old_id}, discrepency?: #{processor.discrepancy?}"
redis = Redis.new
obj = redis.get(run_id)
logs_import = Marshal.load(obj)
puts "GOT FROM REDIS: total: #{logs_import.total}"
logs_import.num_saved += 1
if processor.discrepancy?
logs_import.discrepancies << processor.old_id
end
redis.set(run_id, Marshal.dump(logs_import))
if last_item?(logs_import)
collate_results_and_update_db(logs_import)
send_email_with_results(logs_import)
# broadcast(::Import::FINISHED, run_id)
end
end
def last_item?(logs_import)
logs_import.total == (logs_import.num_saved + logs_import.num_skipped)
end
def collate_results_and_update_db(logs_import)
logs_import.finished_at = Time.zone.now
logs_import.duration_seconds = (logs_import.finished_at - logs_import.started_at).seconds.to_i
logs_import.save!
end
def send_email_with_results(logs_import)
# TODO
end
end

9
app/models/lettings_log.rb

@ -8,6 +8,7 @@ class LettingsLogValidator < ActiveModel::Validator
include Validations::TenancyValidations include Validations::TenancyValidations
include Validations::DateValidations include Validations::DateValidations
include Validations::LocalAuthorityValidations include Validations::LocalAuthorityValidations
def validate(record) def validate(record)
validation_methods = public_methods.select { |method| method.starts_with?("validate_") } validation_methods = public_methods.select { |method| method.starts_with?("validate_") }
validation_methods.each { |meth| public_send(meth, record) } validation_methods.each { |meth| public_send(meth, record) }
@ -24,8 +25,10 @@ class LettingsLog < ApplicationRecord
before_validation :recalculate_start_year!, if: :startdate_changed? before_validation :recalculate_start_year!, if: :startdate_changed?
before_validation :reset_scheme_location!, if: :scheme_changed?, unless: :location_changed? before_validation :reset_scheme_location!, if: :scheme_changed?, unless: :location_changed?
before_validation :process_postcode_changes!, if: :postcode_full_changed? before_validation :process_postcode_changes!, if: :postcode_full_changed?
before_validation :process_previous_postcode_changes!, if: :ppostcode_full_changed?
before_validation :reset_invalidated_dependent_fields! #SLOW: before_validation :process_previous_postcode_changes!, if: :ppostcode_full_changed?
#SLOW: before_validation :reset_invalidated_dependent_fields!
before_validation :reset_location_fields!, unless: :postcode_known? before_validation :reset_location_fields!, unless: :postcode_known?
before_validation :reset_previous_location_fields!, unless: :previous_postcode_known? before_validation :reset_previous_location_fields!, unless: :previous_postcode_known?
before_validation :set_derived_fields! before_validation :set_derived_fields!
@ -727,7 +730,7 @@ private
end end
def upcase_and_remove_whitespace(string) def upcase_and_remove_whitespace(string)
string.present? ? string.upcase.gsub(/\s+/, "") : string string&.upcase.gsub(/\s+/, "")
end end
def fully_wheelchair_accessible? def fully_wheelchair_accessible?

2
app/models/logs_import.rb

@ -0,0 +1,2 @@
class LogsImport < ApplicationRecord
end

95
app/services/imports/lettings_logs_import_service.rb

@ -1,9 +1,14 @@
require 'securerandom'
require 'json'
module Imports module Imports
class LettingsLogsImportService < ImportService class LettingsLogsImportService < ImportService
include Wisper::Publisher
def initialize(storage_service, logger = Rails.logger) def initialize(storage_service, logger = Rails.logger)
@logs_with_discrepancies = Set.new
@logs_overridden = Set.new
super super
Wisper.subscribe(LettingsLogImportListener.new, prefix: :on)
end end
def create_logs(folder) def create_logs(folder)
@ -13,8 +18,58 @@ module Imports
end end
end end
private def import_from(folder, create_method)
filenames = @storage_service.list_files(folder)
filenames.each do |filename|
file_io = @storage_service.get_file_io(filename)
xml_document = Nokogiri::XML(file_io)
send(create_method, xml_document)
rescue StandardError => e
@logger.error "#{e.class} in #{filename}: #{e.message}. Caller: #{e.backtrace.first}"
end
end
def local_load(folder)
filenames = Dir["#{folder}/**/*.xml"]
puts "FILENAMES (#{filenames.size}): #{filenames}"
@run_id = SecureRandom.uuid.to_s
logs_import = LogsImport.create!(
run_id: @run_id,
started_at: Time.zone.now,
total: filenames.size,
discrepancies: [],
filenames: filenames
)
redis = Redis.new
redis.set(@run_id, Marshal.dump(logs_import))
broadcast(::Import::STARTED, @run_id)
filenames.each do |filename|
puts "Loading filename: #{filename}"
Rack::MiniProfiler.step("Start Processing file #{filename}") do
# Generate background job to process file completely
xml_document = Nokogiri::XML(File.open(filename))
LettingsLogImportJob.perform_now(@run_id, xml_document.to_s)
#send(:create_log, xml_document)
end
rescue StandardError => e
@logger.error "#{e.class} in #{filename}: #{e.message}. Caller: #{e.backtrace.first}"
end
if @logs_with_discrepancies.count.positive?
@logger.warn("The following lettings logs had status discrepancies: [#{@logs_with_discrepancies.join(', ')}]")
end
end
end
class LettingsLogsImportProcessor
FORM_NAME_INDEX = { FORM_NAME_INDEX = {
start_year: 0, start_year: 0,
rent_type: 2, rent_type: 2,
@ -54,11 +109,24 @@ module Imports
other_intermediate_rent_product: 5, other_intermediate_rent_product: 5,
}.freeze }.freeze
def create_log(xml_doc) attr_reader :xml_doc, :logs_with_discrepancies, :logs_overridden, :discrepancy, :old_id
attributes = {}
def initialize(xml_doc)
@xml_doc = xml_doc
@discrepancy = false
@old_id = ''
@logs_with_discrepancies = Set.new
@logs_overridden = Set.new
end
def create_log
attributes = {}
lettings_log = nil
previous_status = field_value(xml_doc, "meta", "status") previous_status = field_value(xml_doc, "meta", "status")
Rack::MiniProfiler.step("Loading attributes") do
# Required fields for status complete or logic to work # Required fields for status complete or logic to work
# Note: order matters when we derive from previous values (attributes parameter) # Note: order matters when we derive from previous values (attributes parameter)
attributes["startdate"] = compose_date(xml_doc, "DAY", "MONTH", "YEAR") attributes["startdate"] = compose_date(xml_doc, "DAY", "MONTH", "YEAR")
@ -182,6 +250,7 @@ module Imports
attributes["created_at"] = Time.zone.parse(field_value(xml_doc, "meta", "created-date")) attributes["created_at"] = Time.zone.parse(field_value(xml_doc, "meta", "created-date"))
attributes["updated_at"] = Time.zone.parse(field_value(xml_doc, "meta", "modified-date")) attributes["updated_at"] = Time.zone.parse(field_value(xml_doc, "meta", "modified-date"))
attributes["old_id"] = field_value(xml_doc, "meta", "document-id") attributes["old_id"] = field_value(xml_doc, "meta", "document-id")
@old_id = attributes["old_id"]
# Required for our form invalidated questions (not present in import) # Required for our form invalidated questions (not present in import)
attributes["previous_la_known"] = attributes["prevloc"].nil? ? 0 : 1 attributes["previous_la_known"] = attributes["prevloc"].nil? ? 0 : 1
@ -231,16 +300,22 @@ module Imports
apply_date_consistency!(attributes) apply_date_consistency!(attributes)
apply_household_consistency!(attributes) apply_household_consistency!(attributes)
end # ENDPROFILER
lettings_log = save_lettings_log(attributes) lettings_log = save_lettings_log(attributes)
compute_differences(lettings_log, attributes) compute_differences(lettings_log, attributes)
check_status_completed(lettings_log, previous_status) unless @logs_overridden.include?(lettings_log.old_id) check_status_completed(lettings_log, previous_status) unless @logs_overridden.include?(lettings_log.old_id)
end end
def save_lettings_log(attributes) def save_lettings_log(attributes)
lettings_log = LettingsLog.new(attributes) lettings_log = LettingsLog.new(attributes)
begin begin
lettings_log.save! Rack::MiniProfiler.step("Saving...") do
lettings_log.save!(validate: true)
end
lettings_log lettings_log
rescue ActiveRecord::RecordNotUnique rescue ActiveRecord::RecordNotUnique
legacy_id = attributes["old_id"] legacy_id = attributes["old_id"]
@ -291,6 +366,7 @@ module Imports
@logger.warn "lettings log #{lettings_log.id} is not completed" @logger.warn "lettings log #{lettings_log.id} is not completed"
@logger.warn "lettings log with old id:#{lettings_log.old_id} is incomplete but status should be complete" @logger.warn "lettings log with old id:#{lettings_log.old_id} is incomplete but status should be complete"
@logs_with_discrepancies << lettings_log.old_id @logs_with_discrepancies << lettings_log.old_id
@discrepancy = true
end end
end end
@ -657,5 +733,12 @@ module Imports
0 0
end end
end end
def discrepancy?
!!@discrepancy
end
end end
end end

1
app/views/lettings_test/index.html.erb

@ -0,0 +1 @@
<h1>Lettings Test</h1>

12
config/environments/development.rb

@ -1,6 +1,14 @@
require "active_support/core_ext/integer/time" require "active_support/core_ext/integer/time"
Rails.application.configure do Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.alert = true
Bullet.bullet_logger = true
Bullet.console = true
Bullet.rails_logger = true
end
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# In the development environment your application's code is reloaded any time # In the development environment your application's code is reloaded any time
@ -83,4 +91,8 @@ Rails.application.configure do
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [Time] config.active_record.yaml_column_permitted_classes = [Time]
config.active_job.queue_adapter = :resque
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
end end

2
config/environments/staging.rb

@ -127,4 +127,6 @@ Rails.application.configure do
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [Time] config.active_record.yaml_column_permitted_classes = [Time]
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
end end

8
config/environments/test.rb

@ -6,6 +6,12 @@ require "active_support/core_ext/integer/time"
# and recreated between test runs. Don't rely on the data there! # and recreated between test runs. Don't rely on the data there!
Rails.application.configure do Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.bullet_logger = true
Bullet.raise = true # raise an error if n+1 query occurs
end
# Settings specified here will take precedence over those in config/application.rb. # Settings specified here will take precedence over those in config/application.rb.
# Turn false under Spring and add config.action_view.cache_template_loading = true # Turn false under Spring and add config.action_view.cache_template_loading = true
@ -64,4 +70,6 @@ Rails.application.configure do
# see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017 # see https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [Time] config.active_record.yaml_column_permitted_classes = [Time]
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
end end

3
config/initializers/rack_profiler.rb

@ -0,0 +1,3 @@
if Rails.env == 'development' || Rails.env == 'test'
# Rack::MiniProfilerRails.initialize!(Rails.application)
end

7
config/initializers/wisper.rb

@ -0,0 +1,7 @@
# Wisper global subscribers
# https://github.com/krisleech/wisper
#require_relative '../../app/listeners/lettings_logs/import_listener'
#require_relative '../../app/services/imports/lettings_logs_import_service'
# Wisper.subscribe(LettingsLogsImportListener.new, scope: [Imports::LettingsLogsImportService])

2
config/routes.rb

@ -101,4 +101,6 @@ Rails.application.routes.draw do
match "/422", to: "errors#unprocessable_entity" match "/422", to: "errors#unprocessable_entity"
match "/500", to: "errors#internal_server_error" match "/500", to: "errors#internal_server_error"
end end
resources :lettings_test
end end

18
db/migrate/20220928002015_logs_imports.rb

@ -0,0 +1,18 @@
class LogsImports < ActiveRecord::Migration[7.0]
def change
create_table :logs_imports do |t|
t.string :run_id, null: false
t.datetime :started_at
t.datetime :finished_at
t.integer :duration_seconds, default: 0
t.integer :total, default: 0 # Total logs to import in batch
t.integer :num_saved, default: 0
t.integer :num_skipped, default: 0
t.jsonb :discrepancies # List of files with errors, etc
t.jsonb :filenames # List of filenames processed
t.timestamps
end
end
end

21
db/schema.rb

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do ActiveRecord::Schema[7.0].define(version: 2022_09_28_002015) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -259,6 +259,11 @@ ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do
t.index ["scheme_id"], name: "index_locations_on_scheme_id" t.index ["scheme_id"], name: "index_locations_on_scheme_id"
end end
create_table "log_imports", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "logs_exports", force: :cascade do |t| create_table "logs_exports", force: :cascade do |t|
t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" } t.datetime "created_at", default: -> { "CURRENT_TIMESTAMP" }
t.datetime "started_at", null: false t.datetime "started_at", null: false
@ -267,6 +272,20 @@ ActiveRecord::Schema[7.0].define(version: 2022_09_02_082245) do
t.boolean "empty_export", default: false, null: false t.boolean "empty_export", default: false, null: false
end end
create_table "logs_imports", force: :cascade do |t|
t.string "run_id", null: false
t.datetime "started_at"
t.datetime "finished_at"
t.integer "duration_seconds", default: 0
t.integer "total", default: 0
t.integer "num_saved", default: 0
t.integer "num_skipped", default: 0
t.jsonb "discrepancies"
t.jsonb "filenames"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "organisation_relationships", force: :cascade do |t| create_table "organisation_relationships", force: :cascade do |t|
t.integer "child_organisation_id" t.integer "child_organisation_id"
t.integer "parent_organisation_id" t.integer "parent_organisation_id"

BIN
dump.rdb

Binary file not shown.

11
spec/services/imports/lettings_logs_import_service_spec.rb

@ -68,8 +68,15 @@ RSpec.describe Imports::LettingsLogsImportService do
expect(logger).not_to receive(:error) expect(logger).not_to receive(:error)
expect(logger).not_to receive(:warn) expect(logger).not_to receive(:warn)
expect(logger).not_to receive(:info) expect(logger).not_to receive(:info)
expect { lettings_log_service.create_logs(remote_folder) } #expect { lettings_log_service.create_logs(remote_folder) }
.to change(LettingsLog, :count).by(4) # .to change(LettingsLog, :count).by(4)
total_logs = LettingsLog.all.size
Rack::MiniProfiler.config.storage = Rack::MiniProfiler::MemoryStore
Rack::MiniProfiler.step('Create Case Logs') do
puts "Creating case logs, profiling!"
lettings_log_service.create_logs(remote_folder)
end
expect(LettingsLog.all.size).to eq(total_logs + 4)
end end
it "only updates existing lettings logs" do it "only updates existing lettings logs" do

Loading…
Cancel
Save