3

I'm using the graphql-ruby gem and I have a mutation that updates a record. This is all working, but now I want to include the phony_rails gem to validate phone number.

The problem

As a dev with more FE experience, I'm not great with rails or ruby and I'm using this to learn. That said, I have two phone numbers that I want to validate -- home_phone_number and mobile_phone_number. My mutation argument looks like this:

# frozen_string_literal: true

module Mutations
  module Person
    class UpdatePerson < BaseMutation
      visibility_role :introspect_admin
      visibility_pundit_class PersonPolicy

      argument :id, ID, required: true
      argument :email, String, required: false
      argument :home_phone_number, String, required: false
      argument :mobile_phone_number, String, required: false

      field :person, Types::Person::Person, null: true

      def call(input = {})
        current_user = context[:current_user]
        authorize!(current_user)

        person = Person.find(input[:id])
        person.email = input[:email]
        person.home_phone_number = input[:home_phone_number]
        person.mobile_phone_number = input[:mobile_phone_number]
        person.save!

        { error: nil, person: person }
      end

      # @param [User|nil] current_user
      def authorize!(current_user)
        raise Pundit::NotAuthorizedError, 'Not allowed to update person.' unless
          PersonPolicy.new(current_user, nil).update?
      end
    end
  end
end

Now I want to add validation to home_phone_number and mobile_phone_number. I have written my tests to look like this:

context 'invalid number home phone number' do
  let(:variables) do
    {
      'input' => {
        'id' => person.id,
        'homePhoneNumber' => '123'
      }
    }
  end

  it 'should return an error if home phone is invalid' do
    expect(subject).not_to contain_graphql_errors
    expect(data_dig('error')).not_to be_nil
    expect(data_dig('error', 'error')).to eq('standard_error')
    expect(data_dig('error', 'description')).to eq('Home phone number must be valid')
  end
end

context 'invalid number mobile phone number' do
  let(:variables) do
    {
      'input' => {
        'id' => person.id,
        'mobilePhoneNumber' => '123'
      }
    }
  end

  it 'should return an error if mobile phone is invalid' do
    expect(subject).not_to contain_graphql_errors
    expect(data_dig('error')).not_to be_nil
    expect(data_dig('error', 'error')).to eq('standard_error')
    expect(data_dig('error', 'description')).to eq('Mobile phone number must be valid')
  end
end

What I've tried

What I can get working is this, but not necessarily passing my tests:

def call(input = {})
  current_user = context[:current_user]
  authorize!(current_user)

  validate_phone_numbers(input[:home_phone_number], input[:mobile_phone_number])

# ....

def validate_phone_numbers(home_phone_number, mobile_phone_number)
  phone_numbers = [home_phone_number, mobile_phone_number]

  phone_numbers.each do |contact|
    raise StandardError, 'Phone Number must be valid' if !PhonyRails.plausible_number?(contact) #would this stop execution too?
  end
end

As you can see, in doing this, I wouldn't be able to specify which is a home phone number vs mobile phone number.

I've also tried doing this one-by-one:

def validate_phone_numbers(home_phone_number, mobile_phone_number)
  home_phone_number_valid = PhonyRails.plausible_number?(home_phone_number)
  mobile_phone_number_valid = PhonyRails.plausible_number?(mobile_phone_number)

  raise StandardError, 'Home phone number must be valid' if !home_phone_number_valid
  raise StandardError, 'Mobile phone number must be valid' if !mobile_phone_number_valid

  # Stop execution
  return if !home_phone_number_valid || !mobile_phone_number_valid
end

The lines above also do not exactly work.

Some guidance would be immensely appreciated. Thank you!

nyphur
  • 2,404
  • 2
  • 24
  • 48
  • Check out [GraphQL on Rails: On the way to perfection](https://evilmartians.com/chronicles/graphql-on-rails-3-on-the-way-to-perfection). It covers validation thoroughly. – Jared Beck May 27 '22 at 15:35

1 Answers1

0

Instead of returning { error: nil, person: person } in your resolver, try returning { error: errors_array, person: person } where errors_array contains any validation error messages from phony. eg.

def call
...

  errors_array = []

  errors_array << 'Home Phone Number must be valid' if !PhonyRails.plausible_number?(input[:home_phone_number])
  errors_array << 'Mobile Phone Number must be valid' if !PhonyRails.plausible_number?(input[:mobile_phone_number])

...

{ error: errors_array, person: person }
end

Also make sure cover the case where both the mobile and home phone numbers are invalid in your specs.

I hope this helps!