From 06289b03ea2cbde25e25212dabf51299e889135a Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Sun, 24 Oct 2021 12:40:30 +0530 Subject: [PATCH] chore: Add Contact Note APIs (#3266) --- .../v1/accounts/contacts/base_controller.rb | 9 ++ .../contacts/contact_inboxes_controller.rb | 7 +- .../contacts/conversations_controller.rb | 8 +- .../v1/accounts/contacts/labels_controller.rb | 6 +- .../v1/accounts/contacts/notes_controller.rb | 32 +++++ app/models/note.rb | 7 + .../contacts/notes/create.json.jbuilder | 1 + .../contacts/notes/index.json.jbuilder | 3 + .../contacts/notes/show.json.jbuilder | 1 + .../contacts/notes/update.json.jbuilder | 1 + app/views/api/v1/models/_note.json.jbuilder | 9 ++ config/routes.rb | 1 + .../contacts/notes_controller_spec.rb | 121 ++++++++++++++++++ 13 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 app/controllers/api/v1/accounts/contacts/base_controller.rb create mode 100644 app/controllers/api/v1/accounts/contacts/notes_controller.rb create mode 100644 app/views/api/v1/accounts/contacts/notes/create.json.jbuilder create mode 100644 app/views/api/v1/accounts/contacts/notes/index.json.jbuilder create mode 100644 app/views/api/v1/accounts/contacts/notes/show.json.jbuilder create mode 100644 app/views/api/v1/accounts/contacts/notes/update.json.jbuilder create mode 100644 app/views/api/v1/models/_note.json.jbuilder create mode 100644 spec/controllers/api/v1/accounts/contacts/notes_controller_spec.rb diff --git a/app/controllers/api/v1/accounts/contacts/base_controller.rb b/app/controllers/api/v1/accounts/contacts/base_controller.rb new file mode 100644 index 000000000..15f73ab59 --- /dev/null +++ b/app/controllers/api/v1/accounts/contacts/base_controller.rb @@ -0,0 +1,9 @@ +class Api::V1::Accounts::Contacts::BaseController < Api::V1::Accounts::BaseController + before_action :ensure_contact + + private + + def ensure_contact + @contact = Current.account.contacts.find(params[:contact_id]) + end +end diff --git a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb index fa5b271bf..fdcdcaf9e 100644 --- a/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/contacts/contact_inboxes_controller.rb @@ -1,5 +1,4 @@ -class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::BaseController - before_action :ensure_contact +class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts::Contacts::BaseController before_action :ensure_inbox, only: [:create] def create @@ -13,8 +12,4 @@ class Api::V1::Accounts::Contacts::ContactInboxesController < Api::V1::Accounts: @inbox = Current.account.inboxes.find(params[:inbox_id]) authorize @inbox, :show? end - - def ensure_contact - @contact = Current.account.contacts.find(params[:contact_id]) - end end diff --git a/app/controllers/api/v1/accounts/contacts/conversations_controller.rb b/app/controllers/api/v1/accounts/contacts/conversations_controller.rb index 4ccc14729..cadfe133f 100644 --- a/app/controllers/api/v1/accounts/contacts/conversations_controller.rb +++ b/app/controllers/api/v1/accounts/contacts/conversations_controller.rb @@ -1,8 +1,8 @@ -class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::BaseController +class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts::Contacts::BaseController def index @conversations = Current.account.conversations.includes( :assignee, :contact, :inbox, :taggings - ).where(inbox_id: inbox_ids, contact_id: permitted_params[:contact_id]) + ).where(inbox_id: inbox_ids, contact_id: @contact.id) end private @@ -14,8 +14,4 @@ class Api::V1::Accounts::Contacts::ConversationsController < Api::V1::Accounts:: [] end end - - def permitted_params - params.permit(:contact_id) - end end diff --git a/app/controllers/api/v1/accounts/contacts/labels_controller.rb b/app/controllers/api/v1/accounts/contacts/labels_controller.rb index 440c378f7..228a197f8 100644 --- a/app/controllers/api/v1/accounts/contacts/labels_controller.rb +++ b/app/controllers/api/v1/accounts/contacts/labels_controller.rb @@ -1,13 +1,13 @@ -class Api::V1::Accounts::Contacts::LabelsController < Api::V1::Accounts::BaseController +class Api::V1::Accounts::Contacts::LabelsController < Api::V1::Accounts::Contacts::BaseController include LabelConcern private def model - @model ||= Current.account.contacts.find(permitted_params[:contact_id]) + @model ||= @contact end def permitted_params - params.permit(:contact_id, labels: []) + params.permit(labels: []) end end diff --git a/app/controllers/api/v1/accounts/contacts/notes_controller.rb b/app/controllers/api/v1/accounts/contacts/notes_controller.rb new file mode 100644 index 000000000..d49f3b838 --- /dev/null +++ b/app/controllers/api/v1/accounts/contacts/notes_controller.rb @@ -0,0 +1,32 @@ +class Api::V1::Accounts::Contacts::NotesController < Api::V1::Accounts::Contacts::BaseController + before_action :note, except: [:index, :create] + + def index + @notes = @contact.notes.includes(:user) + end + + def create + @note = @contact.notes.create!(note_params) + end + + def destroy + @note.destroy + head :ok + end + + def show; end + + def update + @note.update(note_params) + end + + private + + def note + @note ||= @contact.notes.find(params[:id]) + end + + def note_params + params.require(:note).permit(:content).merge({ contact_id: @contact.id, user_id: Current.user.id }) + end +end diff --git a/app/models/note.rb b/app/models/note.rb index 81613558d..fff7d3780 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -23,6 +23,7 @@ # fk_rails_... (user_id => users.id) # class Note < ApplicationRecord + before_validation :ensure_account_id validates :content, presence: true validates :account_id, presence: true validates :contact_id, presence: true @@ -31,4 +32,10 @@ class Note < ApplicationRecord belongs_to :account belongs_to :contact belongs_to :user + + private + + def ensure_account_id + self.account_id = contact&.account_id + end end diff --git a/app/views/api/v1/accounts/contacts/notes/create.json.jbuilder b/app/views/api/v1/accounts/contacts/notes/create.json.jbuilder new file mode 100644 index 000000000..b9f9e3fd6 --- /dev/null +++ b/app/views/api/v1/accounts/contacts/notes/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/note.json.jbuilder', resource: @note diff --git a/app/views/api/v1/accounts/contacts/notes/index.json.jbuilder b/app/views/api/v1/accounts/contacts/notes/index.json.jbuilder new file mode 100644 index 000000000..67327b94c --- /dev/null +++ b/app/views/api/v1/accounts/contacts/notes/index.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @notes do |note| + json.partial! 'api/v1/models/note.json.jbuilder', resource: note +end diff --git a/app/views/api/v1/accounts/contacts/notes/show.json.jbuilder b/app/views/api/v1/accounts/contacts/notes/show.json.jbuilder new file mode 100644 index 000000000..b9f9e3fd6 --- /dev/null +++ b/app/views/api/v1/accounts/contacts/notes/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/note.json.jbuilder', resource: @note diff --git a/app/views/api/v1/accounts/contacts/notes/update.json.jbuilder b/app/views/api/v1/accounts/contacts/notes/update.json.jbuilder new file mode 100644 index 000000000..b9f9e3fd6 --- /dev/null +++ b/app/views/api/v1/accounts/contacts/notes/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/note.json.jbuilder', resource: @note diff --git a/app/views/api/v1/models/_note.json.jbuilder b/app/views/api/v1/models/_note.json.jbuilder new file mode 100644 index 000000000..93a6a615e --- /dev/null +++ b/app/views/api/v1/models/_note.json.jbuilder @@ -0,0 +1,9 @@ +json.id resource.id +json.content resource.content +json.account_id json.account_id +json.contact_id json.contact_id +json.user do + json.partial! 'api/v1/models/agent.json.jbuilder', resource: resource.user +end +json.created_at resource.created_at +json.updated_at resource.updated_at diff --git a/config/routes.rb b/config/routes.rb index 61a5d15a5..3149f7a47 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,6 +93,7 @@ Rails.application.routes.draw do resources :conversations, only: [:index] resources :contact_inboxes, only: [:create] resources :labels, only: [:create, :index] + resources :notes end end resources :csat_survey_responses, only: [:index] do diff --git a/spec/controllers/api/v1/accounts/contacts/notes_controller_spec.rb b/spec/controllers/api/v1/accounts/contacts/notes_controller_spec.rb new file mode 100644 index 000000000..a40184508 --- /dev/null +++ b/spec/controllers/api/v1/accounts/contacts/notes_controller_spec.rb @@ -0,0 +1,121 @@ +require 'rails_helper' + +RSpec.describe 'Notes API', type: :request do + let!(:account) { create(:account) } + let!(:contact) { create(:contact, account: account) } + let!(:note) { create(:note, contact: contact) } + let!(:agent) { create(:user, account: account, role: :agent) } + + describe 'GET /api/v1/accounts/{account.id}/contacts/{contact.id}/notes' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'returns all notes to agents' do + get "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body.first[:content]).to eq(note.content) + end + end + end + + describe 'GET /api/v1/accounts/{account.id}/contacts/{contact.id}/notes/{note.id}' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}" + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'shows the note for agents' do + get "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body, symbolize_names: true)[:id]).to eq(note.id) + end + end + end + + describe 'POST /api/v1/accounts/{account.id}/contacts/{contact.id}/notes' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + post "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes", + params: { content: 'test message' }, + as: :json + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'creates a new note' do + post "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes", + params: { content: 'test note' }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body, symbolize_names: true)[:content]).to eq('test note') + end + end + end + + describe 'PATCH /api/v1/accounts/{account.id}/contacts/{contact.id}/notes/:id' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + patch "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}", + params: { content: 'test message' }, + as: :json + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'updates the note' do + patch "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}", + params: { content: 'test message' }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(JSON.parse(response.body, symbolize_names: true)[:content]).to eq('test message') + end + end + end + + describe 'DELETE /api/v1/accounts/{account.id}/notes/:id' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + delete "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}", + as: :json + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'delete note if agent' do + delete "/api/v1/accounts/#{account.id}/contacts/#{contact.id}/notes/#{note.id}", + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + expect(::Note.exists?(note.id)).to eq false + end + end + end +end