Skip to content

Commit

Permalink
Add post appt req message (#3631)
Browse files Browse the repository at this point in the history
  • Loading branch information
saneshark authored Dec 16, 2019
1 parent e5443c4 commit e66b613
Show file tree
Hide file tree
Showing 15 changed files with 514 additions and 66 deletions.
6 changes: 6 additions & 0 deletions config/locales/exceptions.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,12 @@ en:
code: 'VAOS_400'
detail: # deliberarly left nil to reference response.body which is unparsable string (not json)
status: 400
VAOS_204:
<<: *external_defaults
title: Bad Request
code: 'VAOS_400'
detail: 'Appointment request id is invalid'
status: 400
VAOS_403:
<<: *external_defaults
title: Forbidden
Expand Down
16 changes: 15 additions & 1 deletion modules/vaos/app/controllers/vaos/messages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,24 @@ def index
render json: MessagesSerializer.new(messages[:data], meta: messages[:meta])
end

def create
response = messages_service.post_message(appointment_request_id, post_params)
render json: MessagesSerializer.new(response[:data], meta: response[:meta])
end

private

def appointment_request_id
params[:appointment_request_id]
end

def post_params
params.require(:message_text)
params.permit(:message_text)
end

def messages
@messages ||= messages_service.get_messages(params[:appointment_request_id])
@messages ||= messages_service.get_messages(appointment_request_id)
end

def messages_service
Expand Down
58 changes: 58 additions & 0 deletions modules/vaos/app/models/vaos/message_form.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

require 'active_model'
require 'common/models/form'

module VAOS
class MessageForm < Common::Form
attribute :message_text, String # only this attribute gets set by user, everything else is overridden
attribute :url, String
attribute :sender_id, String
attribute :appointment_request_id, String
attribute :message_date_time, String
attribute :message_sent, Boolean
attribute :is_last_message, Boolean
attribute :_appointment_request_id, String

def initialize(user, request_id, json_hash = {})
super(json_hash)
@user = user
@appointment_request_id = request_id
end

# DOWNSTREAM_BUG: addresses the fact that downstream requires this as AppointmentRequestId (upper camelcase)
def _appointment_request_id
appointment_request_id
end

def url
''
end

def sender_id
@user.icn
end

def message_date_time
''
end

def message_sent
true
end

# rubocop:disable Naming/PredicateName
def is_last_message
true
end
# rubocop:enable Naming/PredicateName

validates :message_text, length: { minimum: 1, maximum: 100 }

def params
raise Common::Exceptions::ValidationErrors, self unless valid?

attributes
end
end
end
7 changes: 2 additions & 5 deletions modules/vaos/app/serializers/vaos/messages_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@ class MessagesSerializer

set_type :messages

attributes :surrogate_identifier,
:message_text,
attributes :message_text,
:message_date_time,
:sender_id,
:appointment_request_id,
:date,
:assigning_authority,
:system_id
:date
end
end
23 changes: 22 additions & 1 deletion modules/vaos/app/services/vaos/messages_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,31 @@ def get_messages(request_id)
end
end

def post_message(request_id, request_object_body)
with_monitoring do
params = VAOS::MessageForm.new(user, request_id, request_object_body).params
response = perform(:post, messages_url(request_id), params, headers)

if response.status == 200
{
data: OpenStruct.new(response.body),
meta: {}
}
else
handle_error(response)
end
end
end

private

def handle_error(response)
key = response.status == 204 ? 'VAOS_204' : nil
raise Common::Exceptions::BackendServiceException.new(key, {}, response.status, response.body)
end

def deserialize(json_hash)
json_hash[:appointment_request_message].map { |request| OpenStruct.new(request) }
json_hash[:appointment_request_message].map { |message| OpenStruct.new(message) }
rescue => e
log_message_to_sentry(e.message, :warn, invalid_json: json_hash)
[]
Expand Down
8 changes: 7 additions & 1 deletion modules/vaos/app/services/vaos/middleware/response/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,16 @@ def error_400(body)
raise Common::Exceptions::BackendServiceException.new(
'VAOS_400',
title: 'Bad Request',
detail: body,
detail: parse_error(body),
source: self.class
)
end

def parse_error(body)
JSON.parse(body)['errors'].first['errorMessage']
rescue
body
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion modules/vaos/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
put 'cancel', on: :collection
end
resources :appointment_requests, only: %i[index create update] do
resources :messages, only: :index
resources :messages, only: %i[index create]
end
resources :systems, only: :index do
resources :direct_scheduling_facilities, only: :index
Expand Down
42 changes: 42 additions & 0 deletions modules/vaos/spec/models/message_form_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

require 'rails_helper'

describe VAOS::MessageForm, type: :model do
let(:user) { build(:user, :vaos) }
let(:request_id) { 'fake_request_id' }

describe 'invalid object' do
subject { described_class.new(user, request_id) }

it 'validates presence of required attributes' do
expect(subject).not_to be_valid
expect(subject.errors.keys).to contain_exactly(:message_text)
end

it 'raises a Common::Exceptions::ValidationErrors when trying to fetch coerced params' do
expect { subject.params }.to raise_error(Common::Exceptions::ValidationErrors)
end

context 'message_text length > 100' do
subject do
described_class.new(user, request_id, message_text: Faker::Lorem.characters(101))
end

it 'raises a custom error message' do
expect(subject).not_to be_valid
expect(subject.errors.full_messages).to eq(['Message text is too long (maximum is 100 characters)'])
end
end
end

describe 'valid object' do
subject do
described_class.new(user, request_id, message_text: 'I want to see doctor Jeckyl please.')
end

it 'validates presence of required attributes' do
expect(subject).to be_valid
end
end
end
140 changes: 120 additions & 20 deletions modules/vaos/spec/request/messages_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,145 @@
RSpec.describe 'vaos appointment request messages', type: :request do
include SchemaMatchers

let(:request_id) { '8a4886886e4c8e22016e5bee49c30007' }

before do
Flipper.enable('va_online_scheduling')
sign_in_as(current_user)
allow_any_instance_of(VAOS::UserService).to receive(:session).and_return('stubbed_token')
end

context 'loa1 user with flipper enabled' do
let(:current_user) { build(:user, :loa1) }
describe 'GET messages' do
let(:request_id) { '8a4886886e4c8e22016e5bee49c30007' }

context 'loa1 user with flipper enabled' do
let(:current_user) { build(:user, :loa1) }

it 'does not have access' do
get "/v0/vaos/appointment_requests/#{request_id}/messages"
expect(response).to have_http_status(:forbidden)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('You do not have access to online scheduling')
it 'does not have access' do
get "/v0/vaos/appointment_requests/#{request_id}/messages"
expect(response).to have_http_status(:forbidden)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('You do not have access to online scheduling')
end
end

context 'loa3 user' do
let(:current_user) { build(:user, :vaos) }

context 'with flipper disabled' do
it 'does not have access' do
Flipper.disable('va_online_scheduling')
get "/v0/vaos/appointment_requests/#{request_id}/messages"
expect(response).to have_http_status(:forbidden)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('You do not have access to online scheduling')
end
end

it 'has access and returns messages', :skip_mvi do
VCR.use_cassette('vaos/messages/get_messages', match_requests_on: %i[method uri]) do
get "/v0/vaos/appointment_requests/#{request_id}/messages"

expect(response).to have_http_status(:success)
expect(response.body).to be_a(String)
expect(response).to match_response_schema('vaos/messages')
end
end
end
end

context 'loa3 user' do
let(:current_user) { build(:user, :vaos) }
describe 'POST message' do
let(:request_id) { '8a4886886e4c8e22016ef6a8b1bf0396' }
let(:request_body) { { message_text: 'I want to see doctor Jeckyl please.' } }

context 'loa1 user with flipper enabled' do
let(:current_user) { build(:user, :loa1) }

context 'with flipper disabled' do
it 'does not have access' do
Flipper.disable('va_online_scheduling')
get "/v0/vaos/appointment_requests/#{request_id}/messages"
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:forbidden)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('You do not have access to online scheduling')
end
end

it 'has access and returns messages', :skip_mvi do
VCR.use_cassette('vaos/messages/get_messages', match_requests_on: %i[method uri]) do
get "/v0/vaos/appointment_requests/#{request_id}/messages"
context 'loa3 user' do
let(:current_user) { build(:user, :vaos) }

context 'with flipper disabled' do
it 'does not have access' do
Flipper.disable('va_online_scheduling')
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:forbidden)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('You do not have access to online scheduling')
end
end

context 'with access and valid message' do
it 'posts a message', :skip_mvi do
VCR.use_cassette('vaos/messages/post_message', match_requests_on: %i[method uri]) do
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:success)
expect(response.body).to be_a(String)
expect(json_body_for(response)).to match_schema('vaos/message')
end
end
end

context 'with access and invalid message' do
let(:request_body) { { message_text: Faker::Lorem.characters(101) } }

it 'returns a validation error', :skip_mvi do
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:unprocessable_entity)
expect(response.body).to be_a(String)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('message-text - is too long (maximum is 100 characters)')
end
end

context 'with access and invalid message' do
let(:request_body) { { message_text: '' } }

it 'returns a validation error', :skip_mvi do
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:bad_request)
expect(response.body).to be_a(String)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('The required parameter "message_text", is missing')
end
end

context 'with access and invalid appointment request id' do
let(:request_id) { '8a4886886e4c8e22016eebd3b8820347' }

it 'returns bad request', :skip_mvi do
VCR.use_cassette('vaos/messages/post_message_error', match_requests_on: %i[method uri]) do
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:bad_request)
expect(response.body).to be_a(String)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('Appointment request id is invalid')
end
end
end

context 'with access and too many messages for appointment request' do
it 'returns bad request', :skip_mvi do
VCR.use_cassette('vaos/messages/post_message_error_400', match_requests_on: %i[method uri]) do
post "/v0/vaos/appointment_requests/#{request_id}/messages", params: request_body

expect(response).to have_http_status(:success)
expect(response.body).to be_a(String)
expect(response).to match_response_schema('vaos/messages')
expect(response).to have_http_status(:bad_request)
expect(response.body).to be_a(String)
expect(JSON.parse(response.body)['errors'].first['detail'])
.to eq('Maximum allowed number of messages for this appointment request reached.')
end
end
end
end
end
Expand Down
Loading

0 comments on commit e66b613

Please sign in to comment.