spree_api 3.6.6 → 3.7.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree/api/main.js +36 -0
  3. data/app/assets/javascripts/spree/api/storefront/cart.js +49 -0
  4. data/app/controllers/concerns/spree/api/v2/storefront/order_concern.rb +48 -0
  5. data/app/controllers/spree/api/errors_controller.rb +9 -0
  6. data/app/controllers/spree/api/v1/checkouts_controller.rb +12 -0
  7. data/app/controllers/spree/api/v1/customer_returns_controller.rb +1 -0
  8. data/app/controllers/spree/api/v1/line_items_controller.rb +11 -10
  9. data/app/controllers/spree/api/v1/orders_controller.rb +6 -6
  10. data/app/controllers/spree/api/v1/product_properties_controller.rb +1 -0
  11. data/app/controllers/spree/api/v1/products_controller.rb +6 -6
  12. data/app/controllers/spree/api/v1/promotions_controller.rb +1 -0
  13. data/app/controllers/spree/api/v1/reimbursements_controller.rb +1 -0
  14. data/app/controllers/spree/api/v1/shipments_controller.rb +12 -3
  15. data/app/controllers/spree/api/v1/variants_controller.rb +5 -3
  16. data/app/controllers/spree/api/v2/base_controller.rb +94 -0
  17. data/app/controllers/spree/api/v2/storefront/account/credit_cards_controller.rb +55 -0
  18. data/app/controllers/spree/api/v2/storefront/account_controller.rb +33 -0
  19. data/app/controllers/spree/api/v2/storefront/cart_controller.rb +124 -0
  20. data/app/controllers/spree/api/v2/storefront/checkout_controller.rb +108 -0
  21. data/app/controllers/spree/api/v2/storefront/countries_controller.rb +57 -0
  22. data/app/controllers/spree/api/v2/storefront/products_controller.rb +87 -0
  23. data/app/controllers/spree/api/v2/storefront/taxons_controller.rb +82 -0
  24. data/app/helpers/spree/api/api_helpers.rb +1 -1
  25. data/app/helpers/spree/api/v2/collection_options_helpers.rb +37 -0
  26. data/app/models/doorkeeper/access_grant_decorator.rb +3 -0
  27. data/app/models/doorkeeper/access_token_decorator.rb +3 -0
  28. data/app/models/doorkeeper/application_decorator.rb +3 -0
  29. data/app/serializers/spree/v2/storefront/account/credit_card_serializer.rb +16 -0
  30. data/app/serializers/spree/v2/storefront/account_serializer.rb +29 -0
  31. data/app/serializers/spree/v2/storefront/address_serializer.rb +14 -0
  32. data/app/serializers/spree/v2/storefront/base_serializer.rb +9 -0
  33. data/app/serializers/spree/v2/storefront/cart_serializer.rb +40 -0
  34. data/app/serializers/spree/v2/storefront/country_serializer.rb +18 -0
  35. data/app/serializers/spree/v2/storefront/image_serializer.rb +11 -0
  36. data/app/serializers/spree/v2/storefront/line_item_serializer.rb +17 -0
  37. data/app/serializers/spree/v2/storefront/option_type_serializer.rb +13 -0
  38. data/app/serializers/spree/v2/storefront/option_value_serializer.rb +11 -0
  39. data/app/serializers/spree/v2/storefront/payment_method_serializer.rb +11 -0
  40. data/app/serializers/spree/v2/storefront/payment_serializer.rb +12 -0
  41. data/app/serializers/spree/v2/storefront/product_property_serializer.rb +14 -0
  42. data/app/serializers/spree/v2/storefront/product_serializer.rb +35 -0
  43. data/app/serializers/spree/v2/storefront/promotion_serializer.rb +12 -0
  44. data/app/serializers/spree/v2/storefront/shipment_serializer.rb +16 -0
  45. data/app/serializers/spree/v2/storefront/shipping_rate_serializer.rb +14 -0
  46. data/app/serializers/spree/v2/storefront/state_serializer.rb +11 -0
  47. data/app/serializers/spree/v2/storefront/taxon_image_serializer.rb +11 -0
  48. data/app/serializers/spree/v2/storefront/taxon_serializer.rb +28 -0
  49. data/app/serializers/spree/v2/storefront/taxonomy_serializer.rb +11 -0
  50. data/app/serializers/spree/v2/storefront/user_serializer.rb +11 -0
  51. data/app/serializers/spree/v2/storefront/variant_serializer.rb +21 -0
  52. data/app/views/spree/api/v1/orders/order.v1.rabl +1 -1
  53. data/config/initializers/doorkeeper.rb +20 -0
  54. data/config/locales/en.yml +4 -0
  55. data/config/routes.rb +51 -3
  56. data/db/migrate/20180320110726_create_doorkeeper_tables.rb +69 -0
  57. data/docs/oauth/index.yml +77 -0
  58. data/docs/v2/storefront/index.yaml +2444 -0
  59. data/lib/spree/api/engine.rb +9 -2
  60. data/lib/spree_api.rb +2 -0
  61. data/spree_api.gemspec +5 -1
  62. metadata +92 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 23689ff6a2b514071aba3c4bd533a7901e448ca71603bbb3879d8a33df49e7c9
4
- data.tar.gz: 7e0ab714a65d2e50048951e6593ca23383d2d62d888c3a58036d2b29515f7f2d
3
+ metadata.gz: 966dd14cce417e7119d423b7b05d052b85e0f0372f7bcb04c966f239cf8aafae
4
+ data.tar.gz: e6903c09540a78763157058235d32dc07405747646dc24f2b980ab7df19249d6
5
5
  SHA512:
6
- metadata.gz: ceafa6234fdab1674a7d4dcbfa0b4436d6a901a0a6fb3ac4243ff7d218476683a4eaa713383e35a4fd34a3fd8d9e7d6bbe18040f8957a18718f2b879bc472c63
7
- data.tar.gz: fd88d8f63f688ee351921ad8383ebc77308822e4fbf8dbddcf540b5b270be07f6715de0491eb4d720d2d2d5370091667a686eddb8cfcf89c7d040da3a3a417b2
6
+ metadata.gz: e8ad9bc26e1c98498461bd06975be490627cb53857cb2ca64a01a40e410e866978c7387ee46102d1a39b96f0f751bd14d2da33c3f8928617fb28e12d4b37e4cb
7
+ data.tar.gz: cfc7d9802acc6f4a82bf5ccc56028ed303663fe1495292d2f0a1c507a7da63adcd82dbfbaea29fcf3c334e3c17aab63a811487d33a75c29f1a020671de62d06c
@@ -0,0 +1,36 @@
1
+ //= require spree
2
+
3
+ var SpreeAPI = {
4
+ oauthToken: null, // user Bearer token to authorize operations for the given user
5
+ orderToken: null // order token to authorize operations on current order (cart)
6
+ }
7
+
8
+ SpreeAPI.Storefront = {}
9
+ SpreeAPI.Platform = {}
10
+
11
+ // API routes
12
+ Spree.routes.api_v2_storefront_cart_create = Spree.pathFor('api/v2/storefront/cart')
13
+ Spree.routes.api_v2_storefront_cart_add_item = Spree.pathFor('api/v2/storefront/cart/add_item')
14
+ Spree.routes.api_v2_storefront_cart_apply_coupon_code = Spree.pathFor('api/v2/storefront/cart/apply_coupon_code')
15
+
16
+ // helpers
17
+ SpreeAPI.handle500error = function () {
18
+ alert('Internal Server Error')
19
+ }
20
+
21
+ SpreeAPI.prepareHeaders = function (headers) {
22
+ if (typeof headers === 'undefined') {
23
+ headers = {}
24
+ }
25
+
26
+ // if signed in we need to pass the Bearer authorization token
27
+ // so backend will recognize that actions are authorized in scope of this user
28
+ if (SpreeAPI.oauthToken) {
29
+ headers['Authorization'] = 'Bearer ' + SpreeAPI.oauthToken
30
+ }
31
+
32
+ // default headers, required for POST/PATCH/DELETE requests
33
+ headers['Accept'] = 'application/json'
34
+ headers['Content-Type'] = 'application/json'
35
+ return headers
36
+ }
@@ -0,0 +1,49 @@
1
+ //= require spree/api/main
2
+
3
+ SpreeAPI.Storefront.createCart = function (successCallback, failureCallback) {
4
+ fetch(Spree.routes.api_v2_storefront_cart_create, {
5
+ method: 'POST',
6
+ headers: SpreeAPI.prepareHeaders()
7
+ }).then(function (response) {
8
+ switch (response.status) {
9
+ case 422:
10
+ response.json().then(function (json) { failureCallback(json.error) })
11
+ break
12
+ case 500:
13
+ SpreeAPI.handle500error()
14
+ break
15
+ case 201:
16
+ response.json().then(function (json) {
17
+ SpreeAPI.orderToken = json.data.attributes.token
18
+ successCallback()
19
+ })
20
+ break
21
+ }
22
+ })
23
+ }
24
+
25
+ SpreeAPI.Storefront.addToCart = function (variantId, quantity, options, successCallback, failureCallback) {
26
+ fetch(Spree.routes.api_v2_storefront_cart_add_item, {
27
+ method: 'POST',
28
+ headers: SpreeAPI.prepareHeaders({ 'X-Spree-Order-Token': SpreeAPI.orderToken }),
29
+ body: JSON.stringify({
30
+ variant_id: variantId,
31
+ quantity: quantity,
32
+ options: options
33
+ })
34
+ }).then(function (response) {
35
+ switch (response.status) {
36
+ case 422:
37
+ response.json().then(function (json) { failureCallback(json.error) })
38
+ break
39
+ case 500:
40
+ SpreeAPI.handle500error()
41
+ break
42
+ case 200:
43
+ response.json().then(function (json) {
44
+ successCallback(json.data)
45
+ })
46
+ break
47
+ }
48
+ })
49
+ }
@@ -0,0 +1,48 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Storefront
5
+ module OrderConcern
6
+ private
7
+
8
+ def render_order(result)
9
+ if result.success?
10
+ render_serialized_payload { serialized_current_order }
11
+ else
12
+ render_error_payload(result.error)
13
+ end
14
+ end
15
+
16
+ def ensure_order
17
+ raise ActiveRecord::RecordNotFound if spree_current_order.nil?
18
+ end
19
+
20
+ def order_token
21
+ request.headers['X-Spree-Order-Token'] || params[:order_token]
22
+ end
23
+
24
+ def spree_current_order
25
+ @spree_current_order ||= find_spree_current_order
26
+ end
27
+
28
+ def find_spree_current_order
29
+ Spree::Orders::FindCurrent.new.execute(
30
+ store: spree_current_store,
31
+ user: spree_current_user,
32
+ token: order_token,
33
+ currency: current_currency
34
+ )
35
+ end
36
+
37
+ def serialize_order(order)
38
+ dependencies[:cart_serializer].new(order.reload, include: resource_includes, fields: sparse_fields).serializable_hash
39
+ end
40
+
41
+ def serialized_current_order
42
+ serialize_order(spree_current_order)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ module Spree
2
+ module Api
3
+ class ErrorsController < ActionController::Base
4
+ def render_404
5
+ render 'spree/api/errors/not_found', status: 404
6
+ end
7
+ end
8
+ end
9
+ end
@@ -32,6 +32,8 @@ module Spree
32
32
  @order.associate_user!(Spree.user_class.find(user_id))
33
33
  end
34
34
 
35
+ log_state_changes if params[:state]
36
+
35
37
  return if after_update_attributes
36
38
 
37
39
  if @order.completed? || @order.next
@@ -91,6 +93,16 @@ module Spree
91
93
  false
92
94
  end
93
95
 
96
+ def log_state_changes
97
+ if @order.previous_changes[:state]
98
+ @order.log_state_changes(
99
+ state_name: 'order',
100
+ old_state: @order.previous_changes[:state].first,
101
+ new_state: @order.previous_changes[:state].last
102
+ )
103
+ end
104
+ end
105
+
94
106
  def order_id
95
107
  super || params[:id]
96
108
  end
@@ -11,6 +11,7 @@ module Spree
11
11
 
12
12
  def collection(resource)
13
13
  return @collection if @collection.present?
14
+
14
15
  params[:q] ||= {}
15
16
 
16
17
  @collection = resource.all
@@ -10,12 +10,11 @@ module Spree
10
10
 
11
11
  def create
12
12
  variant = Spree::Variant.find(params[:line_item][:variant_id])
13
- @line_item = order.contents.add(
14
- variant,
15
- params[:line_item][:quantity] || 1,
16
- line_item_params[:options] || {}
17
- )
18
13
 
14
+ @line_item = Spree::Cart::AddItem.call(order: order,
15
+ variant: variant,
16
+ quantity: params[:line_item][:quantity],
17
+ options: line_item_params[:options]).value
19
18
  if @line_item.errors.empty?
20
19
  respond_with(@line_item, status: 201, default_template: :show)
21
20
  else
@@ -25,7 +24,8 @@ module Spree
25
24
 
26
25
  def update
27
26
  @line_item = find_line_item
28
- if @order.contents.update_cart(line_items_attributes)
27
+
28
+ if Spree::Cart::Update.call(order: @order, params: line_items_attributes).success?
29
29
  @line_item.reload
30
30
  respond_with(@line_item, default_template: :show)
31
31
  else
@@ -35,7 +35,8 @@ module Spree
35
35
 
36
36
  def destroy
37
37
  @line_item = find_line_item
38
- @order.contents.remove_line_item(@line_item)
38
+ Spree::Cart::RemoveLineItem.new.call(order: @order, line_item: @line_item)
39
+
39
40
  respond_with(@line_item, status: 204)
40
41
  end
41
42
 
@@ -54,9 +55,9 @@ module Spree
54
55
 
55
56
  def line_items_attributes
56
57
  { line_items_attributes: {
57
- id: params[:id],
58
- quantity: params[:line_item][:quantity],
59
- options: line_item_params[:options] || {}
58
+ id: params[:id],
59
+ quantity: params[:line_item][:quantity],
60
+ options: line_item_params[:options] || {}
60
61
  } }
61
62
  end
62
63
 
@@ -30,10 +30,10 @@ module Spree
30
30
  authorize! :create, Spree::Order
31
31
  if can?(:admin, Spree::Order)
32
32
  order_user = if @current_user_roles.include?('admin') && order_params[:user_id]
33
- Spree.user_class.find(order_params[:user_id])
34
- else
35
- current_api_user
36
- end
33
+ Spree.user_class.find(order_params[:user_id])
34
+ else
35
+ current_api_user
36
+ end
37
37
 
38
38
  import_params = if @current_user_roles.include?('admin')
39
39
  params[:order].present? ? params[:order].permit! : {}
@@ -46,7 +46,7 @@ module Spree
46
46
  respond_with(@order, default_template: :show, status: 201)
47
47
  else
48
48
  @order = Spree::Order.create!(user: current_api_user, store: current_store)
49
- if @order.contents.update_cart(order_params)
49
+ if Cart::Update.call(order: @order, params: order_params).success?
50
50
  respond_with(@order, default_template: :show, status: 201)
51
51
  else
52
52
  invalid_resource!(@order)
@@ -75,7 +75,7 @@ module Spree
75
75
  find_order(true)
76
76
  authorize! :update, @order, order_token
77
77
 
78
- if @order.contents.update_cart(order_params)
78
+ if Cart::Update.call(order: @order, params: order_params).success?
79
79
  user_id = params[:order][:user_id]
80
80
  if current_api_user.has_spree_role?('admin') && user_id
81
81
  @order.associate_user!(Spree.user_class.find(user_id))
@@ -59,6 +59,7 @@ module Spree
59
59
  @product_property ||= @product.product_properties.find_by(id: params[:id])
60
60
  @product_property ||= @product.product_properties.includes(:property).where(spree_properties: { name: params[:id] }).first
61
61
  raise ActiveRecord::RecordNotFound unless @product_property
62
+
62
63
  authorize! :read, @product_property
63
64
  end
64
65
  end
@@ -5,11 +5,11 @@ module Spree
5
5
  before_action :find_product, only: [:update, :show, :destroy]
6
6
 
7
7
  def index
8
- if params[:ids]
9
- @products = product_scope.where(id: params[:ids].split(',').flatten)
10
- else
11
- @products = product_scope.ransack(params[:q]).result
12
- end
8
+ @products = if params[:ids]
9
+ product_scope.where(id: params[:ids].split(',').flatten)
10
+ else
11
+ product_scope.ransack(params[:q]).result
12
+ end
13
13
 
14
14
  @products = @products.distinct.page(params[:page]).per(params[:per_page])
15
15
  expires_in 15.minutes, public: true
@@ -104,7 +104,7 @@ module Spree
104
104
  :variants
105
105
  else
106
106
  :variants_attributes
107
- end
107
+ end
108
108
 
109
109
  params.require(:product).permit(
110
110
  variants_key => [permitted_variant_attributes, :id]
@@ -17,6 +17,7 @@ module Spree
17
17
 
18
18
  def requires_admin
19
19
  return if @current_user_roles.include?('admin')
20
+
20
21
  unauthorized and return
21
22
  end
22
23
 
@@ -11,6 +11,7 @@ module Spree
11
11
 
12
12
  def collection(resource)
13
13
  return @collection if @collection.present?
14
+
14
15
  params[:q] ||= {}
15
16
 
16
17
  @collection = resource.all
@@ -24,7 +24,11 @@ module Spree
24
24
  authorize! :create, Shipment
25
25
  quantity = params[:quantity].to_i
26
26
  @shipment = @order.shipments.create(stock_location_id: params.fetch(:stock_location_id))
27
- @order.contents.add(variant, quantity, shipment: @shipment)
27
+
28
+ @line_item = Spree::Cart::AddItem.call(order: @order,
29
+ variant: variant,
30
+ quantity: quantity,
31
+ options: { shipment: @shipment }).value
28
32
 
29
33
  respond_with(@shipment.reload, default_template: :show)
30
34
  end
@@ -55,7 +59,11 @@ module Spree
55
59
  def add
56
60
  quantity = params[:quantity].to_i
57
61
 
58
- @shipment.order.contents.add(variant, quantity, shipment: @shipment)
62
+ Spree::Cart::AddItem.call(order: @shipment.order,
63
+ variant: variant,
64
+ quantity: quantity,
65
+ options: { shipment: @shipment })
66
+
59
67
  respond_with(@shipment, default_template: :show)
60
68
  end
61
69
 
@@ -65,7 +73,8 @@ module Spree
65
73
  else
66
74
  @shipment.inventory_units_for(variant).sum(:quantity)
67
75
  end
68
- @shipment.order.contents.remove(variant, quantity, shipment: @shipment)
76
+
77
+ Spree::Cart::RemoveItem.call(order: @shipment.order, variant: variant, quantity: quantity, options: { shipment: @shipment })
69
78
 
70
79
  if @shipment.inventory_units.any?
71
80
  @shipment.reload
@@ -48,8 +48,10 @@ module Spree
48
48
  private
49
49
 
50
50
  def product
51
- @product ||= Spree::Product.accessible_by(current_ability, :read).
52
- friendly.find(params[:product_id]) if params[:product_id]
51
+ if params[:product_id]
52
+ @product ||= Spree::Product.accessible_by(current_ability, :read).
53
+ friendly.find(params[:product_id])
54
+ end
53
55
  end
54
56
 
55
57
  def scope
@@ -63,7 +65,7 @@ module Spree
63
65
  variants = variants.with_deleted
64
66
  end
65
67
 
66
- variants.accessible_by(current_ability, :read)
68
+ variants.eligible.accessible_by(current_ability, :read)
67
69
  end
68
70
 
69
71
  def variant_params
@@ -0,0 +1,94 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ class BaseController < ActionController::API
5
+ include CanCan::ControllerAdditions
6
+ include Spree::Core::ControllerHelpers::StrongParameters
7
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
8
+ rescue_from CanCan::AccessDenied, with: :access_denied
9
+
10
+ private
11
+
12
+ def render_serialized_payload(status = 200)
13
+ render json: yield, status: status
14
+ rescue ArgumentError => exception
15
+ render_error_payload(exception.message, 400)
16
+ end
17
+
18
+ def render_error_payload(error, status = 422)
19
+ if error.is_a?(Struct)
20
+ render json: { error: error.to_s, errors: error.to_h }, status: status
21
+ elsif error.is_a?(String)
22
+ render json: { error: error }, status: status
23
+ end
24
+ end
25
+
26
+ def spree_current_store
27
+ @spree_current_store ||= Spree::Store.current(request.env['SERVER_NAME'])
28
+ end
29
+
30
+ def spree_current_user
31
+ @spree_current_user ||= Spree.user_class.find_by(id: doorkeeper_token.resource_owner_id) if doorkeeper_token
32
+ end
33
+
34
+ def spree_authorize!(action, subject, *args)
35
+ authorize!(action, subject, *args)
36
+ end
37
+
38
+ def require_spree_current_user
39
+ raise CanCan::AccessDenied if spree_current_user.nil?
40
+ end
41
+
42
+ # Needs to be overriden so that we use Spree's Ability rather than anyone else's.
43
+ def current_ability
44
+ @current_ability ||= Spree::Ability.new(spree_current_user)
45
+ end
46
+
47
+ def request_includes
48
+ # if API user want's to receive only the bare-minimum
49
+ # the API will return only the main resource without any included
50
+ if params[:include]&.blank?
51
+ []
52
+ elsif params[:include].present?
53
+ params[:include].split(',')
54
+ end
55
+ end
56
+
57
+ def resource_includes
58
+ (request_includes || default_resource_includes).map(&:intern)
59
+ end
60
+
61
+ # overwrite this method in your controllers to set JSON API default include value
62
+ # https://jsonapi.org/format/#fetching-includes
63
+ # eg.:
64
+ # %w[images variants]
65
+ # ['variant.images', 'line_items']
66
+ def default_resource_includes
67
+ []
68
+ end
69
+
70
+ def sparse_fields
71
+ return unless params[:fields]&.respond_to?(:each)
72
+
73
+ fields = {}
74
+ params[:fields].
75
+ select { |_, v| v.is_a?(String) }.
76
+ each { |type, values| fields[type.intern] = values.split(',').map(&:intern) }
77
+ fields.presence
78
+ end
79
+
80
+ def current_currency
81
+ spree_current_store.default_currency || Spree::Config[:currency]
82
+ end
83
+
84
+ def record_not_found
85
+ render_error_payload(I18n.t(:resource_not_found, scope: 'spree.api'), 404)
86
+ end
87
+
88
+ def access_denied(exception)
89
+ render_error_payload(exception.message, 403)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end