Skip to content

Commit 777b6fa

Browse files
committed
Add admin_metadata to attributes for admins
Introduce the `admin_metadata` attribute to support dynamic metadata handling for admins. It enables configuration of metadata via API preferences, and ensures that metadata fields are properly permitted for admin users through new helper methods. **Implementation:** - Added `admin_metadata` to resources for admins. - Integrated `metadata_permit_parameters` and `metadata_api_parameters preference` in API configuration. - Implemented `permitted_<resource>_attributes` and `<method_name>_attributes` methods for dynamic metadata permissions. - Added `extract_metadata` in `line_items_controller` to handle metadata fields.
1 parent 678750d commit 777b6fa

File tree

12 files changed

+232
-6
lines changed

12 files changed

+232
-6
lines changed

api/app/controllers/spree/api/base_controller.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class BaseController < ActionController::Base
1818
class_attribute :admin_line_item_attributes
1919
self.admin_line_item_attributes = [:price, :variant_id, :sku]
2020

21+
class_attribute :admin_metadata_attributes
22+
self.admin_metadata_attributes = [{ admin_metadata: {} }]
23+
2124
attr_accessor :current_api_user
2225

2326
before_action :load_user
@@ -35,15 +38,29 @@ class BaseController < ActionController::Base
3538

3639
private
3740

41+
Spree::Api::Config.metadata_permit_parameters.each do |resource|
42+
define_method("permitted_#{resource.to_s.underscore}_attributes") do
43+
if can?(:admin, "Spree::#{resource}".constantize)
44+
super() + admin_metadata_attributes
45+
else
46+
super()
47+
end
48+
end
49+
end
50+
3851
# users should be able to set price when importing orders via api
3952
def permitted_line_item_attributes
4053
if can?(:admin, Spree::LineItem)
41-
super + admin_line_item_attributes
54+
super + admin_line_item_attributes + admin_metadata_attributes
4255
else
4356
super
4457
end
4558
end
4659

60+
def permitted_user_attributes
61+
can?(:admin, Spree.user_class) ? super + admin_metadata_attributes : super
62+
end
63+
4764
def load_user
4865
@current_api_user ||= Spree.user_class.find_by(spree_api_key: api_key.to_s)
4966
end

api/app/controllers/spree/api/line_items_controller.rb

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ def create
1515
@line_item = @order.contents.add(
1616
variant,
1717
params[:line_item][:quantity] || 1,
18-
options: line_item_params[:options].to_h
18+
options: line_item_params[:options].to_h,
19+
**extract_metadata
1920
)
21+
2022
respond_with(@line_item, status: 201, default_template: :show)
2123
rescue ActiveRecord::RecordInvalid => error
2224
invalid_resource!(error.record)
@@ -56,10 +58,21 @@ def line_items_attributes
5658
{ line_items_attributes: {
5759
id: params[:id],
5860
quantity: params[:line_item][:quantity],
59-
options: line_item_params[:options] || {}
61+
options: line_item_params[:options] || {},
62+
**extract_metadata
6063
} }
6164
end
6265

66+
def extract_metadata
67+
metadata = { customer_metadata: line_item_params[:customer_metadata] }
68+
69+
if @current_user_roles&.include?("admin")
70+
metadata[:admin_metadata] = line_item_params[:admin_metadata]
71+
end
72+
73+
metadata
74+
end
75+
6376
def line_item_params
6477
params.require(:line_item).permit(permitted_line_item_attributes)
6578
end

api/app/helpers/spree/api/api_helpers.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ module ApiHelpers
4343
end
4444
end
4545

46+
Spree::Api::Config.metadata_api_parameters.each do |method_name, resource|
47+
define_method("#{method_name}_attributes") do
48+
authorized_attributes(resource, "#{method_name}_attributes")
49+
end
50+
end
51+
52+
def authorized_attributes(resource, config_attribute)
53+
can?(:admin, resource) ? Spree::Api::Config.public_send(config_attribute) + [:admin_metadata] : Spree::Api::Config.public_send(config_attribute)
54+
end
55+
4656
def required_fields_for(model)
4757
required_fields = model._validators.select do |_field, validations|
4858
validations.any? { |validation| validation.is_a?(ActiveModel::Validations::PresenceValidator) }

api/lib/spree/api_configuration.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ class ApiConfiguration < Preferences::Configuration
4141

4242
preference :line_item_attributes, :array, default: [:id, :quantity, :price, :variant_id, :customer_metadata]
4343

44+
# Spree::Api::Config.metadata_api_parameters contains the models
45+
# to which the admin_metadata attribute is added
46+
preference :metadata_api_parameters, :array, default: [
47+
[:order, 'Spree::Order'],
48+
[:customer_return, 'Spree::CustomerReturn'],
49+
[:payment, 'Spree::Payment'],
50+
[:return_authorization, 'Spree::ReturnAuthorization'],
51+
[:shipment, 'Spree::Shipment'],
52+
[:user, 'Spree.user_class'],
53+
[:line_item, 'Spree::LineItem']
54+
]
55+
56+
# Spree::Api::Config.metadata_permit_parameters contains the models
57+
# to which the admin_metadata attribute is permitted
58+
preference :metadata_permit_parameters, :array, default: [
59+
:Order,
60+
:CustomerReturn,
61+
:Payment,
62+
:ReturnAuthorization,
63+
:Shipment
64+
]
65+
4466
preference :option_type_attributes, :array, default: [:id, :name, :presentation, :position]
4567

4668
preference :payment_attributes, :array, default: [

api/spec/requests/spree/api/customer_returns_spec.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ module Spree::Api
5959
expect(json_response).to have_attributes(attributes)
6060
end
6161

62+
it "can view admin_metadata" do
63+
customer_return = FactoryBot.create(:customer_return)
64+
65+
get spree.api_order_customer_return_path(customer_return.order, customer_return.id)
66+
67+
expect(json_response).to have_key('admin_metadata')
68+
end
69+
6270
it "can get a list of customer returns" do
6371
FactoryBot.create(:customer_return, shipped_order: order)
6472
FactoryBot.create(:customer_return, shipped_order: order)
@@ -97,7 +105,7 @@ module Spree::Api
97105
it "can learn how to create a new customer return" do
98106
get spree.new_api_order_customer_return_path(order)
99107

100-
expect(json_response["attributes"]).to eq(["id", "number", "stock_location_id", "created_at", "updated_at"])
108+
expect(json_response["attributes"]).to eq(["id", "number", "stock_location_id", "created_at", "updated_at", "customer_metadata", "admin_metadata"])
101109
end
102110

103111
it "can update a customer return" do
@@ -112,6 +120,23 @@ module Spree::Api
112120
expect(json_response["stock_location_id"]).to eq final_stock_location.id
113121
end
114122

123+
it "can update a customer return admin_metadata" do
124+
initial_stock_location = FactoryBot.create(:stock_location)
125+
final_stock_location = FactoryBot.create(:stock_location)
126+
customer_return = FactoryBot.create(:customer_return, stock_location: initial_stock_location)
127+
128+
put spree.api_order_customer_return_path(customer_return.order, customer_return.id),
129+
params: {
130+
order_id: customer_return.order.number,
131+
customer_return: { stock_location_id: final_stock_location.id, admin_metadata: { 'order_number' => 'PN345678' } }
132+
}
133+
134+
expect(response.status).to eq(200)
135+
expect(json_response).to have_attributes(attributes)
136+
expect(json_response["stock_location_id"]).to eq final_stock_location.id
137+
expect(json_response["admin_metadata"]).to eq({ 'order_number' => 'PN345678' })
138+
end
139+
115140
context "when creating new return items" do
116141
it "can create a new customer return" do
117142
stock_location = FactoryBot.create(:stock_location)

api/spec/requests/spree/api/line_items_spec.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,34 @@ module Spree::Api
228228
end
229229
end
230230

231+
context "as an admin" do
232+
sign_in_as_admin!
233+
234+
it "can see admin_metadata" do
235+
post spree.api_order_line_items_path(order),
236+
params: {
237+
line_item: { variant_id: product.master.to_param, quantity: 1 },
238+
order_token: order.guest_token
239+
}
240+
241+
expect(response.status).to eq(201)
242+
expect(json_response).to have_key('admin_metadata')
243+
end
244+
245+
it "allows creating line item with customer metadata and admin metadata" do
246+
post spree.api_order_line_items_path(order),
247+
params: {
248+
line_item: { variant_id: product.master.to_param,
249+
quantity: 1,
250+
customer_metadata: { "Company" => "Sample Company" },
251+
admin_metadata: { "discount" => "not_applicable" } }
252+
}
253+
254+
expect(json_response['customer_metadata']).to eq({ "Company" => "Sample Company" })
255+
expect(json_response['admin_metadata']).to eq({ "discount" => "not_applicable" })
256+
end
257+
end
258+
231259
context "as just another user" do
232260
before do
233261
user = create(:user)

api/spec/requests/spree/api/orders_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,32 @@ module Spree::Api
987987
expect(json_response['error']).to eq(I18n.t(:could_not_transition, scope: "spree.api", resource: 'order'))
988988
expect(response.status).to eq(422)
989989
end
990+
991+
it "can update a order admin_metadata" do
992+
put spree.api_order_path(order), params: { order: { admin_metadata: { 'order_number' => 'PN345678' } } }
993+
994+
expect(response.status).to eq(200)
995+
996+
expect(json_response["admin_metadata"]).to eq({ 'order_number' => 'PN345678' })
997+
end
998+
999+
it "can update customer metadata if the order is complete" do
1000+
order = create(:order)
1001+
order.completed_at = Time.current
1002+
order.state = 'complete'
1003+
order.save!
1004+
1005+
put spree.api_order_path(order), params: { order: { customer_metadata: { 'Note' => 'Do not ring the bell' }, admin_metadata: { 'order_number' => 'PN345678' } } }
1006+
1007+
expect(json_response['customer_metadata']).to eq({ 'Note' => 'Do not ring the bell' })
1008+
expect(json_response["admin_metadata"]).to eq({ 'order_number' => 'PN345678' })
1009+
end
1010+
1011+
it "can view admin_metadata" do
1012+
get spree.api_order_path(order)
1013+
1014+
expect(json_response).to have_key('admin_metadata')
1015+
end
9901016
end
9911017

9921018
context "can cancel an order" do

api/spec/requests/spree/api/payments_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,26 @@ module Spree::Api
187187
expect(response.status).to eq(403)
188188
expect(json_response["error"]).to eq("This payment cannot be updated because it is completed.")
189189
end
190+
191+
it "can update a payment admin_metadata" do
192+
payment.update(state: 'pending')
193+
194+
put spree.api_order_payment_path(order, payment),
195+
params: {
196+
payment: {
197+
amount: 2.01,
198+
admin_metadata: { 'order_number' => 'PN345678' }
199+
}
200+
}
201+
202+
expect(response.status).to eq(200)
203+
expect(json_response["admin_metadata"]).to eq({ 'order_number' => 'PN345678' })
204+
end
205+
206+
it "can view admin_metadata" do
207+
get spree.api_order_payments_path(order)
208+
expect(json_response["payments"].first).to have_key('admin_metadata')
209+
end
190210
end
191211
end
192212

api/spec/requests/spree/api/return_authorizations_spec.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ module Spree::Api
121121

122122
it "can learn how to create a new return authorization" do
123123
get spree.new_api_order_return_authorization_path(order)
124-
expect(json_response["attributes"]).to eq(["id", "number", "state", "order_id", "memo", "created_at", "updated_at"])
124+
expect(json_response["attributes"]).to eq(["id", "number", "state", "order_id", "memo", "created_at", "updated_at", "customer_metadata", "admin_metadata"])
125125
required_attributes = json_response["required_attributes"]
126126
expect(required_attributes).to include("order")
127127
end
@@ -151,6 +151,31 @@ module Spree::Api
151151
expect { return_authorization.reload }.to raise_error(ActiveRecord::RecordNotFound)
152152
end
153153

154+
it "can view admin_metadata" do
155+
FactoryBot.create(:return_authorization, order:)
156+
157+
return_authorization = order.return_authorizations.first
158+
159+
get spree.api_order_return_authorization_path(order, return_authorization.id)
160+
161+
expect(response.status).to eq(200)
162+
expect(json_response).to have_attributes(attributes)
163+
expect(json_response).to have_key('admin_metadata')
164+
end
165+
166+
it "can update a return authorization on the order and metadata" do
167+
FactoryBot.create(:return_authorization, order:)
168+
169+
return_authorization = order.return_authorizations.first
170+
171+
put spree.api_order_return_authorization_path(order, return_authorization.id), params: { return_authorization: { memo: "ABC", admin_metadata: { 'return_type' => 'warranty claim' }, customer_metadata: { 'return_reason' => 'product not working' } } }
172+
173+
expect(response.status).to eq(200)
174+
expect(json_response).to have_attributes(attributes)
175+
expect(json_response["admin_metadata"]).to eq({ 'return_type' => 'warranty claim' })
176+
expect(json_response["customer_metadata"]).to eq({ 'return_reason' => 'product not working' })
177+
end
178+
154179
it_behaves_like "a return authorization creator"
155180
end
156181

api/spec/requests/spree/api/shipments_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ module Spree::Api
9292
expect(json_response['stock_location_name']).to eq(stock_location.name)
9393
end
9494

95+
it "can update a shipment and it's metadata" do
96+
params = {
97+
shipment: {
98+
stock_location_id: stock_location.to_param,
99+
admin_metadata: { 'delivery_type' => 'express' },
100+
customer_metadata: { 'timing' => 'standard' }
101+
}
102+
}
103+
104+
put(spree.api_shipment_path(shipment), params:)
105+
106+
expect(response.status).to eq(200)
107+
expect(json_response['stock_location_name']).to eq(stock_location.name)
108+
expect(json_response["admin_metadata"]).to eq({ 'delivery_type' => 'express' })
109+
expect(json_response["customer_metadata"]).to eq({ 'timing' => 'standard' })
110+
end
111+
95112
it "can make a shipment ready" do
96113
allow_any_instance_of(Spree::Order).to receive_messages(paid?: true, complete?: true)
97114
put spree.ready_api_shipment_path(shipment)

0 commit comments

Comments
 (0)