From 997d1e0f347d47a0d99e3da551f3f2743d6c7379 Mon Sep 17 00:00:00 2001 From: Candice Cirbo Date: Tue, 17 Dec 2024 16:19:04 -0700 Subject: [PATCH 1/6] Feat: updated contact controller and serializer --- app/controllers/api/v1/contacts_controller.rb | 32 +++++++++++++++++++ app/serializers/contacts_serializer.rb | 18 +++++++++++ config/routes.rb | 7 ++-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/contacts_controller.rb b/app/controllers/api/v1/contacts_controller.rb index 9b7bcc3..aafd27c 100644 --- a/app/controllers/api/v1/contacts_controller.rb +++ b/app/controllers/api/v1/contacts_controller.rb @@ -4,6 +4,17 @@ class ContactsController < ApplicationController before_action :authenticate_user def index + if params[:company_id] + company = @current_user.companies.find_by(id: params[:id]) + authorize company + + if company + contacts = company.contacts + render json: { company: CompanySerializer.new(company), contacts: ContactSerializer.new(contacts) } + else + render json: { error: "Company not found or unauthorized access" }, status: :not_found + end + else authorize Contact contacts = @current_user.contacts if contacts.empty? @@ -12,10 +23,31 @@ def index render json: ContactsSerializer.new(contacts), status: :ok end end + end + + # def show + # contact = @current_user.contacts.find_by(id: params[:id]) + # authorize contact + + # if contact + # render json: ContactsSerializer.new(contact), status: :ok + # else + # render json: { error: "Contact not found or unauthorized access" }, status: :not_found + # end + # end def create authorize Contact + if params[:company_id] + company = @current_user.companies.find_by(id: params[:company_id]) + if company + contact = company.contacts.new(contact_params.merge(user_id: @current_user.id)) + else + return render json: { error: "Company not found" }, status: :not_found + end + else contact = @current_user.contacts.new(contact_params) + end if contact.save render json: ContactsSerializer.new(contact), status: :created else diff --git a/app/serializers/contacts_serializer.rb b/app/serializers/contacts_serializer.rb index 19e0f80..d87ad14 100644 --- a/app/serializers/contacts_serializer.rb +++ b/app/serializers/contacts_serializer.rb @@ -1,4 +1,22 @@ class ContactsSerializer include JSONAPI::Serializer attributes :first_name, :last_name, :company_id, :email, :phone_number, :notes, :user_id + + attribute :company do |object| + company = object.company + if company + { + id: company.id, + name: company.name, + website: company.website, + street_address: company.street_address, + city: company.city, + state: company.state, + zip_code: company.zip_code, + notes: company.notes + } + else + nil + end + end end diff --git a/config/routes.rb b/config/routes.rb index c748e5b..f0fc3d1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,8 +12,10 @@ resources :users, only: [:create, :index, :show, :update] do resources :job_applications, only: [:create, :index, :show] - resources :companies, only: [:create, :index] - resources :contacts, only: [:create, :index] + resources :companies, only: [:create, :index] do + resources :contacts, only: [:create, :index] + end + resources :contacts, only: [:index, :create] resource :dashboard, only: :show end @@ -21,3 +23,4 @@ end end end + From c3dfb21712e8c10921b106ff5b2d46e96c600bcf Mon Sep 17 00:00:00 2001 From: Renee Messersmith Date: Wed, 18 Dec 2024 16:51:36 -0500 Subject: [PATCH 2/6] Test: create action 100 percent test coverage --- spec/requests/api/v1/contacts/create_spec.rb | 22 ++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/requests/api/v1/contacts/create_spec.rb b/spec/requests/api/v1/contacts/create_spec.rb index 685aab7..80e47a5 100644 --- a/spec/requests/api/v1/contacts/create_spec.rb +++ b/spec/requests/api/v1/contacts/create_spec.rb @@ -81,6 +81,18 @@ expect(json[:attributes][:last_name]).to eq("Smith") expect(json[:attributes][:phone_number]).to eq("123-555-6789") end + + it 'creates a contact when given the company ID, returns a 201' do + minimal_params = { contact: {first_name: "John", last_name: "Smith" } } + post api_v1_user_company_contacts_path(user_id: @user1.id, company_id: @company.id), params: minimal_params , headers: { "Authorization" => "Bearer #{@token}" }, as: :json + + expect(response).to have_http_status(:created) + json = JSON.parse(response.body, symbolize_names: true)[:data] + + expect(json[:attributes][:first_name]).to eq("John") + expect(json[:attributes][:last_name]).to eq("Smith") + expect(json[:attributes][:company][:name]).to eq("Turing") + end end context "when the request is invalid - Sad Paths" do @@ -154,6 +166,16 @@ json = JSON.parse(response.body, symbolize_names: true) expect(json[:error]).to eq("Phone number must be in the format '555-555-5555'") end + + it 'returns a 404 error when a company is not found by ID number' do + minimal_params = { contact: {first_name: "John", last_name: "Smith" } } + post api_v1_user_company_contacts_path(user_id: @user1.id, company_id: 99999), params: minimal_params , headers: { "Authorization" => "Bearer #{@token}" }, as: :json + + expect(response).to have_http_status(:not_found) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:error]).to eq("Company not found") + end end context "edge cases - Sad Paths" do From df329bef21d6da14b6797e5bd6d84eab83c71955 Mon Sep 17 00:00:00 2001 From: Renee Messersmith Date: Wed, 18 Dec 2024 17:16:20 -0500 Subject: [PATCH 3/6] Fix: spacing in controller --- app/controllers/api/v1/contacts_controller.rb | 70 ++++++++----------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/app/controllers/api/v1/contacts_controller.rb b/app/controllers/api/v1/contacts_controller.rb index aafd27c..51601b4 100644 --- a/app/controllers/api/v1/contacts_controller.rb +++ b/app/controllers/api/v1/contacts_controller.rb @@ -5,39 +5,28 @@ class ContactsController < ApplicationController def index if params[:company_id] - company = @current_user.companies.find_by(id: params[:id]) - authorize company - - if company - contacts = company.contacts - render json: { company: CompanySerializer.new(company), contacts: ContactSerializer.new(contacts) } - else - render json: { error: "Company not found or unauthorized access" }, status: :not_found - end - else - authorize Contact - contacts = @current_user.contacts - if contacts.empty? - render json: { data: [], message: "No contacts found" }, status: :ok + company = @current_user.companies.find_by(id: params[:company_id]) + if company + authorize company + contacts = company.contacts + render json: { company: CompanySerializer.new(company), contacts: ContactsSerializer.new(contacts) } + else + skip_authorization + render json: { error: "Company not found or unauthorized access" }, status: :not_found + end else - render json: ContactsSerializer.new(contacts), status: :ok + authorize Contact + contacts = @current_user.contacts + if contacts.empty? + render json: { data: [], message: "No contacts found" }, status: :ok + else + render json: ContactsSerializer.new(contacts), status: :ok + end end end - end - - # def show - # contact = @current_user.contacts.find_by(id: params[:id]) - # authorize contact - - # if contact - # render json: ContactsSerializer.new(contact), status: :ok - # else - # render json: { error: "Contact not found or unauthorized access" }, status: :not_found - # end - # end - def create - authorize Contact + def create + authorize Contact if params[:company_id] company = @current_user.companies.find_by(id: params[:company_id]) if company @@ -46,20 +35,21 @@ def create return render json: { error: "Company not found" }, status: :not_found end else - contact = @current_user.contacts.new(contact_params) + contact = @current_user.contacts.new(contact_params) + end + + if contact.save + render json: ContactsSerializer.new(contact), status: :created + else + render json: { error: contact.errors.full_messages.to_sentence }, status: :unprocessable_entity end - if contact.save - render json: ContactsSerializer.new(contact), status: :created - else - render json: { error: contact.errors.full_messages.to_sentence }, status: :unprocessable_entity - end - end + end - private + private - def contact_params - params.require(:contact).permit(:first_name, :last_name, :company_id, :email, :phone_number, :notes) - end + def contact_params + params.require(:contact).permit(:first_name, :last_name, :company_id, :email, :phone_number, :notes) + end end end end From 3d6bb7ad5aba8f4c8d02c4423d8509b240eb2412 Mon Sep 17 00:00:00 2001 From: Renee Messersmith Date: Wed, 18 Dec 2024 17:17:10 -0500 Subject: [PATCH 4/6] Test: contacts controller at full test coverage --- spec/requests/api/v1/contacts/index_spec.rb | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/requests/api/v1/contacts/index_spec.rb b/spec/requests/api/v1/contacts/index_spec.rb index ca4a07e..2982bdd 100644 --- a/spec/requests/api/v1/contacts/index_spec.rb +++ b/spec/requests/api/v1/contacts/index_spec.rb @@ -40,11 +40,27 @@ expect(json[:data]).to eq([]) expect(json[:message]).to eq("No contacts found") end + + it "should return 200 and all contacts associated with a company" do + get api_v1_user_company_contacts_path(user_id: @user.id, company_id: @company.id), headers: { "Authorization" => "Bearer #{@token}" }, as: :json + + expect(response).to be_successful + expect(response).to have_http_status(:ok) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:company][:data][:attributes][:name]).to eq("Turing") + expect(json[:contacts][:data][0][:attributes][:last_name]).to eq("Smith") + + end end context "Sad 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) + user_params = { email: "its_me", password: "reallyGoodPass" } + post api_v1_sessions_path, params: user_params, as: :json + @token = JSON.parse(response.body)["token"] end it "returns a 403 and an error message if no token is provided" do @@ -76,6 +92,16 @@ expect(json[:error]).to eq("Not authenticated") end + + it "returns a 404 and an error message if company ID is not found" do + get api_v1_user_company_contacts_path(user_id: @user.id, company_id: 99999), headers: { "Authorization" => "Bearer #{@token}" }, as: :json + + expect(response).to_not be_successful + expect(response).to have_http_status(:not_found) + json = JSON.parse(response.body, symbolize_names: true) + + expect(json[:error]).to eq("Company not found or unauthorized access") + end end end end \ No newline at end of file From e86fa4ce6962d67ae8cd038eda5154eaabd72b83 Mon Sep 17 00:00:00 2001 From: Candice Cirbo Date: Thu, 19 Dec 2024 15:04:34 -0700 Subject: [PATCH 5/6] Updated README --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 191eb6a..18501c8 100644 --- a/README.md +++ b/README.md @@ -644,6 +644,43 @@ Status: 201 created } ``` +#### Create a contact with a company name from the dropdown box +Request: +``` +POST api/v1/users/2/companies/2/contacts + +Authorization: Bearer Token - put in token for user + +raw json body with all fields: + +{ + "data": [ + { + "id": "1", + "type": "contacts", + "attributes": { + "first_name": "Jane", + "last_name": "Doe", + "company_id": 2, + "email": "", + "phone_number": "", + "notes": "", + "user_id": 2, + "company": { + "id": 2, + "name": "Future Designs LLC", + "website": "https://futuredesigns.com", + "street_address": "456 Future Blvd", + "city": "Austin", + "state": "TX", + "zip_code": "73301", + "notes": "Submitted application for the UI Designer role." + } + } + } + ] + }, + ``` #### Contact Errors 401 Error Response if no token provided: From d7e905a42d51ea3024aa7a44cecd1ae6f6d42f7c Mon Sep 17 00:00:00 2001 From: Renee Messersmith Date: Thu, 19 Dec 2024 17:56:23 -0500 Subject: [PATCH 6/6] ReadMe: Updated to highlight the company ID in the URL --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 18501c8..a3b1af0 100644 --- a/README.md +++ b/README.md @@ -601,7 +601,7 @@ Successful response for users without saved contacts: ``` #### Create a contact with required and optional fields. -New contacts require a unique first and last name. All other fields are optional. +***New contacts require a unique first and last name. All other fields are optional.*** Request: ``` @@ -645,9 +645,11 @@ Status: 201 created ``` #### Create a contact with a company name from the dropdown box +***New contacts with company name require a unique first and last name, and company ID in the URI. All other fields are optional.*** + Request: ``` -POST api/v1/users/2/companies/2/contacts +POST api/v1/users/:user_id/companies/:company_id/contacts Authorization: Bearer Token - put in token for user