diff --git a/Gemfile.lock b/Gemfile.lock index a3b9150f4..44e08ab34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -183,7 +183,7 @@ GEM crack (1.0.1) bigdecimal rexml - crass (1.0.6) + crass (1.0.7) crawler_detect (1.2.11) qonfig (>= 0.24) csl (2.2.1) @@ -196,8 +196,9 @@ GEM time (< 1.0) csl-styles (2.0.2) csl (~> 2.0) - css_parser (2.2.0) + css_parser (3.0.0) addressable + ssrf_filter (~> 1.5) csv (3.3.5) dalli (5.0.2) logger @@ -476,7 +477,7 @@ GEM minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) - msgpack (1.8.0) + msgpack (1.8.3) multi_json (1.21.1) multipart-post (2.4.1) mysql2 (0.5.7) @@ -729,6 +730,7 @@ GEM sparql-client (3.3.0) net-http-persistent (~> 4.0, >= 4.0.2) rdf (~> 3.3) + ssrf_filter (1.5.0) string_pattern (2.4.0) regexp_parser (~> 2.5, >= 2.5.0) stringio (3.2.0) @@ -864,4 +866,4 @@ DEPENDENCIES webmock (~> 3.26, >= 3.26.2) BUNDLED WITH - 2.6.9 + 4.0.14 diff --git a/app/controllers/contacts_controller.rb b/app/controllers/contacts_controller.rb index 780902115..3b42dacce 100644 --- a/app/controllers/contacts_controller.rb +++ b/app/controllers/contacts_controller.rb @@ -7,8 +7,6 @@ class ContactsController < ApplicationController before_action :authenticate_user! before_action :set_include - after_action :set_provider_contacts, only: %i[create update] - after_action :remove_provider_contacts, only: %i[destroy] load_and_authorize_resource def index @@ -122,12 +120,16 @@ def create @contact = Contact.new(safe_params) authorize! :create, @contact + @contact.role_name = [] if @contact.role_name.nil? + if @contact.save options = {} options[:include] = @include options[:is_collection] = false options[:params] = { current_ability: current_ability, detail: true } + @contact.set_provider_contacts + render( json: ContactSerializer.new(@contact, options).serializable_hash.to_json, status: :created @@ -140,12 +142,17 @@ def create end def update - if @contact.update(safe_params) + @contact.assign_attributes(safe_params) + @contact.role_name = [] if @contact.role_name.nil? + + if @contact.save options = {} options[:include] = @include options[:is_collection] = false options[:params] = { current_ability: current_ability, detail: true } + @contact.set_provider_contacts + render( json: ContactSerializer.new(@contact, options).serializable_hash.to_json, status: :ok @@ -160,6 +167,7 @@ def update # don't delete, but set deleted_at timestamp def destroy if @contact.update(deleted_at: Time.zone.now) + remove_provider_contacts head :no_content else # Rails.logger.error @contact.errors.inspect @@ -190,34 +198,63 @@ def set_include end private +=begin def set_provider_contacts - if @contact.valid? - Contact.roles.each do | role | - if @contact.has_role?(role) - @contact.set_provider_role(role, { 'email': @contact.email, 'given_name': @contact.given_name, 'family_name': @contact.family_name }) - elsif @contact.has_provider_role?(role) - @contact.set_provider_role(role, nil) - end - end + if @contact.valid? && @contact.provider.present? + contacts = @contact.provider.contacts.where(deleted_at: nil) + provider = @contact.provider # Make sure no other contact with this provider claims these roles. - @contact.provider.contacts.each do | contact | + contacts.each do | contact | if !@contact.is_me?(contact) - if contact.remove_roles!(Array.wrap(@contact.role_name)) - contact.update_attribute("role_name", contact.role_name) + old_role_name = contact.role_name.present? ? contact.role_name : [] + new_role_name = (contact.role_name.present? ? contact.role_name : []) - (@contact.role_name.present? ? @contact.role_name : []) + if old_role_name.sort != new_role_name.sort + contact.update_column("role_name", new_role_name) + end + end + end + + # Clear provider role associations. + Contact.roles.each do | role | + @contact.set_provider_role!(role, nil) + end + + # Reset provider role associations. + contacts.each do |contact| + contact&.role_name&.each do | role | + if contact.has_role?(role) + contact.set_provider_role!(role, { 'email': contact.email || nil, 'given_name': contact.given_name || nil, 'family_name': contact.family_name || nil }) end end end + + # Send provider export message. (Ignore if record was created/updated via Salesforce API) + provider.save + provider.send_provider_export_message(provider.to_jsonapi.merge(slack_output: true)) if !provider.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + + # Send contact export messages. (Ignore if record was created/updated via Salesforce API) + contacts.each do |contact| + contact.save + contact.send_contact_export_message(contact.to_jsonapi.merge(slack_output: true)) if !contact.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + end end end +=end def remove_provider_contacts Array.wrap(@contact.role_name).each do | role | if @contact.has_provider_role?(role) - @contact.set_provider_role(role, nil) + @contact.set_provider_role!(role, nil) end end - @contact.update_attribute("role_name", []) + @contact.role_name = [] + + @contact.provider.save + @contact.provider.send_provider_export_message(@contact.provider.to_jsonapi.merge(slack_output: true)) if !@contact.provider.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + + @contact.save + @contact.send_contact_export_message(@contact.to_jsonapi.merge(slack_output: true)) if !@contact.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") end def safe_params diff --git a/app/controllers/providers_controller.rb b/app/controllers/providers_controller.rb index 42ce30924..7fab3953e 100644 --- a/app/controllers/providers_controller.rb +++ b/app/controllers/providers_controller.rb @@ -202,6 +202,8 @@ def create options[:is_collection] = false options[:params] = { current_ability: current_ability, detail: true } + @provider.set_provider_contacts + render( json: ProviderSerializer.new(@provider, options).serializable_hash.to_json, status: :ok @@ -219,6 +221,8 @@ def update options[:is_collection] = false options[:params] = { current_ability: current_ability, detail: true } + @provider.set_provider_contacts + render( json: ProviderSerializer.new(@provider, options).serializable_hash.to_json, status: :ok @@ -336,6 +340,59 @@ def set_provider end private +=begin + def set_provider_contacts + if @provider.valid? + contacts = @provider.contacts.where(deleted_at: nil).to_a + + # Clear contact role associations for this provider. + contacts.each { |contact| + contact.role_name = [] + } + + # Reset contact role associations to the new ones for this provider. + Contact.roles.each do | target_role | + target_role_name = target_role + "_contact" + contact_data = @provider.send(target_role_name) + + if contact_data.present? && contact_data["email"].present? + target_email = contact_data["email"] + target_given_name = contact_data["given_name"] || nil + target_family_name = contact_data["family_name"] || nil + + # Find matching contact; if none exists, build it and add it to our set of contacts for this provider. + contact = contacts.find { |c| c.email.downcase == target_email.downcase } + + if contact.nil? + contact = @provider.contacts.build(email: target_email) + contact.role_name = [] + contacts << contact + end + + contact.role_name |= [ target_role ] + contact.given_name = target_given_name + contact.family_name = target_family_name + end + end + + # Send provider export message. (Ignore if record was created/updated via Salesforce API) + @provider.save + @provider.send_provider_export_message(@provider.to_jsonapi.merge(slack_output: true)) if !@provider.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + + # We might have added contacts to what was originally an empty set. + if contacts.empty? + contacts = @provider.contacts.where(deleted_at: nil).to_a + end + + # Send contact export messages. (Ignore if record was created/updated via Salesforce API) + contacts.each do |contact| + contact.save + contact.send_contact_export_message(contact.to_jsonapi.merge(slack_output: true)) if !contact.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + end + end + end +=end + def safe_params if params[:data].blank? fail JSON::ParserError, diff --git a/app/models/concerns/indexable.rb b/app/models/concerns/indexable.rb index 0d24a9253..5a4fe803f 100644 --- a/app/models/concerns/indexable.rb +++ b/app/models/concerns/indexable.rb @@ -47,12 +47,8 @@ module Indexable end end # ignore if record was created via Salesforce API - elsif instance_of?(Provider) && !from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") - send_provider_export_message(to_jsonapi.merge(slack_output: true)) elsif instance_of?(Client) && !from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") send_client_export_message(to_jsonapi.merge(slack_output: true)) - elsif instance_of?(Contact) && !from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") - send_contact_export_message(to_jsonapi.merge(slack_output: true)) end end diff --git a/app/models/contact.rb b/app/models/contact.rb index b758411fd..472a9bcce 100644 --- a/app/models/contact.rb +++ b/app/models/contact.rb @@ -457,6 +457,14 @@ def remove_roles!(roles = []) self.changed? end + def remove_roles(roles = []) + role_name = Array.wrap(self.role_name) + roles.each do | role | + role_name.delete(role) + end + role_name + end + def is_me?(contact = nil) uid == contact.uid end @@ -482,22 +490,65 @@ def has_provider_role?(role) end end - def set_provider_role(role, contact) + def set_provider_role!(role, contact = nil) + # puts "**setting provider role #{role} contact to #{contact.email} for provider #{provider.symbol}" case role when "voting" - provider.update_attribute("voting_contact", contact) + provider.voting_contact = contact when "billing" - provider.update_attribute("billing_contact", contact) + provider.billing_contact = contact when "secondary_billing" - provider.update_attribute("secondary_billing_contact", contact) + provider.secondary_billing_contact = contact when "service" - provider.update_attribute("service_contact", contact) + provider.service_contact = contact when "secondary_service" - provider.update_attribute("secondary_service_contact", contact) + provider.secondary_service_contact = contact when "technical" - provider.update_attribute("technical_contact", contact) + provider.technical_contact = contact when "secondary_technical" - provider.update_attribute("secondary_technical_contact", contact) + provider.secondary_technical_contact = contact + end + end + + def set_provider_contacts + if self.valid? && self.provider.present? + contacts = self.provider.contacts.where(deleted_at: nil) + provider = self.provider + + # Make sure no other contact with this provider claims these roles. + contacts.each do | contact | + if !self.is_me?(contact) + old_role_name = contact.role_name.present? ? contact.role_name : [] + new_role_name = (contact.role_name.present? ? contact.role_name : []) - (self.role_name.present? ? self.role_name : []) + if old_role_name.sort != new_role_name.sort + contact.update_column("role_name", new_role_name) + end + end + end + + # Clear provider role associations. + Contact.roles.each do | role | + self.set_provider_role!(role, nil) + end + + # Reset provider role associations. + contacts.each do |contact| + contact&.role_name&.each do | role | + if contact.has_role?(role) + contact.set_provider_role!(role, { 'email': contact.email || nil, 'given_name': contact.given_name || nil, 'family_name': contact.family_name || nil }) + end + end + end + + # Send provider export message. (Ignore if record was created/updated via Salesforce API) + provider.save + provider.send_provider_export_message(provider.to_jsonapi.merge(slack_output: true)) if !provider.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + + # Send contact export messages. (Ignore if record was created/updated via Salesforce API) + contacts.each do |contact| + contact.save + contact.send_contact_export_message(contact.to_jsonapi.merge(slack_output: true)) if !contact.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + end end end diff --git a/app/models/provider.rb b/app/models/provider.rb index 9ffb29c22..6c58375ca 100644 --- a/app/models/provider.rb +++ b/app/models/provider.rb @@ -183,6 +183,7 @@ class Provider < ApplicationRecord before_save { self.updated = Time.zone.now.utc.iso8601 } accepts_nested_attributes_for :prefixes + accepts_nested_attributes_for :contacts # use different index for testing if Rails.env.test? @@ -917,6 +918,58 @@ def self.export(query: nil) "#{i} providers exported." end + def set_provider_contacts + if self.valid? + contacts = self.contacts.where(deleted_at: nil).to_a + + # Clear contact role associations for this provider. + contacts.each { |contact| + contact.role_name = [] + } + + # Reset contact role associations to the new ones for this provider. + Contact.roles.each do | target_role | + target_role_name = target_role + "_contact" + contact_data = self.send(target_role_name) + + if contact_data.present? && contact_data["email"].present? + target_email = contact_data["email"] + target_given_name = contact_data["given_name"] || nil + target_family_name = contact_data["family_name"] || nil + + # Find matching contact; if none exists, build it and add it to our set of contacts for this provider. + contact = contacts.find { |c| c.email.downcase == target_email.downcase } + + if contact.nil? + contact = self.contacts.build(email: target_email) + contact.role_name = [] + contacts << contact + end + + contact.role_name |= [ target_role ] + contact.given_name = target_given_name + contact.family_name = target_family_name + end + end + + # Send provider export message. (Ignore if record was created/updated via Salesforce API) + self.save + self.send_provider_export_message(self.to_jsonapi.merge(slack_output: true)) if !self.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + + # We might have added contacts to what was originally an empty set. + if contacts.empty? + contacts = self.contacts.where(deleted_at: nil).to_a + end + + # Send contact export messages. (Ignore if record was created/updated via Salesforce API) + contacts.each do |contact| + contact.save + contact.send_contact_export_message(contact.to_jsonapi.merge(slack_output: true)) if !contact.from_salesforce && (Rails.env.production? || ENV["SQS_PREFIX"] == "stage") + end + end + end + + private def set_region r = ISO3166::Country[country_code].world_region if country_code.present?