spree_api 3.6.6 → 3.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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