簡體   English   中英

為什么我的生產日志顯示ActiveRecord :: RecordNotUnique錯誤,但是我的本地binding.pry向我顯示ActiveModel :: Errors?

[英]Why does my production logs show an ActiveRecord::RecordNotUnique error but my local binding.pry shows me an ActiveModel::Errors?

我有一個注冊表單,其中包含對模型和數據庫的驗證,以防止重復輸入。

我在生產中使用honeybadger記錄錯誤。 當用戶嘗試使用相同的憑據進行注冊時,honeybadger報告ActiveRecord::RecordNotUnique: Mysql2::Error: Duplicate entry for .... 錯誤的其余部分包含我要防止的PII(我在一家金融公司工作,所以這是合規性問題)。

我的解決方案是將Model.create包裝在救援塊中,並在報告之前自定義honeybadger錯誤。 我為此寫了一些rspec,但一直失敗。 當我在塊中包含binding.pry時,我可以看到重復項創建了一個錯誤,但該錯誤是ActiveModel::Errors的實例。 現在,我可以嘗試挽救ActiveModel::Errors錯誤,但是恐怕ActiveRecord::RecordNotUnique錯誤仍然會記錄在生產環境中,這就是我要更改的內容。

我不知道:

1)為什么生產和本地會顯示不同類型的錯誤?

2)為了從生產日志中提供自定義錯誤消息(並隱藏PII),我將不得不從哪類錯誤中解救出來。

任何和所有幫助將不勝感激。 謝謝!

我嘗試過的一些整體解決方案是:

1)使用准備好的陳述。 但是,這沒有用,因為我使用的ActiveRecord(4.2.11)版本沒有准備好的語句。

2)在此之后,利用Honeybadger的能力來忽略錯誤: https ://docs.honeybadger.io/lib/ruby/getting-started/ignoring-errors.html,但是團隊決定我們不想關閉該錯誤。完全。

# /app/models/prime_signup.rb
class PrimeSignup < ActiveRecord::Base
 validates_presence_of :first_name, :last_name, :email
 validates :email, uniqueness: true

  def person
    @person ||= Person.find_by(email: email)
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end

# /db/schema.rb
create_table "prime_signups", force: :cascade do |t|
    t.string   "first_name",   limit: 255
    t.string   "last_name",    limit: 255
    t.string   "email",        limit: 255
    t.string   "phone_number", limit: 20
  end

  add_index "prime_signups", ["email"], name: "index_prime_signups_on_email", unique: true, using: :btree

# /app/controllers/api/v1/prime_signups_controller.rb

class API::V1::PrimeSignupsController < API::V1Controller
 // omitting skip_before_actions for brevity

  def create
    return render_forward_compatible_json_error(json_error, resource) unless resource.valid?
    service.perform
    render json: resource, serializer: API::V1::PrimeSignupSerializer, status: 201
  end

  private

  def resource_params
    params.require(:prime_signup).permit(:first_name, :last_name, :email, :phone_number,
                                         :utm_source, :utm_medium, :utm_campaign, :utm_term,
                                         :utm_content)
  end

  def resource # This is the method I'm trying to rescue the error from
    binding.pry 
    begin
      @resource ||= PrimeSignup.create(resource_params)
    rescue ActiveRecord::RecordNotUnique => e  # This is how I'm trying to customize the error
      Honeybadger.notify(
        error,
        error_message: 'Duplicate Entry',
      )
    end
  end

  def json_error
    JSONExceptions::InvalidFieldValues.new(detail: resource_errors)
  end

  def resource_errors
    resource.errors.messages.map {|field, message| "#{field} #{message.join}."}.join(" ")
  end

  def service
    ::Services::PrimeSignupCreation.new(resource)
  end

  def render_forward_compatible_json_error(error, resource)
    json_error_format = { errors: [error.to_json] }
    resource_key = resource.class.name.snakecase
    old_error_format = {resource_key => resource.errors.details}
    render json: json_error_format.merge(old_error_format), status: error.status
  end
end

require 'rails_helper'

describe 'API::V1::PrimeSignups', type: :request do
  describe 'POST /api/v1/prime_signups' do
    context 'duplicate entry' do
      it 'raises a custom honeybadger error' do

        prime_signup_params = {
          prime_signup: {
            first_name: "Walter",
            last_name: "White",
            email: "walter@white.com",
            phone_number: '123456789'
          },
          authenticity_token: 'authenticated',
          format: :json
        }

        expect(Honeybadger).to receive(:notify)
        VCR.use_cassette('/api/v1/prime_signups') do
          post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
          post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
        end
        expect(response.status).to eq(422)

        # VCR.use_cassette('/api/v1/prime_signups') do
        #   post '/api/v1/prime_signups', prime_signup_params.to_json, { "CONTENT_TYPE"=>"application/json" }
        # end
      end
    end
  end
end

我希望從中挽救的錯誤屬於ActiveRecord::RecordNotUnique類型,或者記錄的生產錯誤屬於ActiveModel::Errors類型。 基本上期望兩者之間的一致性。

同樣,任何有關規格和更好格式的指南將不勝感激。 我很糟糕。

您在生產中看到ActiveRecord::RecordNotUnique但在本地看到ActiveModel::Errors原因可能是由於您未在本地復制而在生產中出現競爭條件。

也就是說,在生產中可能發生的情況是,唯一性驗證正在通過,因為試圖同時創建兩個具有重復信息的記錄,並且每個運行中的記錄都是有效的,因為它們都未在數據庫中找到現有記錄使用相同的電子郵件地址。 其中一個創建成功,然后第二個失敗,因為第一個創建在第二個查詢執行其唯一性查詢之后但無法進行插入之前被數據庫保留。

在您的測試環境中,兩次嘗試的創建是順序發生的,而不是同時發生的,因此第二次嘗試不會超過唯一性檢查。

要測試控制器的生產行為,您需要對PrimeSignup.create進行存根並將其引發ActiveRecord::RecordNotUnique錯誤。

盡管您沒有要求,但我也將為您提供替代方法的建議,因為我恰好是Honeybadger的聯合創始人之一... :)您可以將email參數添加到過濾參數列表中在您的Honeybadger配置中,那么該PII不會與其他錯誤信息一起報告。

這樣,如果Rails驗證通過驗證失敗,並且您根本不必處理救援異常,則Rails驗證不應允許保存。

def resource 
  @resource ||= PrimeSignup.find_or_initialize_by(resource_params)
    if @resource.save
      Honeybadger.notify(
        @resource.errors.full_messages,
        error_message:  @resource.errors.full_messages.join(', ')
      )
    end
  end
end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM