From 0e3309d5b543d17fdbf8322966a3d3bdc9e1457c Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Tue, 20 Feb 2024 16:05:08 -0500 Subject: [PATCH 1/9] Upgrade to Rails 7, Mongoid 8, and Nokogiri 1.16.2 to resolve a variety of vulnerabilities. --- cqm-models.gemspec | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cqm-models.gemspec b/cqm-models.gemspec index 1903e9fe..0e60b16f 100644 --- a/cqm-models.gemspec +++ b/cqm-models.gemspec @@ -22,8 +22,9 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundle-audit' spec.add_development_dependency 'byebug', '~> 11.0.1' spec.add_development_dependency 'codecov' - spec.add_development_dependency 'mongoid', '~> 6.4' - spec.add_development_dependency 'rails', '~> 5.2' + spec.add_development_dependency 'mongoid', '~> 8' + spec.add_development_dependency 'nokogiri', '>= 1.16.2' + spec.add_development_dependency 'rails', '~> 7.1' spec.add_development_dependency 'rake', '~> 12.3.3' spec.add_development_dependency 'rspec', '~> 3.0' spec.add_development_dependency 'rubocop', '~> 0.54.0' From 823e31014a751ea2765cddeabc4082c0cfdd5fc6 Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Tue, 20 Feb 2024 16:05:56 -0500 Subject: [PATCH 2/9] Pass facility_location as a Hash to resolve unit test failure. Part of facility_loc hash commit --- app/models/qdm/basetypes/data_element.rb | 4 +++- spec/cqm/models_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/models/qdm/basetypes/data_element.rb b/app/models/qdm/basetypes/data_element.rb index 3a684a8c..f353ae43 100644 --- a/app/models/qdm/basetypes/data_element.rb +++ b/app/models/qdm/basetypes/data_element.rb @@ -76,7 +76,9 @@ def shift_dates(seconds) elsif field == 'facilityLocation' facility_location = send(field) unless facility_location.nil? - shift_facility_location_dates(facility_location, seconds) + # Guessing something changed in mongoid such that facility_location gets typed to an Object + # but this method (I think) expects a Hash. + shift_facility_location_dates(facility_location.attributes, seconds) send(field + '=', facility_location) end end diff --git a/spec/cqm/models_spec.rb b/spec/cqm/models_spec.rb index c381863f..ec7e36c7 100644 --- a/spec/cqm/models_spec.rb +++ b/spec/cqm/models_spec.rb @@ -168,8 +168,8 @@ expect(@patient_de1.qdmPatient.diagnostic_studies.first.relevantPeriod.high.utc.to_s).to include('07:00:00') # DiatnosticStudyPerformed facilityLocation high and low should be two hours ahead - expect(@patient_de1.qdmPatient.diagnostic_studies.first.facilityLocation['locationPeriod'][:low].utc.to_s).to include('06:00:00') - expect(@patient_de1.qdmPatient.diagnostic_studies.first.facilityLocation['locationPeriod'][:high].utc.to_s).to include('07:00:00') + expect(@patient_de1.qdmPatient.diagnostic_studies.first.facilityLocation.locationPeriod.low.utc.to_s).to include('06:00:00') + expect(@patient_de1.qdmPatient.diagnostic_studies.first.facilityLocation.locationPeriod.high.utc.to_s).to include('07:00:00') end it 'shift patient data elements backwards in time' do @@ -198,8 +198,8 @@ expect(@patient_de2.qdmPatient.diagnostic_studies.first.relevantPeriod.high.utc.to_s).to include('03:00:00') # DiatnosticStudyPerformed facilityLocation high and low should be two hours behind - expect(@patient_de2.qdmPatient.diagnostic_studies.first.facilityLocation['locationPeriod'][:low].utc.to_s).to include('02:00:00') - expect(@patient_de2.qdmPatient.diagnostic_studies.first.facilityLocation['locationPeriod'][:high].utc.to_s).to include('03:00:00') + expect(@patient_de2.qdmPatient.diagnostic_studies.first.facilityLocation.locationPeriod.low.utc.to_s).to include('02:00:00') + expect(@patient_de2.qdmPatient.diagnostic_studies.first.facilityLocation.locationPeriod.high.utc.to_s).to include('03:00:00') end it 'relatedTo properly links data elements' do From 9552403965aa5bc332b60681bea9d83bb022a600 Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Tue, 20 Feb 2024 16:08:04 -0500 Subject: [PATCH 3/9] Move Provider NPI uniqueness check to QDM::Identifier. Resolves unit test failure as a workaround for updates in mongoid 8 (and 7) that expect uniqueness checks against explicit fields. --- app/models/cqm/provider.rb | 2 -- app/models/qdm/attributes/identifier.rb | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/cqm/provider.rb b/app/models/cqm/provider.rb index dba91b9b..8ea20079 100644 --- a/app/models/cqm/provider.rb +++ b/app/models/cqm/provider.rb @@ -12,8 +12,6 @@ class Provider field :specialty, type: String field :title, type: String - validates_uniqueness_of :npi, allow_blank: true - embeds_many :addresses embeds_many :telecoms embeds_many :ids, class_name: 'QDM::Identifier' diff --git a/app/models/qdm/attributes/identifier.rb b/app/models/qdm/attributes/identifier.rb index 1ef466d0..43182737 100644 --- a/app/models/qdm/attributes/identifier.rb +++ b/app/models/qdm/attributes/identifier.rb @@ -5,5 +5,8 @@ class Identifier < Attribute field :namingSystem, type: String field :value, type: String field :qdmVersion, type: String, default: '5.6' + + validates_uniqueness_of :value, conditions: -> {where(namingSystem: CQM::Provider::NPI_OID)} + end end From 0eecbe8f34b59d88183d652ee13a455ed87741ea Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Tue, 20 Feb 2024 16:12:24 -0500 Subject: [PATCH 4/9] dot retrieval resolves unit test failure --- spec/cqm/models_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/cqm/models_spec.rb b/spec/cqm/models_spec.rb index ec7e36c7..f606e245 100644 --- a/spec/cqm/models_spec.rb +++ b/spec/cqm/models_spec.rb @@ -216,7 +216,7 @@ it 'entity datatype can be saved correctly' do puts @patient_c.qdmPatient.dataElements[3].participant.first['identifier'] @patient_c.save - expect(@patient_c.qdmPatient.dataElements[3].participant.first['specialty'][:code]).to eq 'foo code 2' + expect(@patient_c.qdmPatient.dataElements[3].participant.first['specialty'].code).to eq 'foo code 2' expect(@patient_c.qdmPatient.dataElements[3].participant.first['identifier']['value']).to eq 'foo value' end From b0da72c2b7c0cdc2c79fed9a037c90d24a77d95b Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Thu, 22 Feb 2024 16:08:01 -0500 Subject: [PATCH 5/9] Minor version bump --- cqm-models.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cqm-models.gemspec b/cqm-models.gemspec index 0e60b16f..97962e69 100644 --- a/cqm-models.gemspec +++ b/cqm-models.gemspec @@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) Gem::Specification.new do |spec| spec.name = 'cqm-models' - spec.version = '4.1.2' + spec.version = '4.2.0' spec.authors = ['aholmes@mitre.org', 'mokeefe@mitre.org', 'lades@mitre.org'] spec.summary = 'Mongo models that correspond to the QDM specification.' From 6105f7aa072f7493d5feea1727f54d03e82623fd Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Thu, 22 Feb 2024 16:21:03 -0500 Subject: [PATCH 6/9] Explicitly set Timezone to UTC --- spec/spec_helper.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index df4464d1..04851b06 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,6 +6,9 @@ Mongoid.load!('config/mongoid.yml', :test) +# Seems new version of mongoid (and/or Mongo) doesn't default to UTC. +Time.zone = 'UTC' + RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' From 5545390db85908aed0a7961f97a9542c7f07ecf3 Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Thu, 22 Feb 2024 16:44:54 -0500 Subject: [PATCH 7/9] Found a version of rubocop that supports ruby 3.x. Formatting changes to appease rubocop. --- app/models/qdm/attributes/identifier.rb | 3 +-- cqm-models.gemspec | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/qdm/attributes/identifier.rb b/app/models/qdm/attributes/identifier.rb index 43182737..061df1c2 100644 --- a/app/models/qdm/attributes/identifier.rb +++ b/app/models/qdm/attributes/identifier.rb @@ -6,7 +6,6 @@ class Identifier < Attribute field :value, type: String field :qdmVersion, type: String, default: '5.6' - validates_uniqueness_of :value, conditions: -> {where(namingSystem: CQM::Provider::NPI_OID)} - + validates_uniqueness_of :value, conditions: -> { where(namingSystem: CQM::Provider::NPI_OID) } end end diff --git a/cqm-models.gemspec b/cqm-models.gemspec index 97962e69..a2d34734 100644 --- a/cqm-models.gemspec +++ b/cqm-models.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rails', '~> 7.1' spec.add_development_dependency 'rake', '~> 12.3.3' spec.add_development_dependency 'rspec', '~> 3.0' - spec.add_development_dependency 'rubocop', '~> 0.54.0' + spec.add_development_dependency 'rubocop', '~> 0.63.0' spec.add_development_dependency 'simplecov' end From 8179d3d466f4992358c712fabb4d2b2a94e2f56d Mon Sep 17 00:00:00 2001 From: dczulada Date: Fri, 1 Mar 2024 10:24:41 -0500 Subject: [PATCH 8/9] Rails 7 mongoid 8 dsc (#218) * additional mongoid updates * remove check for validate_generator --- .github/workflows/ci.yml | 1 - app/models/cqm/individual_result.rb | 4 ++-- app/models/cqm/measure.rb | 2 +- app/models/cqm/patient.rb | 2 +- app/models/qdm/basetypes/data_element.rb | 9 +++++++-- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c2b1434..b86bd0ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,4 +53,3 @@ jobs: run: | ./bin/validate_dist.sh ./bin/validate_browser.sh - ./bin/validate_generator.sh diff --git a/app/models/cqm/individual_result.rb b/app/models/cqm/individual_result.rb index 0e6cbc81..d905ae29 100644 --- a/app/models/cqm/individual_result.rb +++ b/app/models/cqm/individual_result.rb @@ -35,8 +35,8 @@ class IndividualResult field :state, type: String, default: 'queued' # Relations to other model classes - belongs_to :measure - belongs_to :patient + belongs_to :measure, class_name: 'CQM::Measure', inverse_of: :calculation_result + belongs_to :patient, class_name: 'CQM::Patient', inverse_of: :calculation_result # Convert the stored array into a hash between clause and result def clause_results_by_clause diff --git a/app/models/cqm/measure.rb b/app/models/cqm/measure.rb index dd184d6e..34ca8400 100644 --- a/app/models/cqm/measure.rb +++ b/app/models/cqm/measure.rb @@ -73,7 +73,7 @@ class Measure # hence the 'inverse_of: nil') has_and_belongs_to_many :value_sets, inverse_of: nil - has_many :calculation_results, class_name: 'CQM::IndividualResult' + has_many :calculation_results, class_name: 'CQM::IndividualResult', inverse_of: :measure def all_stratifications population_sets.flat_map(&:stratifications) diff --git a/app/models/cqm/patient.rb b/app/models/cqm/patient.rb index 481eabed..d14d8471 100644 --- a/app/models/cqm/patient.rb +++ b/app/models/cqm/patient.rb @@ -11,7 +11,7 @@ class Patient has_and_belongs_to_many :providers, class_name: 'CQM::Provider' embeds_one :qdmPatient, class_name: 'QDM::Patient', autobuild: true - has_many :calculation_results, class_name: 'CQM::IndividualResult' + has_many :calculation_results, class_name: 'CQM::IndividualResult', inverse_of: :patient # Include '_type' in any JSON output. This is necessary for deserialization. def to_json(options = nil) diff --git a/app/models/qdm/basetypes/data_element.rb b/app/models/qdm/basetypes/data_element.rb index f353ae43..b47c4ffb 100644 --- a/app/models/qdm/basetypes/data_element.rb +++ b/app/models/qdm/basetypes/data_element.rb @@ -68,8 +68,13 @@ def shift_dates(seconds) send(field + '=', (send(field).to_time + seconds.seconds).to_datetime) if send(field).is_a? Time send(field + '=', send(field).shift_dates(seconds)) if (send(field).is_a? Interval) || (send(field).is_a? DataElement) - # Special case for facility locations - if field == 'facilityLocations' + # Special case for facility locations and result + if field == 'result' + result = send(field) + unless result.nil? || (!result.is_a? Time) + send(field + '=', (result.to_time + seconds.seconds).to_datetime) + end + elsif field == 'facilityLocations' send(field).each do |facility_location| shift_facility_location_dates(facility_location, seconds) end From 95a2129f27057cb1df1d378f9ffca3714b8a1088 Mon Sep 17 00:00:00 2001 From: Joseph Kotanchik Date: Tue, 5 Mar 2024 15:15:29 -0500 Subject: [PATCH 9/9] Handling of date shifting on `result` is taken care of in the data element base type, making this `if` branch duplicative. --- app/models/qdm/basetypes/data_element.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/models/qdm/basetypes/data_element.rb b/app/models/qdm/basetypes/data_element.rb index b47c4ffb..f353ae43 100644 --- a/app/models/qdm/basetypes/data_element.rb +++ b/app/models/qdm/basetypes/data_element.rb @@ -68,13 +68,8 @@ def shift_dates(seconds) send(field + '=', (send(field).to_time + seconds.seconds).to_datetime) if send(field).is_a? Time send(field + '=', send(field).shift_dates(seconds)) if (send(field).is_a? Interval) || (send(field).is_a? DataElement) - # Special case for facility locations and result - if field == 'result' - result = send(field) - unless result.nil? || (!result.is_a? Time) - send(field + '=', (result.to_time + seconds.seconds).to_datetime) - end - elsif field == 'facilityLocations' + # Special case for facility locations + if field == 'facilityLocations' send(field).each do |facility_location| shift_facility_location_dates(facility_location, seconds) end