diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..23830fb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} diff --git a/README.md b/README.md index 0177739..ad07ff1 100644 --- a/README.md +++ b/README.md @@ -697,6 +697,43 @@ raw json body with all fields: ] }, ``` +#### Show a Contact that belongs to a User (not company contact) +***Ensure you Create the Contact first for the user NOT company - 2 different Routes!*** + +Request: +``` +GET http://localhost:3001/api/v1/users/:user_id/contacts/:contact_id +Authorization: Bearer Token - put in token for user +``` +Successful Response: + +``` +{ + "data": { + "id": "1", + "type": "contacts", + "attributes": { + "first_name": "Josnny", + "last_name": "Smsith", + "company_id": 1, + "email": "jonny@gmail.com", + "phone_number": "555-785-5555", + "notes": "Good contact for XYZ", + "user_id": 1, + "company": { + "id": 1, + "name": "Tech Innovators", + "website": "https://techinnovators.com", + "street_address": "123 Innovation Way", + "city": "San Francisco", + "state": "CA", + "zip_code": "94107", + "notes": "Reached out on LinkedIn, awaiting response." + } + } + } +} +``` #### Contact Errors 401 Error Response if no token provided: diff --git a/app/controllers/api/v1/contacts_controller.rb b/app/controllers/api/v1/contacts_controller.rb index febdb90..c634d20 100644 --- a/app/controllers/api/v1/contacts_controller.rb +++ b/app/controllers/api/v1/contacts_controller.rb @@ -46,6 +46,25 @@ def create end end + def show + if params[:id].blank? + render json: ErrorSerializer.format_error(ErrorMessage.new("Contact ID is missing", 400)), status: :bad_request + return + end + + user = User.find(params[:user_id]) + authorize user + + contact = Contact.find_by(id: params[:id]) + + if contact.nil? || contact.user_id != user.id + render json: ErrorSerializer.format_error(ErrorMessage.new("Contact not found", 404)), status: :not_found + else + contact_data = ContactsSerializer.new(contact) + render json: contact_data, status: :ok + end + end + private def contact_params diff --git a/app/policies/contact_policy.rb b/app/policies/contact_policy.rb index 7001753..22bdd74 100644 --- a/app/policies/contact_policy.rb +++ b/app/policies/contact_policy.rb @@ -7,7 +7,7 @@ def index? def create? admin? || user.present? end - + class Scope < ApplicationPolicy::Scope def resolve diff --git a/config/routes.rb b/config/routes.rb index f0fc3d1..cb377c7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,7 +15,7 @@ resources :companies, only: [:create, :index] do resources :contacts, only: [:create, :index] end - resources :contacts, only: [:index, :create] + resources :contacts, only: [:index, :create, :show] resource :dashboard, only: :show end diff --git a/spec/requests/api/v1/contacts/show_spec.rb b/spec/requests/api/v1/contacts/show_spec.rb new file mode 100644 index 0000000..e6c9277 --- /dev/null +++ b/spec/requests/api/v1/contacts/show_spec.rb @@ -0,0 +1,73 @@ +require "rails_helper" + +describe "Contacts Controller", type: :request do + describe "#show action" do + context "Happy Paths" do + before(:each) do + @user = User.create!(name: "Me", email: "its_me", password: "reallyGoodPass") + @company = Company.create!( + name: "Turing", website: "www.turing.com", street_address: "123 Main St", + city: "Denver", state: "CO", zip_code: "80218", user_id: @user.id + ) + @contact = Contact.create!(first_name: "John", last_name: "Smith", + company_id: @company.id, email: "123@example.com", phone_number: "123-555-6789", + notes: "Notes here...", user_id: @user.id + ) + user_params = { email: "its_me", password: "reallyGoodPass" } + post api_v1_sessions_path, params: user_params, as: :json + @token = JSON.parse(response.body)["token"] + + @user2 = User.create!(name: "Jane", email: "email", password: "Password") + user_params2 = { email: "email", password: "Password" } + post api_v1_sessions_path, params: user_params2, as: :json + @token2 = JSON.parse(response.body)["token"] + end + + it "returns 200 and provides the appropriate contact details" do + get api_v1_user_contacts_path(@user.id, @contact.id), headers: { "Authorization" => "Bearer #{@token}" }, as: :json + + expect(response).to be_successful + + json = JSON.parse(response.body, symbolize_names: true)[:data].first + + expect(json[:attributes][:first_name]).to eq("John") + expect(json[:attributes][:last_name]).to eq("Smith") + expect(json[:attributes][:email]).to eq("123@example.com") + expect(json[:attributes][:phone_number]).to eq("123-555-6789") + expect(json[:attributes][:notes]).to eq("Notes here...") + expect(json[:attributes][:company_id]).to eq(@company.id) + end + + context "Sad Paths" do + it "returns a 403 and an error message if no token is provided" do + get api_v1_user_contacts_path(@user.id, @contact.id), as: :json + + expect(response).to have_http_status(:unauthorized) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:error]).to eq("Not authenticated") + end + + it "returns a 403 and an error message if an invalid token is provided" do + get api_v1_user_contacts_path(@user.id, @contact.id), headers: { "Authorization" => "Bearer invalid.token.here" }, as: :json + + expect(response).to have_http_status(:unauthorized) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:error]).to eq("Not authenticated") + end + + it "returns a 403 and an error message if the token is expired" do + expired_token = JWT.encode({ user_id: @user.id, exp: 1.hour.ago.to_i }, Rails.application.secret_key_base, "HS256") + + get api_v1_user_contacts_path(@user.id, @contact.id), headers: { "Authorization" => "Bearer #{expired_token}" }, as: :json + + expect(response).to have_http_status(:unauthorized) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:error]).to eq("Not authenticated") + end + end + end + end +end \ No newline at end of file