From a58b23bccc623f9710c45dc2f0d405aaaa9536ad Mon Sep 17 00:00:00 2001 From: OJ Bucao Date: Tue, 17 Dec 2024 15:19:10 -0800 Subject: [PATCH 1/5] (fix) Apply requested PR changes; all specs passing --- .../power_of_attorney_request_resolution_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb b/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb index e7a006aac77..9e554815756 100644 --- a/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb +++ b/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb @@ -55,6 +55,18 @@ end end +<<<<<<< HEAD +======= + it 'does not allow invalid resolving_type values' do + resolution = build(:power_of_attorney_request_resolution, :with_invalid_type) + resolution.resolving_type = 'AccreditedRepresentativePortal::InvalidType' + + expect(resolution).not_to be_valid + expect(resolution.errors[:resolving_type]).to include('is not included in the list') + end + end + +>>>>>>> 3815598394 ((fix) Apply requested PR changes; all specs passing) describe 'heterogeneous list behavior' do it 'conveniently returns heterogeneous lists' do travel_to Time.zone.parse('2024-11-25T09:46:24Z') do From e5c36db0d76b953c64550a36c4d230d8e61ddc71 Mon Sep 17 00:00:00 2001 From: OJ Bucao Date: Mon, 16 Dec 2024 16:56:10 -0800 Subject: [PATCH 2/5] Add PowerOfAttorneyRequestSerializer and accompanying spec - Implement PowerOfAttorneyRequestSerializer to handle resolution attributes: - Includes `id`, `type`, `created_at`, `reason`, and conditional `creator_id` - Ensure safe handling of nil values with safe navigation (`&.`) and `try` - Add RSpec tests to validate: - Resolution serialization for decisions, expirations, and declinations - Handling of `reason` and `creator_id` conditionally - Nil resolution scenarios --- .../power_of_attorney_request_serializer.rb | 21 ++++++ .../power_of_attorney_request_resolution.rb | 18 ++++- ...wer_of_attorney_request_serializer_spec.rb | 73 +++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb create mode 100644 modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb diff --git a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb new file mode 100644 index 00000000000..b35acb5f643 --- /dev/null +++ b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module AccreditedRepresentativePortal + class PowerOfAttorneyRequestSerializer + include JSONAPI::Serializer + + attributes :id, :claimant_id, :created_at + + attribute :resolution do |object| + next nil unless object.resolution + + { + id: object.resolution.id, + type: object.resolution.resolving_type&.demodulize&.underscore, + created_at: object.resolution.created_at.iso8601, + reason: object.resolution&.reason, + creator_id: object.resolution.resolving.try(:creator_id) + }.compact + end + end +end diff --git a/modules/accredited_representative_portal/spec/factories/power_of_attorney_request_resolution.rb b/modules/accredited_representative_portal/spec/factories/power_of_attorney_request_resolution.rb index 20798bf1d54..1b7ca929b5a 100644 --- a/modules/accredited_representative_portal/spec/factories/power_of_attorney_request_resolution.rb +++ b/modules/accredited_representative_portal/spec/factories/power_of_attorney_request_resolution.rb @@ -3,12 +3,16 @@ FactoryBot.define do factory :power_of_attorney_request_resolution, class: 'AccreditedRepresentativePortal::PowerOfAttorneyRequestResolution' do - association :power_of_attorney_request, factory: :power_of_attorney_request - resolving_id { SecureRandom.uuid } - reason_ciphertext { 'Encrypted Reason' } + association :power_of_attorney_request + resolving_type { 'AccreditedRepresentativePortal::PowerOfAttorneyRequestExpiration' } + reason { 'Test reason for resolution' } created_at { Time.current } encrypted_kms_key { SecureRandom.hex(16) } + after(:build) do |resolution| + resolution.id ||= SecureRandom.random_number(1000) + end + trait :with_expiration do resolving_type { 'AccreditedRepresentativePortal::PowerOfAttorneyRequestExpiration' } resolving { create(:power_of_attorney_request_expiration) } @@ -16,7 +20,13 @@ trait :with_decision do resolving_type { 'AccreditedRepresentativePortal::PowerOfAttorneyRequestDecision' } - resolving { create(:power_of_attorney_request_decision) } + resolving { create(:power_of_attorney_request_decision, creator: create(:user_account)) } + end + + trait :with_declination do + resolving_type { 'AccreditedRepresentativePortal::PowerOfAttorneyRequestDecision' } + reason { "Didn't authorize treatment record disclosure" } + resolving { create(:power_of_attorney_request_decision, creator: create(:user_account)) } end trait :with_invalid_type do diff --git a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb new file mode 100644 index 00000000000..38da05c1556 --- /dev/null +++ b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb @@ -0,0 +1,73 @@ +# spec/serializers/power_of_attorney_request_serializer_spec.rb +require 'rails_helper' + +RSpec.describe AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer do + let(:request) { create(:power_of_attorney_request, created_at: '2024-12-17T00:30:55Z') } + + subject { described_class.new(request).serializable_hash[:data][:attributes] } + + context 'when resolution is nil' do + it 'returns the request without resolution' do + expect(subject).to eq({ + id: request.id, + claimant_id: request.claimant_id, + created_at: '2024-12-17T00:30:55Z', + resolution: nil + }) + end + end + + context 'when resolution is an expiration' do + let(:resolution) do + create(:power_of_attorney_request_resolution, :with_expiration, reason: 'Test reason for resolution') + end + + before { request.update(resolution: resolution) } + + it 'includes resolution details with type expiration and reason' do + expect(subject[:resolution]).to eq({ + id: resolution.id, + type: 'power_of_attorney_request_expiration', + created_at: resolution.created_at.iso8601, + reason: 'Test reason for resolution' + }) + end + end + + context 'when resolution is a decision with creator_id' do + let(:resolution) do + create(:power_of_attorney_request_resolution, :with_decision) + end + + before { request.update(resolution: resolution) } + + it 'includes resolution details with type decision and creator_id' do + expect(subject[:resolution]).to eq({ + id: resolution.id, + type: 'power_of_attorney_request_decision', + created_at: resolution.created_at.iso8601, + reason: 'Test reason for resolution', + creator_id: resolution.resolving.creator_id + }) + end + end + + context 'when resolution is a declination with reason' do + let(:resolution) do + create(:power_of_attorney_request_resolution, :with_declination, + reason: "Didn't authorize treatment record disclosure") + end + + before { request.update(resolution: resolution) } + + it 'includes resolution details with type decision, reason, and creator_id' do + expect(subject[:resolution]).to eq({ + id: resolution.id, + type: 'power_of_attorney_request_decision', + created_at: resolution.created_at.iso8601, + reason: "Didn't authorize treatment record disclosure", + creator_id: resolution.resolving.creator_id + }) + end + end +end From e7856e05ae752da4379987f7d91de9ef8fbaec42 Mon Sep 17 00:00:00 2001 From: OJ Bucao Date: Mon, 16 Dec 2024 17:53:30 -0800 Subject: [PATCH 3/5] Add serializer for PowerOfAttorneyRequest with controller and tests - Implement `PowerOfAttorneyRequestSerializer` to standardize JSON:API output - Add request specs for controller endpoints (`index` and `show`) - Add serializer specs to ensure proper formatting of resolution details - Normalize response keys for consistency in test expectations --- .../power_of_attorney_requests_controller.rb | 53 +++------------ .../power_of_attorney_request_serializer.rb | 8 ++- .../v0/power_of_attorney_requests_spec.rb | 65 +++++++++++-------- ...wer_of_attorney_request_serializer_spec.rb | 50 +++++++------- 4 files changed, 80 insertions(+), 96 deletions(-) diff --git a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb index 867b3db8d32..98f59734be0 100644 --- a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb +++ b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb @@ -3,54 +3,21 @@ module AccreditedRepresentativePortal module V0 class PowerOfAttorneyRequestsController < ApplicationController - POA_REQUEST_ITEM_MOCK_DATA = { - status: 'Pending', - declinedReason: nil, - powerOfAttorneyCode: '091', - submittedAt: '2024-04-30T11:03:17Z', - acceptedOrDeclinedAt: nil, - isAddressChangingAuthorized: false, - isTreatmentDisclosureAuthorized: true, - veteran: { - firstName: 'Jon', - middleName: nil, - lastName: 'Smith', - participantId: '6666666666666' - }, - representative: { - email: 'j2@example.com', - firstName: 'Jane', - lastName: 'Doe' - }, - claimant: { - firstName: 'Sam', - lastName: 'Smith', - participantId: '777777777777777', - relationshipToVeteran: 'Child' - }, - claimantAddress: { - city: 'Hartford', - state: 'CT', - zip: '06107', - country: 'GU', - militaryPostOffice: nil, - militaryPostalCode: nil - } - }.freeze - - POA_REQUEST_LIST_MOCK_DATA = [ - POA_REQUEST_ITEM_MOCK_DATA, - POA_REQUEST_ITEM_MOCK_DATA, - POA_REQUEST_ITEM_MOCK_DATA - ].freeze - def index - render json: POA_REQUEST_LIST_MOCK_DATA + requests = PowerOfAttorneyRequest.includes(:resolution) + render json: PowerOfAttorneyRequestSerializer.new(requests).serializable_hash, status: :ok end def show - render json: POA_REQUEST_ITEM_MOCK_DATA + request = PowerOfAttorneyRequest.includes(:resolution).find(params[:id]) + render json: PowerOfAttorneyRequestSerializer.new(request).serializable_hash, status: :ok + rescue ActiveRecord::RecordNotFound + render json: { error: 'Record not found' }, status: :not_found end + + private + + def authenticate; end end end end diff --git a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb index b35acb5f643..89961c9989e 100644 --- a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb +++ b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb @@ -4,7 +4,11 @@ module AccreditedRepresentativePortal class PowerOfAttorneyRequestSerializer include JSONAPI::Serializer - attributes :id, :claimant_id, :created_at + attributes :id, :claimant_id + + attribute :created_at do |object| + object.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ') + end attribute :resolution do |object| next nil unless object.resolution @@ -12,7 +16,7 @@ class PowerOfAttorneyRequestSerializer { id: object.resolution.id, type: object.resolution.resolving_type&.demodulize&.underscore, - created_at: object.resolution.created_at.iso8601, + created_at: object.resolution.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), reason: object.resolution&.reason, creator_id: object.resolution.resolving.try(:creator_id) }.compact diff --git a/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb b/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb index c1349f2db60..d58082927c0 100644 --- a/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb +++ b/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb @@ -4,28 +4,8 @@ RSpec.describe AccreditedRepresentativePortal::V0::PowerOfAttorneyRequestsController, type: :request do let(:test_user) { create(:representative_user) } - let(:poa_request_details_id) { '123' } - let(:poa_request_details_mock_data) do - { - 'status' => 'Pending', - 'declinedReason' => nil, - 'powerOfAttorneyCode' => '091', - 'submittedAt' => '2024-04-30T11:03:17Z', - 'acceptedOrDeclinedAt' => nil, - 'isAddressChangingAuthorized' => false, - 'isTreatmentDisclosureAuthorized' => true, - 'veteran' => { 'firstName' => 'Jon', 'middleName' => nil, 'lastName' => 'Smith', - 'participantId' => '6666666666666' }, - 'representative' => { 'email' => 'j2@example.com', 'firstName' => 'Jane', 'lastName' => 'Doe' }, - 'claimant' => { 'firstName' => 'Sam', 'lastName' => 'Smith', 'participantId' => '777777777777777', - 'relationshipToVeteran' => 'Child' }, - 'claimantAddress' => { 'city' => 'Hartford', 'state' => 'CT', 'zip' => '06107', 'country' => 'GU', - 'militaryPostOffice' => nil, 'militaryPostalCode' => nil } - } - end - let(:poa_request_list_mock_data) do - [poa_request_details_mock_data, poa_request_details_mock_data, poa_request_details_mock_data] - end + let(:poa_request) { create(:power_of_attorney_request) } + let(:poa_requests) { create_list(:power_of_attorney_request, 3) } before do Flipper.enable(:accredited_representative_portal_pilot) @@ -33,18 +13,49 @@ end describe 'GET /accredited_representative_portal/v0/power_of_attorney_requests' do - it 'returns the list of a power of attorney request' do + it 'returns the list of power of attorney requests' do + poa_requests + get('/accredited_representative_portal/v0/power_of_attorney_requests') + expect(response).to have_http_status(:ok) - expect(JSON.parse(response.body)).to eq(poa_request_list_mock_data) + parsed_response = JSON.parse(response.body) + + expected_response = AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer + .new(poa_requests) + .serializable_hash + + expect(deep_stringify(parsed_response)).to eq(deep_stringify(expected_response)) end end describe 'GET /accredited_representative_portal/v0/power_of_attorney_requests/:id' do - it 'returns the details of a power of attorney request' do - get("/accredited_representative_portal/v0/power_of_attorney_requests/#{poa_request_details_id}") + it 'returns the details of a specific power of attorney request' do + get("/accredited_representative_portal/v0/power_of_attorney_requests/#{poa_request.id}") + expect(response).to have_http_status(:ok) - expect(JSON.parse(response.body)).to include(poa_request_details_mock_data) + parsed_response = JSON.parse(response.body) + + expected_response = AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer + .new(poa_request) + .serializable_hash + + expect(deep_stringify(parsed_response)).to eq(deep_stringify(expected_response)) + end + end + + def deep_stringify(value) + case value + when Hash + value.each_with_object({}) do |(k, v), result| + result[k.to_s] = deep_stringify(v) # Stringify keys and recursively process values + end + when Array + value.map { |v| deep_stringify(v) } # Recursively process arrays + when Symbol + value.to_s # Convert symbols to strings + else + value # Leave other primitives (e.g., strings, numbers, nil) as is end end end diff --git a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb index 38da05c1556..14011bf6ec6 100644 --- a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb +++ b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + # spec/serializers/power_of_attorney_request_serializer_spec.rb require 'rails_helper' RSpec.describe AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer do - let(:request) { create(:power_of_attorney_request, created_at: '2024-12-17T00:30:55Z') } - subject { described_class.new(request).serializable_hash[:data][:attributes] } + let(:request) { create(:power_of_attorney_request, created_at: '2024-12-17T00:30:55Z') } + context 'when resolution is nil' do it 'returns the request without resolution' do expect(subject).to eq({ - id: request.id, - claimant_id: request.claimant_id, - created_at: '2024-12-17T00:30:55Z', - resolution: nil - }) + id: request.id, + claimant_id: request.claimant_id, + created_at: '2024-12-17T00:30:55.000Z', + resolution: nil + }) end end @@ -26,11 +28,11 @@ it 'includes resolution details with type expiration and reason' do expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_expiration', - created_at: resolution.created_at.iso8601, - reason: 'Test reason for resolution' - }) + id: resolution.id, + type: 'power_of_attorney_request_expiration', + created_at: resolution.created_at.iso8601(3), + reason: 'Test reason for resolution' + }) end end @@ -43,12 +45,12 @@ it 'includes resolution details with type decision and creator_id' do expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_decision', - created_at: resolution.created_at.iso8601, - reason: 'Test reason for resolution', - creator_id: resolution.resolving.creator_id - }) + id: resolution.id, + type: 'power_of_attorney_request_decision', + created_at: resolution.created_at.iso8601(3), + reason: 'Test reason for resolution', + creator_id: resolution.resolving.creator_id + }) end end @@ -62,12 +64,12 @@ it 'includes resolution details with type decision, reason, and creator_id' do expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_decision', - created_at: resolution.created_at.iso8601, - reason: "Didn't authorize treatment record disclosure", - creator_id: resolution.resolving.creator_id - }) + id: resolution.id, + type: 'power_of_attorney_request_decision', + created_at: resolution.created_at.iso8601(3), + reason: "Didn't authorize treatment record disclosure", + creator_id: resolution.resolving.creator_id + }) end end end From c7eebf807e118597207e2b27de9f24472e461c98 Mon Sep 17 00:00:00 2001 From: OJ Bucao Date: Thu, 19 Dec 2024 11:59:58 -0800 Subject: [PATCH 4/5] (feat): Add serializers for PowerOfAttorneyRequest and Resolution with specs - Implement `PowerOfAttorneyRequestSerializer` to handle serialization of power of attorney requests, including nested resolution data. - Implement `PowerOfAttorneyRequestResolutionSerializer` to serialize resolution details, accommodating various resolution subtypes. - Add comprehensive specs for both serializers to ensure accurate and dynamic handling of attributes. - Adjust model factories accordingly --- .../power_of_attorney_requests_controller.rb | 12 +- ..._attorney_request_resolution_serializer.rb | 27 +++++ .../power_of_attorney_request_serializer.rb | 14 +-- .../factories/power_of_attorney_decision.rb | 8 ++ .../factories/power_of_attorney_request.rb | 4 + ...wer_of_attorney_request_resolution_spec.rb | 12 -- .../v0/power_of_attorney_requests_spec.rb | 19 +-- ...rney_request_resolution_serializer_spec.rb | 42 +++++++ ...wer_of_attorney_request_serializer_spec.rb | 109 +++++++----------- 9 files changed, 133 insertions(+), 114 deletions(-) create mode 100644 modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer.rb create mode 100644 modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer_spec.rb diff --git a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb index 98f59734be0..f8387180711 100644 --- a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb +++ b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb @@ -4,20 +4,16 @@ module AccreditedRepresentativePortal module V0 class PowerOfAttorneyRequestsController < ApplicationController def index - requests = PowerOfAttorneyRequest.includes(:resolution) - render json: PowerOfAttorneyRequestSerializer.new(requests).serializable_hash, status: :ok + poa_requests = PowerOfAttorneyRequest.includes(:resolution) + render json: PowerOfAttorneyRequestSerializer.new(poa_requests).serializable_hash, status: :ok end def show - request = PowerOfAttorneyRequest.includes(:resolution).find(params[:id]) - render json: PowerOfAttorneyRequestSerializer.new(request).serializable_hash, status: :ok + poa_request = PowerOfAttorneyRequest.includes(:resolution).find(params[:id]) + render json: PowerOfAttorneyRequestSerializer.new(poa_request).serializable_hash, status: :ok rescue ActiveRecord::RecordNotFound render json: { error: 'Record not found' }, status: :not_found end - - private - - def authenticate; end end end end diff --git a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer.rb b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer.rb new file mode 100644 index 00000000000..99ce0602977 --- /dev/null +++ b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module AccreditedRepresentativePortal + class PowerOfAttorneyRequestResolutionSerializer + include JSONAPI::Serializer + + attribute :id + + attribute :type do |object| + object.resolving_type.demodulize.underscore.split('_').last + end + + attribute :decision_type, if: proc { |obj| obj.resolving.respond_to?(:type) } do |object| + object.resolving.type + end + + attribute :reason + + attribute :creator_id, if: proc { |obj| obj.resolving.respond_to?(:creator_id) } do |object| + object.resolving.creator_id + end + + attribute :created_at do |object| + object.created_at.iso8601 + end + end +end diff --git a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb index 89961c9989e..4114143b6b1 100644 --- a/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb +++ b/modules/accredited_representative_portal/app/serializers/accredited_representative_portal/power_of_attorney_request_serializer.rb @@ -7,19 +7,15 @@ class PowerOfAttorneyRequestSerializer attributes :id, :claimant_id attribute :created_at do |object| - object.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ') + object.created_at.iso8601 end attribute :resolution do |object| - next nil unless object.resolution + next nil if object.resolution.blank? - { - id: object.resolution.id, - type: object.resolution.resolving_type&.demodulize&.underscore, - created_at: object.resolution.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'), - reason: object.resolution&.reason, - creator_id: object.resolution.resolving.try(:creator_id) - }.compact + AccreditedRepresentativePortal::PowerOfAttorneyRequestResolutionSerializer.new( + object.resolution + ).serializable_hash[:data][:attributes] end end end diff --git a/modules/accredited_representative_portal/spec/factories/power_of_attorney_decision.rb b/modules/accredited_representative_portal/spec/factories/power_of_attorney_decision.rb index fd6f2e65d66..96066eafe0c 100644 --- a/modules/accredited_representative_portal/spec/factories/power_of_attorney_decision.rb +++ b/modules/accredited_representative_portal/spec/factories/power_of_attorney_decision.rb @@ -6,5 +6,13 @@ id { Faker::Internet.uuid } association :creator, factory: :user_account type { 'Approval' } + + trait :declination do + type { 'Declination' } + end + + trait :approval do + type { 'Approval' } + end end end diff --git a/modules/accredited_representative_portal/spec/factories/power_of_attorney_request.rb b/modules/accredited_representative_portal/spec/factories/power_of_attorney_request.rb index 0aa23eea4f6..5d04780c575 100644 --- a/modules/accredited_representative_portal/spec/factories/power_of_attorney_request.rb +++ b/modules/accredited_representative_portal/spec/factories/power_of_attorney_request.rb @@ -5,5 +5,9 @@ association :claimant, factory: :user_account id { Faker::Internet.uuid } created_at { Time.current } + + trait :with_resolution do + resolution { create(:power_of_attorney_request_resolution, :with_decision) } + end end end diff --git a/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb b/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb index 9e554815756..e7a006aac77 100644 --- a/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb +++ b/modules/accredited_representative_portal/spec/models/accredited_representative_portal/power_of_attorney_request_resolution_spec.rb @@ -55,18 +55,6 @@ end end -<<<<<<< HEAD -======= - it 'does not allow invalid resolving_type values' do - resolution = build(:power_of_attorney_request_resolution, :with_invalid_type) - resolution.resolving_type = 'AccreditedRepresentativePortal::InvalidType' - - expect(resolution).not_to be_valid - expect(resolution.errors[:resolving_type]).to include('is not included in the list') - end - end - ->>>>>>> 3815598394 ((fix) Apply requested PR changes; all specs passing) describe 'heterogeneous list behavior' do it 'conveniently returns heterogeneous lists' do travel_to Time.zone.parse('2024-11-25T09:46:24Z') do diff --git a/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb b/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb index d58082927c0..b9f4f6f2b58 100644 --- a/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb +++ b/modules/accredited_representative_portal/spec/requests/accredited_representative_portal/v0/power_of_attorney_requests_spec.rb @@ -25,7 +25,7 @@ .new(poa_requests) .serializable_hash - expect(deep_stringify(parsed_response)).to eq(deep_stringify(expected_response)) + expect(parsed_response.to_json).to eq(expected_response.to_json) end end @@ -40,22 +40,7 @@ .new(poa_request) .serializable_hash - expect(deep_stringify(parsed_response)).to eq(deep_stringify(expected_response)) - end - end - - def deep_stringify(value) - case value - when Hash - value.each_with_object({}) do |(k, v), result| - result[k.to_s] = deep_stringify(v) # Stringify keys and recursively process values - end - when Array - value.map { |v| deep_stringify(v) } # Recursively process arrays - when Symbol - value.to_s # Convert symbols to strings - else - value # Leave other primitives (e.g., strings, numbers, nil) as is + expect(parsed_response.to_json).to eq(expected_response.to_json) end end end diff --git a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer_spec.rb b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer_spec.rb new file mode 100644 index 00000000000..3df2538fd8b --- /dev/null +++ b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_resolution_serializer_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require_relative '../../rails_helper' + +RSpec.describe AccreditedRepresentativePortal::PowerOfAttorneyRequestResolutionSerializer, type: :serializer do + describe 'serialization' do + subject { described_class.new(resolution).serializable_hash[:data][:attributes] } + + let(:user) { create(:user_account) } + let(:resolution) do + create(:power_of_attorney_request_resolution, resolving: resolving, reason: 'Did not authorize') + end + + context 'when resolving is a Decision' do + let(:resolving) { create(:power_of_attorney_request_decision, type: 'declination', creator: user) } + + it 'serializes resolution with decision-specific fields' do + expect(subject).to eq( + id: resolution.id, + created_at: resolution.created_at.iso8601, + reason: 'Did not authorize', + type: 'decision', + decision_type: 'declination', + creator_id: user.id + ) + end + end + + context 'when resolving is an Expiration' do + let(:resolving) { create(:power_of_attorney_request_expiration) } + + it 'serializes resolution with expiration-specific fields' do + expect(subject).to eq( + id: resolution.id, + created_at: resolution.created_at.iso8601, + reason: 'Did not authorize', + type: 'expiration' + ) + end + end + end +end diff --git a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb index 14011bf6ec6..b923cf977bd 100644 --- a/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb +++ b/modules/accredited_representative_portal/spec/serializers/accredited_representative_portal/power_of_attorney_request_serializer_spec.rb @@ -1,75 +1,48 @@ # frozen_string_literal: true -# spec/serializers/power_of_attorney_request_serializer_spec.rb -require 'rails_helper' - -RSpec.describe AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer do - subject { described_class.new(request).serializable_hash[:data][:attributes] } - - let(:request) { create(:power_of_attorney_request, created_at: '2024-12-17T00:30:55Z') } - - context 'when resolution is nil' do - it 'returns the request without resolution' do - expect(subject).to eq({ - id: request.id, - claimant_id: request.claimant_id, - created_at: '2024-12-17T00:30:55.000Z', - resolution: nil - }) - end - end - - context 'when resolution is an expiration' do - let(:resolution) do - create(:power_of_attorney_request_resolution, :with_expiration, reason: 'Test reason for resolution') +require_relative '../../rails_helper' + +RSpec.describe AccreditedRepresentativePortal::PowerOfAttorneyRequestSerializer, type: :serializer do + describe 'serialization' do + subject { described_class.new(poa_request).serializable_hash[:data][:attributes] } + + let(:claimant) { create(:user_account) } + let(:poa_request) { create(:power_of_attorney_request, claimant: claimant, resolution: resolution) } + + context 'when resolution exists' do + let(:resolution) do + create(:power_of_attorney_request_resolution, + resolving: create(:power_of_attorney_request_decision, type: 'declination')) + end + + it 'serializes POA request with resolution' do + expect(subject).to eq( + id: poa_request.id, + claimant_id: poa_request.claimant_id, + created_at: poa_request.created_at.iso8601, + resolution: { + id: resolution.id, + created_at: resolution.created_at.iso8601, + reason: resolution.reason, + type: 'decision', + decision_type: 'declination', + creator_id: resolution.resolving.creator_id + } + ) + end end - before { request.update(resolution: resolution) } - - it 'includes resolution details with type expiration and reason' do - expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_expiration', - created_at: resolution.created_at.iso8601(3), - reason: 'Test reason for resolution' - }) - end - end - - context 'when resolution is a decision with creator_id' do - let(:resolution) do - create(:power_of_attorney_request_resolution, :with_decision) - end - - before { request.update(resolution: resolution) } - - it 'includes resolution details with type decision and creator_id' do - expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_decision', - created_at: resolution.created_at.iso8601(3), - reason: 'Test reason for resolution', - creator_id: resolution.resolving.creator_id - }) - end - end - - context 'when resolution is a declination with reason' do - let(:resolution) do - create(:power_of_attorney_request_resolution, :with_declination, - reason: "Didn't authorize treatment record disclosure") - end - - before { request.update(resolution: resolution) } - - it 'includes resolution details with type decision, reason, and creator_id' do - expect(subject[:resolution]).to eq({ - id: resolution.id, - type: 'power_of_attorney_request_decision', - created_at: resolution.created_at.iso8601(3), - reason: "Didn't authorize treatment record disclosure", - creator_id: resolution.resolving.creator_id - }) + context 'when resolution is absent' do + let(:resolution) { nil } + + it 'serializes POA request without resolution' do + expect(subject).to eq( + id: poa_request.id, + claimant_id: poa_request.claimant_id, + created_at: poa_request.created_at.iso8601, + resolution: nil + ) + end end end end From e3032d5989949ccde35b8db198bb55382b43d5d8 Mon Sep 17 00:00:00 2001 From: OJ Bucao Date: Fri, 20 Dec 2024 13:54:02 -0800 Subject: [PATCH 5/5] (fix) Refactor AR PowerOfAttorneyRequestController includes for improved query performance - Updated `includes` to reference `resolving` instead of just `resolution` - Added a limit of 100 records in the `index` action to optimize data retrieval --- .../v0/power_of_attorney_requests_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb index f8387180711..2f0dc0a512a 100644 --- a/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb +++ b/modules/accredited_representative_portal/app/controllers/accredited_representative_portal/v0/power_of_attorney_requests_controller.rb @@ -4,12 +4,12 @@ module AccreditedRepresentativePortal module V0 class PowerOfAttorneyRequestsController < ApplicationController def index - poa_requests = PowerOfAttorneyRequest.includes(:resolution) + poa_requests = PowerOfAttorneyRequest.includes(resolution: :resolving).limit(100) render json: PowerOfAttorneyRequestSerializer.new(poa_requests).serializable_hash, status: :ok end def show - poa_request = PowerOfAttorneyRequest.includes(:resolution).find(params[:id]) + poa_request = PowerOfAttorneyRequest.includes(resolution: :resolving).find(params[:id]) render json: PowerOfAttorneyRequestSerializer.new(poa_request).serializable_hash, status: :ok rescue ActiveRecord::RecordNotFound render json: { error: 'Record not found' }, status: :not_found