Add balance to api v1 customers endpoint

- customers#show: Add balance (data_type: double) to customer attributes.
- customers#index: Add balance only if specified in extra_fields
query parameter: extra_fields[customer]=balance
This commit is contained in:
Mikael Norlén
2023-02-13 11:55:18 +01:00
parent 3989843a21
commit e95d08cae8
6 changed files with 184 additions and 4 deletions

View File

@@ -6,11 +6,17 @@ module Api
module V1
class CustomersController < Api::V1::BaseController
include AddressTransformation
include ExtraFields
skip_authorization_check only: :index
before_action :authorize_action, only: [:show, :update, :destroy]
# Query parameters
before_action only: [:index] do
@extra_customer_fields = extra_fields :customer, [:balance]
end
def index
@pagy, customers = pagy(search_customers, pagy_options)
@@ -51,7 +57,11 @@ module Api
private
def customer
@customer ||= Customer.find(params[:id])
@customer ||= if action_name == "show"
CustomersWithBalance.new(Customer.where(id: params[:id])).query.first!
else
Customer.find(params[:id])
end
end
def authorize_action
@@ -61,6 +71,11 @@ module Api
def search_customers
customers = visible_customers.includes(:bill_address, :ship_address)
customers = customers.where(enterprise_id: params[:enterprise_id]) if params[:enterprise_id]
if @extra_customer_fields.include?(:balance)
customers = CustomersWithBalance.new(customers).query
end
customers.ransack(params[:q]).result.order(:id)
end

View File

@@ -61,4 +61,9 @@ class CustomerSchema < JsonApiSchema
def self.relationships
[:enterprise]
end
# Optional attributes included with eg: CustomerSchema.schema(extra_fields: :balance)
def self.balance
{ balance: { type: :number, format: :double } }
end
end

View File

@@ -16,6 +16,10 @@ module Api
address(object.shipping_address)
end
attribute :balance, if: proc { |record|
record.respond_to?(:balance_value)
}, &:balance_value
belongs_to :enterprise, links: {
related: ->(object) {
url_helpers.api_v1_enterprise_url(id: object.enterprise_id)

View File

@@ -26,10 +26,13 @@ describe "Customers", type: :request do
get "List customers" do
tags "Customers"
parameter name: :enterprise_id, in: :query, type: :string
parameter name: "extra_fields[customer]", in: :query, type: :string, example: :balance,
description: "Add extra fields to each customer"
produces "application/json"
response "200", "Customers list" do
param(:enterprise_id) { enterprise1.id }
param("extra_fields[customer]") { :balance }
schema "$ref": "#/components/schemas/customers_collection"
run_test!
@@ -105,6 +108,33 @@ describe "Customers", type: :request do
end
end
describe "query parameters" do
describe "extra_fields[customer]" do
context "with balance" do
it "adds balance to each customer" do
get "/api/v1/customers", params: { extra_fields: { customer: :balance } }
balances = json_response[:data].map{ |c| c[:attributes][:balance] }
expect(balances.all?{ |b| b.is_a? Numeric }).to eq(true)
end
end
context "with unknown field" do
it "returns unprocessable entity" do
get "/api/v1/customers", params: { extra_fields: { customer: :unknown } }
expect([response.status, json_error_detail]).to eq [422, "Unsupported fields: unknown"]
end
end
context "when not recevied" do
it "does not add balances" do
get "/api/v1/customers"
balances = json_response[:data].map{ |c| c[:attributes][:balance] }
expect([response.status, balances.compact]).to eq [200, []]
end
end
end
end
post "Create customer" do
tags "Customers"
consumes "application/json"
@@ -191,7 +221,10 @@ describe "Customers", type: :request do
response "200", "Customer" do
param(:id) { customer1.id }
schema "$ref": "#/components/schemas/customer"
schema CustomerSchema.schema(
require_all: true,
extra_fields: { name: :balance, required: true }
)
run_test! do
date_time_string =

View File

@@ -27,8 +27,9 @@ RSpec.configure do |config|
components: {
schemas: {
error_response: ErrorsSchema.schema,
# only customer#show is with extra_fields: {name: :balance, required: true}
customer: CustomerSchema.schema(require_all: true),
customers_collection: CustomerSchema.collection(require_all: true)
customers_collection: CustomerSchema.collection(require_all: true, extra_fields: :balance)
},
securitySchemes: {
api_key_header: {

View File

@@ -210,6 +210,9 @@ components:
country:
code: AU
name: Australia
balance:
type: number
format: double
required:
- id
- enterprise_id
@@ -307,6 +310,12 @@ paths:
in: query
schema:
type: string
- name: extra_fields[customer]
in: query
example: balance
description: Add extra fields to each customer
schema:
type: string
responses:
'200':
description: Customers list
@@ -406,7 +415,120 @@ paths:
content:
application/json:
schema:
"$ref": "#/components/schemas/customer"
type: object
properties:
data:
type: object
properties:
id:
type: string
example: '1'
type:
type: string
example: customer
attributes:
type: object
properties:
id:
type: integer
example: 1
enterprise_id:
type: integer
example: 2
first_name:
type: string
nullable: true
example: Alice
last_name:
type: string
nullable: true
example: Springs
code:
type: string
nullable: true
example: BUYER1
email:
type: string
example: alice@example.com
allow_charges:
type: boolean
example: false
tags:
type: array
items:
type: string
example:
- staff
- discount
terms_and_conditions_accepted_at:
type: string
format: date-time
nullable: true
example: '2022-03-12T15:55:00.000+11:00'
billing_address:
type: object
nullable: true
example:
shipping_address:
type: object
nullable: true
example:
phone: 0404 333 222 111
latitude: -37.8173751
longitude: 144.964803195704
first_name: Alice
last_name: Springs
street_address_1: 1 Flinders Street
street_address_2: ''
postal_code: '1234'
locality: Melbourne
region:
code: Vic
name: Victoria
country:
code: AU
name: Australia
balance:
type: number
format: double
required:
- id
- enterprise_id
- first_name
- last_name
- code
- email
- allow_charges
- tags
- terms_and_conditions_accepted_at
- billing_address
- shipping_address
- balance
relationships:
type: object
properties:
enterprise:
type: object
properties:
data:
type: object
properties:
id:
type: string
type:
type: string
example: enterprise
links:
type: object
properties:
related:
type: string
meta:
type: object
links:
type: object
required:
- data
'404':
description: Not found
content: