Gavin
Gavin

Reputation: 2834

Rails web API throwing of error

I am trying to create a web API that allows creation of FriendShip by email or phone_number.

class Api::FriendshipsController < Api::BaseController

    respond_to :json

    def create
        friend = User.where("email = ? OR phone_number = ?", params[:emailOrPhone], params[:emailOrPhone]).first # create a friend by email or phone_number
        if friend.valid? # check if the friend exists, if it does we create our new friendship
            friendship = Friendship.new
            friendship.user = current_user
            friendship.friend = friend
            if friendship.valid? # check if friendship is valid
                friendship.save # if it is, we save and return a success JSON response
                render json: {created: true}, status: 200
            else # if it's not a valid friendship, we display a error JSON response
                render json: {created: false}, status: 400
            end
        end
    end
end

Here is my FriendShip model

class Friendship < ActiveRecord::Base
    belongs_to :user
    belongs_to :friend, :class_name => "User"

    validates_uniqueness_of :user_id, scope: :friend_id, :message => '%{friend_id} is already a friend'

    validate :check_friend_and_user # prevent user trying to add him/herself as friend.

    def check_friend_and_user
      errors.add(:friend, "can't be the same as user") if user == friend
    end
end

Whenever the uniqueness constrain is violated, an error missing interpolation argument :friend_id in "%{friend_id} is already a friend" ({:model=>"Friendship", :attribute=>"User", :value=>2} given) with error code 500

How do I make it not throw an error but instead proceed to give back a 'fail json response' with status code 400

I want the caller of this API to know that they are trying to add someone that is already a friend. Getting back a status code 500 and a bunch of html does not seems to uniquely identify it. So I would like to throw a error in the form of JSON and status 200

Upvotes: 0

Views: 47

Answers (1)

engineerDave
engineerDave

Reputation: 3935

What you seem to be trying to do is determine if the friend is already associated to the User via the friendship class. Which you can simplify with a has_many :friendships association on the User object.

Also, the way you're looking up by email OR phone is IMO unnecessarily ambiguous and will be problematic if you want to separately track one or the other for other purposes. Which you seem to be wanting to do since you have them broken out into separate database columns. I think you could just put two form inputs email or phone number and only pass one along to the controller. If you must have only one then you could determine what the form submits in Javascript or something.

In that case, you'd be better off sending the type of identifier with your initial data from your form, so you can look for one or the other. So your form would explicitly send the column lookup identifier, e.g. the params would be equivalent to the ruby hash

{friendship: {email: "[email protected]"}}

With that in the params then you could do what your trying with this code

# assuming you're passing via a params hash that would look like       
# one or the other of the following
# {friendship: {email: "[email protected]"}}
# {friendship: {phone_number: "123-123-1234"}}
def create
  if current_user.friendships.find_or_create_by(friendship_params)
    render json: {created: true}, status: 200
  else # if it's not a valid friendship, we display a error JSON response
    render json: {created: false}, status: 400
  end
end

protected


def friendship_params
  require(:friendship).permit(:email, :phone_number)
end

Upvotes: 1

Related Questions