Files
sure/app/controllers/api/v1/merchants_controller.rb
Jose 69d9f51d57 # test(api): Add request specs for merchants and tags endpoints (#645)
* Add files via upload

Signed-off-by: Jose <39016041+jospaquim@users.noreply.github.com>

* Add merchants and tags resources to routes

Signed-off-by: Jose <39016041+jospaquim@users.noreply.github.com>

* update

* update spaces

* fix: Apply CodeRabbit suggestions and add YARD documentation

* docs: Add API documentation for merchants and tags endpoints

* fix: Address CodeRabbit feedback on documentation

* fix: Use authorize_scope! instead of ensure_read_scope

* test(api): Add request specs for merchants and tags endpoints

* test(api): Add request specs for merchants and tags endpoints

* test(api): Convert specs to Minitest format in test/

* fix: Correct indentation for private methods

* fix: merchant and tag test

* Enhance tag tests for family scope and access

Added tests to ensure tags from other families are not returned and that attempts to access them return 404.

Signed-off-by: Jose <39016041+jospaquim@users.noreply.github.com>

* Enhance merchants controller tests for family scope

Added tests to ensure that merchants from other families are not returned in the index action and that accessing a merchant from another family returns a 404 error.

Signed-off-by: Jose <39016041+jospaquim@users.noreply.github.com>

* Fix test/implementation

* Remove old token test code

* Improve test

---------

Signed-off-by: Jose <39016041+jospaquim@users.noreply.github.com>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-01-23 10:18:22 +01:00

86 lines
2.8 KiB
Ruby

# frozen_string_literal: true
module Api
module V1
# API v1 endpoint for merchants
# Provides read-only access to family and provider merchants
#
# @example List all merchants
# GET /api/v1/merchants
#
# @example Get a specific merchant
# GET /api/v1/merchants/:id
#
class MerchantsController < BaseController
before_action -> { authorize_scope!(:read) }
# List all merchants available to the family
#
# Returns both family-owned merchants and provider merchants
# that are assigned to the family's transactions.
#
# @return [Array<Hash>] JSON array of merchant objects
def index
family = current_resource_owner.family
# Single query with OR conditions - more efficient than Ruby deduplication
family_merchant_ids = family.merchants.select(:id)
provider_merchant_ids = family.transactions.select(:merchant_id)
@merchants = Merchant
.where(id: family_merchant_ids)
.or(Merchant.where(id: provider_merchant_ids, type: "ProviderMerchant"))
.distinct
.alphabetically
render json: @merchants.map { |m| merchant_json(m) }
rescue StandardError => e
Rails.logger.error("API Merchants Error: #{e.message}")
render json: { error: "Failed to fetch merchants" }, status: :internal_server_error
end
# Get a specific merchant by ID
#
# Returns a merchant if it belongs to the family or is assigned
# to any of the family's transactions.
#
# @param id [String] The merchant ID
# @return [Hash] JSON merchant object or error
def show
family = current_resource_owner.family
@merchant = family.merchants.find_by(id: params[:id]) ||
Merchant.joins(transactions: :entry)
.where(entries: { account_id: family.accounts.select(:id) })
.distinct
.find_by(id: params[:id])
if @merchant
render json: merchant_json(@merchant)
else
render json: { error: "Merchant not found" }, status: :not_found
end
rescue StandardError => e
Rails.logger.error("API Merchant Show Error: #{e.message}")
render json: { error: "Failed to fetch merchant" }, status: :internal_server_error
end
private
# Serialize a merchant to JSON format
#
# @param merchant [Merchant] The merchant to serialize
# @return [Hash] JSON-serializable hash
def merchant_json(merchant)
{
id: merchant.id,
name: merchant.name,
type: merchant.type,
created_at: merchant.created_at,
updated_at: merchant.updated_at
}
end
end
end
end