From 0b195781c50baff4ff78877dfd90b0bfbb614544 Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Wed, 15 Apr 2026 10:29:24 -0300 Subject: [PATCH] feat(lifecycle): REST endpoint for lifecycle deliveries audit log --- .../lifecycle_deliveries_controller.rb | 43 ++++++++++++++- .../lifecycle_deliveries/index.json.jbuilder | 11 ++++ .../lifecycle_deliveries/show.json.jbuilder | 1 + .../captain/_lifecycle_delivery.json.jbuilder | 24 ++++++++ .../lifecycle_deliveries_controller_spec.rb | 55 +++++++++++++++++++ 5 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/show.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/captain/_lifecycle_delivery.json.jbuilder create mode 100644 spec/enterprise/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller_spec.rb diff --git a/enterprise/app/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller.rb b/enterprise/app/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller.rb index 332d8c138..6f29d6790 100644 --- a/enterprise/app/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller.rb +++ b/enterprise/app/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller.rb @@ -1,5 +1,46 @@ class Api::V1::Accounts::Captain::LifecycleDeliveriesController < Api::V1::Accounts::BaseController + RESULTS_PER_PAGE = 25 + MAX_RESULTS_PER_PAGE = 100 + + before_action :current_account + before_action -> { check_authorization(Captain::Lifecycle::Delivery) } + before_action :set_page, only: [:index] + before_action :set_delivery, only: [:show] + def index - head :ok + scope = base_scope + scope = apply_filters(scope) + @total_count = scope.count + @deliveries = scope.page(@page).per(@per_page) end + + def show; end + + private + + def set_page + @page = (params[:page] || 1).to_i + @per_page = [(params[:per_page] || RESULTS_PER_PAGE).to_i, MAX_RESULTS_PER_PAGE].min + end + + def set_delivery + @delivery = Current.account.captain_lifecycle_deliveries.find(params[:id]) + end + + def base_scope + Current.account.captain_lifecycle_deliveries + .includes(:lifecycle_rule, captain_reservation: :contact) + .order(created_at: :desc) + end + + # rubocop:disable Metrics/AbcSize + def apply_filters(scope) + scope = scope.where(status: params[:status]) if params[:status].present? + scope = scope.where(lifecycle_rule_id: params[:rule_id]) if params[:rule_id].present? + scope = scope.where(captain_reservation_id: params[:reservation_id]) if params[:reservation_id].present? + scope = scope.where('fire_at >= ?', params[:from]) if params[:from].present? + scope = scope.where('fire_at <= ?', params[:to]) if params[:to].present? + scope + end + # rubocop:enable Metrics/AbcSize end diff --git a/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/index.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/index.json.jbuilder new file mode 100644 index 000000000..8d8d3f8c5 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/index.json.jbuilder @@ -0,0 +1,11 @@ +json.payload do + json.array! @deliveries do |delivery| + json.partial! 'api/v1/models/captain/lifecycle_delivery', resource: delivery + end +end + +json.meta do + json.total_count @total_count + json.page @page + json.per_page @per_page +end diff --git a/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/show.json.jbuilder b/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/show.json.jbuilder new file mode 100644 index 000000000..42d6e80aa --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/captain/lifecycle_deliveries/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/v1/models/captain/lifecycle_delivery', resource: @delivery diff --git a/enterprise/app/views/api/v1/models/captain/_lifecycle_delivery.json.jbuilder b/enterprise/app/views/api/v1/models/captain/_lifecycle_delivery.json.jbuilder new file mode 100644 index 000000000..8d924ebb3 --- /dev/null +++ b/enterprise/app/views/api/v1/models/captain/_lifecycle_delivery.json.jbuilder @@ -0,0 +1,24 @@ +json.id resource.id +json.account_id resource.account_id +json.lifecycle_rule_id resource.lifecycle_rule_id +json.lifecycle_rule_name resource.lifecycle_rule&.name +json.captain_reservation_id resource.captain_reservation_id +json.conversation_id resource.conversation_id +json.inbox_id resource.inbox_id +json.fire_at resource.fire_at&.iso8601 +json.sent_at resource.sent_at&.iso8601 +json.status resource.status +json.skip_reason resource.skip_reason +json.failure_reason resource.failure_reason +json.rendered_body resource.rendered_body +json.origin resource.origin + +if resource.captain_reservation + json.reservation do + json.id resource.captain_reservation.id + json.suite_identifier resource.captain_reservation.suite_identifier + contact = resource.captain_reservation.contact + json.customer_name contact&.name + json.customer_phone contact&.phone_number + end +end diff --git a/spec/enterprise/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller_spec.rb new file mode 100644 index 000000000..bfd45efa2 --- /dev/null +++ b/spec/enterprise/controllers/api/v1/accounts/captain/lifecycle_deliveries_controller_spec.rb @@ -0,0 +1,55 @@ +require 'rails_helper' + +RSpec.describe 'Api::V1::Accounts::Captain::LifecycleDeliveries', type: :request do + let(:account) { create(:account) } + let(:admin) { create(:user, account: account, role: :administrator) } + let(:unit) { create(:captain_unit, account: account) } + let(:reservation) { create(:captain_reservation, account: account, unit: unit) } + + def json_response + JSON.parse(response.body, symbolize_names: true) + end + + describe 'GET /api/v1/accounts/:account_id/captain/lifecycle_deliveries' do + before do + allow(Captain::Lifecycle::Scheduler).to receive(:schedule_for) + end + + it 'returns deliveries of the account, paginated' do + create_list(:captain_lifecycle_delivery, 3, account: account, captain_reservation: reservation) + get "/api/v1/accounts/#{account.id}/captain/lifecycle_deliveries", + headers: admin.create_new_auth_token, as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:payload].length).to eq(3) + expect(json_response[:meta][:total_count]).to eq(3) + end + + it 'filters by status' do + create(:captain_lifecycle_delivery, account: account, captain_reservation: reservation, status: 'sent', sent_at: Time.current) + create(:captain_lifecycle_delivery, account: account, captain_reservation: reservation, status: 'skipped', skip_reason: 'quiet_hours') + + get "/api/v1/accounts/#{account.id}/captain/lifecycle_deliveries?status=skipped", + headers: admin.create_new_auth_token, as: :json + + expect(json_response[:payload].length).to eq(1) + expect(json_response[:payload].first[:status]).to eq('skipped') + end + end + + describe 'GET /api/v1/accounts/:account_id/captain/lifecycle_deliveries/:id' do + before { allow(Captain::Lifecycle::Scheduler).to receive(:schedule_for) } + + it 'returns the rendered_body' do + delivery = create(:captain_lifecycle_delivery, + account: account, + captain_reservation: reservation, + rendered_body: 'Oi João!') + get "/api/v1/accounts/#{account.id}/captain/lifecycle_deliveries/#{delivery.id}", + headers: admin.create_new_auth_token, as: :json + + expect(response).to have_http_status(:success) + expect(json_response[:rendered_body]).to eq('Oi João!') + end + end +end