Merge pull request #6960 from andrewpbrett/namespace-api

Namespace existing API endpoints to api/v0/*
This commit is contained in:
Matt-Yorkley
2021-03-30 19:45:27 +02:00
committed by GitHub
101 changed files with 1435 additions and 1320 deletions

View File

@@ -1170,7 +1170,7 @@ Style/FrozenStringLiteralComment:
- 'engines/order_management/app/services/reports/renderers/base.rb'
- 'engines/order_management/app/services/reports/report_data/base.rb'
- 'engines/web/app/controllers/web/angular_templates_controller.rb'
- 'engines/web/app/controllers/web/api/cookies_consent_controller.rb'
- 'engines/web/app/controllers/web/api/v0/cookies_consent_controller.rb'
- 'engines/web/app/controllers/web/application_controller.rb'
- 'engines/web/app/helpers/web/cookies_policy_helper.rb'
- 'engines/web/lib/web/cookies_consent.rb'

View File

@@ -147,7 +147,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm("Are you sure?")
$http(
method: "DELETE"
url: "/api/products/" + product.id
url: "/api/v0/products/" + product.id
).success (data) ->
$scope.products.splice $scope.products.indexOf(product), 1
DirtyProducts.deleteProduct product.id
@@ -162,7 +162,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
if confirm(t("are_you_sure"))
$http(
method: "DELETE"
url: "/api/products/" + product.permalink_live + "/variants/" + variant.id
url: "/api/v0/products/" + product.permalink_live + "/variants/" + variant.id
).success (data) ->
$scope.removeVariant(product, variant)
else

View File

@@ -1,7 +1,7 @@
angular.module('admin.enterpriseFees').directive 'spreeDeleteResource', ->
(scope, element, attrs) ->
if scope.enterprise_fee.id
url = '/api/enterprise_fees/' + scope.enterprise_fee.id
url = '/api/v0/enterprise_fees/' + scope.enterprise_fee.id
html = '<a href="' + url + '" class="delete-resource icon_link icon-trash no-text" data-action="remove" data-confirm="' + t('are_you_sure') + '" url="' + url + '"></a>'
#var html = '<a href="'+url+'" class="delete-resource" data-confirm="Are you sure?"><img alt="Delete" src="/assets/admin/icons/delete.png" /> Delete</a>';
element.append html

View File

@@ -1,5 +1,5 @@
angular.module("admin.indexUtils").factory "resources", ($resource) ->
LineItem = $resource '/api/orders/:order_number/line_items/:line_item_id.json',
LineItem = $resource '/api/v0/orders/:order_number/line_items/:line_item_id.json',
{ order_number: '@order_number', line_item_id: '@line_item_id'},
'update': { method: 'PUT' }
Customer = $resource '/admin/customers/:customer_id.json',

View File

@@ -1,5 +1,5 @@
angular.module('admin.orderCycles').factory('ExchangeProduct', ($resource) ->
ExchangeProductResource = $resource('/api/exchanges/:exchange_id/products.json', {}, {
ExchangeProductResource = $resource('/api/v0/exchanges/:exchange_id/products.json', {}, {
'index': { method: 'GET' }
'variant_count': { method: 'GET', params: { action_name: "variant_count" }}
})

View File

@@ -8,7 +8,7 @@ angular.module("ofn.admin").factory "ProductImageService", (FileUploader, SpreeA
autoUpload: true
configure: (product) =>
@imageUploader.url = "/api/product_images/#{product.id}"
@imageUploader.url = "/api/v0/product_images/#{product.id}"
@imagePreview = product.image_url
@imageUploader.onSuccessItem = (image, response) =>
product.thumb_url = response.thumb_url

View File

@@ -9,12 +9,12 @@ angular.module("admin.resources").factory 'EnterpriseResource', ($resource) ->
'update':
method: 'PUT'
'removeLogo':
url: '/api/enterprises/:id/logo.json'
url: '/api/v0/enterprises/:id/logo.json'
method: 'DELETE'
'removePromoImage':
url: '/api/enterprises/:id/promo_image.json'
url: '/api/v0/enterprises/:id/promo_image.json'
method: 'DELETE'
'removeTermsAndConditions':
url: '/api/enterprises/:id/terms_and_conditions.json'
url: '/api/v0/enterprises/:id/terms_and_conditions.json'
method: 'DELETE'
})

View File

@@ -1,17 +1,17 @@
angular.module("admin.resources").factory 'OrderResource', ($resource) ->
$resource('/admin/orders/:id/:action.json', {}, {
'index':
url: '/api/orders.json'
url: '/api/v0/orders.json'
method: 'GET'
'update':
method: 'PUT'
'capture':
url: '/api/orders/:id/capture.json'
url: '/api/v0/orders/:id/capture.json'
method: 'PUT'
params:
id: '@id'
'ship':
url: '/api/orders/:id/ship.json'
url: '/api/v0/orders/:id/ship.json'
method: 'PUT'
params:
id: '@id'

View File

@@ -1,6 +1,6 @@
angular.module("admin.resources").factory 'ProductResource', ($resource) ->
$resource('/admin/product/:id/:action.json', {}, {
'index':
url: '/api/products/bulk_products.json'
url: '/api/v0/products/bulk_products.json'
method: 'GET'
})

View File

@@ -10,8 +10,8 @@ angular.module("ofn.admin").factory "BulkProducts", (ProductResource, dataFetche
angular.extend(@pagination, data.pagination)
cloneProduct: (product) ->
$http.post("/api/products/" + product.id + "/clone").success (data) =>
dataFetcher("/api/products/" + data.id + "?template=bulk_show").then (newProduct) =>
$http.post("/api/v0/products/" + product.id + "/clone").success (data) =>
dataFetcher("/api/v0/products/" + data.id + "?template=bulk_show").then (newProduct) =>
@unpackProduct newProduct
@insertProductAfter(product, newProduct)

View File

@@ -42,7 +42,7 @@ angular.module("admin.variantOverrides").controller "AdminVariantOverridesCtrl",
$scope.fetchProducts()
$scope.fetchProducts = ->
url = "/api/products/overridable?page=::page::;per_page=100"
url = "/api/v0/products/overridable?page=::page::;per_page=100"
PagedFetcher.fetch url, $scope.addProducts
$scope.addProducts = (data) ->

View File

@@ -24,7 +24,7 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, CurrentHub, $http,
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/shops/" + $scope.hub.id)
$http.get("/api/v0/shops/" + $scope.hub.id)
.success (data) ->
$scope.shopfront_loading = false
$scope.hub = data

View File

@@ -24,7 +24,7 @@ Darkswarm.controller "ProducerNodeCtrl", ($scope, HashNavigation, $anchorScroll,
$scope.shopfront_loading = true
$scope.toggle_tab(event)
$http.get("/api/shops/" + $scope.producer.id)
$http.get("/api/v0/shops/" + $scope.producer.id)
.success (data) ->
$scope.shopfront_loading = false
$scope.producer = data

View File

@@ -1,5 +1,5 @@
angular.module("Darkswarm").factory 'Customer', ($resource, $injector, Messages) ->
Customer = $resource('/api/customers/:id/:action.json', {}, {
Customer = $resource('/api/v0/customers/:id/:action.json', {}, {
'index':
method: 'GET'
isArray: true

View File

@@ -8,5 +8,5 @@ Darkswarm.factory "EnterpriseImageService", (FileUploader, spreeApiKey) ->
autoUpload: true
configure: (enterprise) =>
@imageUploader.url = "/api/enterprises/#{enterprise.id}/update_image"
@imageUploader.url = "/api/v0/enterprises/#{enterprise.id}/update_image"
@imageUploader.onSuccessItem = (image, response) => @imageSrc = response

View File

@@ -5,7 +5,7 @@ Darkswarm.factory "EnterpriseModal", ($modal, $rootScope, $http)->
scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
scope.embedded_layout = window.location.search.indexOf("embedded_shopfront=true") != -1
$http.get("/api/shops/" + enterprise.id).success (data) ->
$http.get("/api/v0/shops/" + enterprise.id).success (data) ->
scope.enterprise = data
$modal.open(templateUrl: "enterprise_modal.html", scope: scope)
.error (data) ->

View File

@@ -18,7 +18,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
Loading.message = t('creating') + " " + @enterprise.name
$http(
method: "POST"
url: "/api/enterprises"
url: "/api/v0/enterprises"
data:
enterprise: @prepare()
params:
@@ -42,7 +42,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
Loading.message = t('updating') + " " + @enterprise.name
$http(
method: "PUT"
url: "/api/enterprises/#{@enterprise.id}"
url: "/api/v0/enterprises/#{@enterprise.id}"
data:
enterprise: @prepare()
params:

View File

@@ -1,21 +1,21 @@
Darkswarm.factory 'OrderCycleResource', ($resource) ->
$resource('/api/order_cycles/:id.json', {}, {
$resource('/api/v0/order_cycles/:id.json', {}, {
'products':
method: 'GET'
isArray: true
url: '/api/order_cycles/:id/products.json'
url: '/api/v0/order_cycles/:id/products.json'
params:
id: '@id'
'taxons':
method: 'GET'
isArray: true
url: '/api/order_cycles/:id/taxons.json'
url: '/api/v0/order_cycles/:id/taxons.json'
params:
id: '@id'
'properties':
method: 'GET'
isArray: true
url: '/api/order_cycles/:id/properties.json'
url: '/api/v0/order_cycles/:id/properties.json'
params:
id: '@id'
})

View File

@@ -1,7 +1,7 @@
Darkswarm.factory 'ShopsResource', ($resource) ->
$resource('/api/shops/:id.json', {}, {
$resource('/api/v0/shops/:id.json', {}, {
'closed_shops':
method: 'GET'
isArray: true
url: '/api/shops/closed_shops.json'
url: '/api/v0/shops/closed_shops.json'
})

View File

@@ -1,103 +0,0 @@
# Base controller for OFN's API
require_dependency 'spree/api/controller_setup'
require "spree/core/controller_helpers/ssl"
module Api
class BaseController < ActionController::Metal
include RawParams
include ActionController::StrongParameters
include ActionController::RespondWith
include Spree::Api::ControllerSetup
include Spree::Core::ControllerHelpers::SSL
include ::ActionController::Head
include ::ActionController::ConditionalGet
include ActionView::Layouts
layout false
attr_accessor :current_api_user
before_action :set_content_type
before_action :authenticate_user
rescue_from Exception, with: :error_during_processing
rescue_from CanCan::AccessDenied, with: :unauthorized
rescue_from ActiveRecord::RecordNotFound, with: :not_found
helper Spree::Api::ApiHelpers
ssl_allowed
# Include these because we inherit from ActionController::Metal
# rather than ActionController::Base and these are required for AMS
include ActionController::Serialization
include ActionController::UrlFor
include Rails.application.routes.url_helpers
use_renderers :json
check_authorization
def respond_with_conflict(json_hash)
render json: json_hash, status: :conflict
end
private
# Use logged in user (spree_current_user) for API authentication (current_api_user)
def authenticate_user
return if @current_api_user = spree_current_user
if api_key.blank?
# An anonymous user
@current_api_user = Spree.user_class.new
return
end
return if @current_api_user = Spree.user_class.find_by(spree_api_key: api_key.to_s)
invalid_api_key
end
def set_content_type
headers["Content-Type"] = "application/json"
end
def error_during_processing(exception)
Bugsnag.notify(exception)
render(json: { exception: exception.message },
status: :unprocessable_entity) && return
end
def current_ability
Spree::Ability.new(current_api_user)
end
def api_key
request.headers["X-Spree-Token"] || params[:token]
end
helper_method :api_key
def invalid_resource!(resource)
@resource = resource
render(json: { error: I18n.t(:invalid_resource, scope: "spree.api"),
errors: @resource.errors },
status: :unprocessable_entity)
end
def invalid_api_key
render(json: { error: I18n.t(:invalid_api_key, key: api_key, scope: "spree.api") },
status: :unauthorized) && return
end
def unauthorized
render(json: { error: I18n.t(:unauthorized, scope: "spree.api") },
status: :unauthorized) && return
end
def not_found
render(json: { error: I18n.t(:resource_not_found, scope: "spree.api") },
status: :not_found) && return
end
end
end

View File

@@ -1,37 +0,0 @@
module Api
class CustomersController < Api::BaseController
skip_authorization_check only: :index
def index
@customers = current_api_user.customers
render json: @customers, each_serializer: CustomerSerializer
end
def update
@customer = Customer.find(params[:id])
authorize! :update, @customer
client_secret = RecurringPayments.setup_for(@customer) if params[:customer][:allow_charges]
if @customer.update(customer_params)
add_recurring_payment_info(client_secret)
render json: @customer, serializer: CustomerSerializer, status: :ok
else
invalid_resource!(@customer)
end
end
private
def add_recurring_payment_info(client_secret)
return unless client_secret
@customer.gateway_recurring_payment_client_secret = client_secret
@customer.gateway_shop_id = @customer.enterprise.stripe_account&.stripe_user_id
end
def customer_params
params.require(:customer).permit(:code, :email, :enterprise_id, :allow_charges)
end
end
end

View File

@@ -1,42 +0,0 @@
# frozen_string_literal: true
require 'api/admin/enterprise_serializer'
module Api
class EnterpriseAttachmentController < Api::BaseController
class MissingImplementationError < StandardError; end
class UnknownEnterpriseAuthorizationActionError < StandardError; end
before_action :load_enterprise
respond_to :json
def destroy
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message) unless @enterprise.public_send("#{attachment_name}?")
@enterprise.update!(attachment_name => nil)
render json: @enterprise, serializer: Admin::EnterpriseSerializer, spree_current_user: spree_current_user
end
protected
def attachment_name
raise MissingImplementationError, "Method attachment_name should be defined"
end
def enterprise_authorize_action
raise MissingImplementationError, "Method enterprise_authorize_action should be defined"
end
def load_enterprise
@enterprise = Enterprise.find_by(permalink: params[:enterprise_id].to_s)
raise UnknownEnterpriseAuthorizationActionError if enterprise_authorize_action.blank?
authorize!(enterprise_authorize_action, @enterprise)
end
def destroy_attachment_does_not_exist_error_message
I18n.t("api.enterprise_#{attachment_name}.destroy_attachment_does_not_exist")
end
end
end

View File

@@ -1,21 +0,0 @@
module Api
class EnterpriseFeesController < Api::BaseController
respond_to :json
def destroy
authorize! :destroy, enterprise_fee
if enterprise_fee.destroy
render plain: I18n.t(:successfully_removed), status: :no_content
else
render plain: enterprise_fee.errors.full_messages.first, status: :forbidden
end
end
private
def enterprise_fee
@enterprise_fee ||= EnterpriseFee.find_by id: params[:id]
end
end
end

View File

@@ -1,78 +0,0 @@
module Api
class EnterprisesController < Api::BaseController
before_action :override_owner, only: [:create, :update]
before_action :check_type, only: :update
before_action :override_sells, only: [:create, :update]
before_action :override_visible, only: [:create, :update]
respond_to :json
def create
authorize! :create, Enterprise
# params[:user_ids] breaks the enterprise creation
# We remove them from params and save them after creating the enterprise
user_ids = enterprise_params.delete(:user_ids)
@enterprise = Enterprise.new(enterprise_params)
if @enterprise.save
@enterprise.user_ids = user_ids
render json: @enterprise.id, status: :created
else
invalid_resource!(@enterprise)
end
end
def update
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
authorize! :update, @enterprise
if @enterprise.update(enterprise_params)
render json: @enterprise.id, status: :ok
else
invalid_resource!(@enterprise)
end
end
def update_image
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
authorize! :update, @enterprise
if params[:logo] && @enterprise.update( logo: params[:logo] )
render plain: @enterprise.logo.url(:medium), status: :ok
elsif params[:promo] && @enterprise.update( promo_image: params[:promo] )
render plain: @enterprise.promo_image.url(:medium), status: :ok
else
invalid_resource!(@enterprise)
end
end
private
def override_owner
enterprise_params[:owner_id] = current_api_user.id
end
def check_type
enterprise_params.delete :type unless current_api_user.admin?
end
def override_sells
has_hub = current_api_user.owned_enterprises.is_hub.any?
new_enterprise_is_producer = !!enterprise_params[:is_primary_producer]
enterprise_params[:sells] = if has_hub && !new_enterprise_is_producer
'any'
else
'unspecified'
end
end
def override_visible
enterprise_params[:visible] = false
end
def enterprise_params
@enterprise_params ||= PermittedAttributes::Enterprise.new(params).call.
to_h.with_indifferent_access
end
end
end

View File

@@ -1,100 +0,0 @@
# frozen_string_literal: true
# This controller lists products that can be added to an exchange
#
# Pagination is optional and can be required by using param[:page]
module Api
class ExchangeProductsController < Api::BaseController
include PaginationData
DEFAULT_PER_PAGE = 100
skip_authorization_check only: [:index]
# If exchange_id is present in the URL:
# Lists Products that can be added to that Exchange
#
# If exchange_id is not present in the URL:
# Lists Products of the Enterprise given that can be added to the given Order Cycle
# In this case parameters are: enterprise_id, order_cycle_id and incoming
# (order_cycle_id is not necessary for incoming exchanges)
def index
if exchange_params[:exchange_id].present?
load_data_from_exchange
else
load_data_from_other_params
end
render_variant_count && return if params[:action_name] == "variant_count"
render_paginated_products paginated_products
end
private
def render_variant_count
render plain: {
count: variants.count
}.to_json
end
def variants
renderer.exchange_variants(@incoming, @enterprise)
end
def products
renderer.exchange_products(@incoming, @enterprise)
end
def renderer
@renderer ||= ExchangeProductsRenderer.
new(@order_cycle, spree_current_user)
end
def paginated_products
return products unless pagination_required?
products.
page(params[:page]).
per(params[:per_page] || DEFAULT_PER_PAGE)
end
def load_data_from_exchange
exchange = Exchange.find_by(id: exchange_params[:exchange_id])
@order_cycle = exchange.order_cycle
@incoming = exchange.incoming
@enterprise = exchange.sender
end
def load_data_from_other_params
@enterprise = Enterprise.find_by(id: exchange_params[:enterprise_id])
# This will be a string (eg "true") when it arrives via params, but we want a boolean
@incoming = ActiveModel::Type::Boolean.new.cast exchange_params[:incoming]
if exchange_params[:order_cycle_id]
@order_cycle = OrderCycle.find_by(id: exchange_params[:order_cycle_id])
elsif !@incoming
raise "order_cycle_id is required to list products for new outgoing exchange"
end
end
def render_paginated_products(paginated_products)
serialized_products = ActiveModel::ArraySerializer.new(
paginated_products,
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
order_cycle: @order_cycle
)
render json: {
products: serialized_products,
pagination: pagination_data(paginated_products)
}
end
def exchange_params
params.permit(:enterprise_id, :exchange_id, :order_cycle_id, :incoming).
to_h.with_indifferent_access
end
end
end

View File

@@ -1,16 +0,0 @@
module Api
class LogosController < Api::EnterpriseAttachmentController
private
def attachment_name
:logo
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_logo
end
end
end
end

View File

@@ -1,101 +0,0 @@
module Api
class OrderCyclesController < Api::BaseController
include EnterprisesHelper
include ApiActionCaching
skip_authorization_check
skip_before_action :authenticate_user, :ensure_api_key, only: [:taxons, :properties]
caches_action :taxons, :properties,
expires_in: CacheService::FILTERS_EXPIRY,
cache_path: proc { |controller| controller.request.url }
def products
return render_no_products unless order_cycle.open?
products = ProductsRenderer.new(
distributor,
order_cycle,
customer,
search_params
).products_json
render plain: products
rescue ProductsRenderer::NoProducts
render_no_products
end
def taxons
taxons = Spree::Taxon.
joins(:products).
where(spree_products: { id: distributed_products }).
select('DISTINCT spree_taxons.*')
render plain: ActiveModel::ArraySerializer.new(
taxons, each_serializer: Api::TaxonSerializer
).to_json
end
def properties
render plain: ActiveModel::ArraySerializer.new(
product_properties | producer_properties, each_serializer: Api::PropertySerializer
).to_json
end
private
def render_no_products
render status: :not_found, json: {}
end
def product_properties
Spree::Property.
joins(:products).
where(spree_products: { id: distributed_products }).
select('DISTINCT spree_properties.*')
end
def producer_properties
producers = Enterprise.
joins(:supplied_products).
where(spree_products: { id: distributed_products })
Spree::Property.
joins(:producer_properties).
where(producer_properties: { producer_id: producers }).
select('DISTINCT spree_properties.*')
end
def search_params
permitted_search_params = params.slice :q, :page, :per_page
if permitted_search_params.key? :q
permitted_search_params[:q].slice!(*permitted_ransack_params)
end
permitted_search_params
end
def permitted_ransack_params
[:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont,
:properties_id_or_supplier_properties_id_in_any,
:primary_taxon_id_in_any]
end
def distributor
@distributor ||= Enterprise.find_by(id: params[:distributor])
end
def order_cycle
@order_cycle ||= OrderCycle.find_by(id: params[:id])
end
def customer
@current_api_user.andand.customer_of(distributor) || nil
end
def distributed_products
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
end
end
end

View File

@@ -1,67 +0,0 @@
module Api
class OrdersController < Api::BaseController
include PaginationData
def show
authorize! :read, order
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
end
def index
authorize! :admin, Spree::Order
orders = SearchOrders.new(params, current_api_user).orders
render json: {
orders: serialized_orders(orders),
pagination: pagination_data(orders)
}
end
def ship
authorize! :admin, order
if order.ship
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
render json: { error: I18n.t('api.orders.failed_to_update') }, status: :unprocessable_entity
end
end
def capture
authorize! :admin, order
pending_payment = order.pending_payments.first
return payment_capture_failed unless order.payment_required? && pending_payment
if pending_payment.capture!
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
payment_capture_failed
end
rescue Spree::Core::GatewayError => e
error_during_processing(e)
end
private
def payment_capture_failed
render json: { error: I18n.t(:payment_processing_failed) }, status: :unprocessable_entity
end
def serialized_orders(orders)
ActiveModel::ArraySerializer.new(
orders,
each_serializer: Api::Admin::OrderSerializer
)
end
def order
@order ||= Spree::Order.
where(number: params[:id]).
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
first!
end
end
end

View File

@@ -1,19 +0,0 @@
module Api
class ProductImagesController < Api::BaseController
respond_to :json
def update_product_image
@product = Spree::Product.find(params[:product_id])
authorize! :update, @product
if @product.images.first.nil?
@image = Spree::Image.create(attachment: params[:file], viewable_id: @product.master.id, viewable_type: 'Spree::Variant')
render json: @image, serializer: ImageSerializer, status: :created
else
@image = @product.images.first
@image.update(attachment: params[:file])
render json: @image, serializer: ImageSerializer, status: :ok
end
end
end
end

View File

@@ -1,159 +0,0 @@
require 'open_food_network/permissions'
require 'spree/core/product_duplicator'
module Api
class ProductsController < Api::BaseController
include PaginationData
respond_to :json
DEFAULT_PER_PAGE = 15
before_action :set_default_available_on, only: :create
skip_authorization_check only: [:show, :bulk_products, :overridable]
def show
@product = find_product(params[:id])
render json: @product, serializer: Api::Admin::ProductSerializer
end
def create
authorize! :create, Spree::Product
@product = Spree::Product.new(product_params)
begin
if @product.save
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
else
invalid_resource!(@product)
end
rescue ActiveRecord::RecordNotUnique
@product.permalink = nil
retry
end
end
def update
authorize! :update, Spree::Product
@product = find_product(params[:id])
if @product.update(product_params)
render json: @product, serializer: Api::Admin::ProductSerializer, status: :ok
else
invalid_resource!(@product)
end
end
def destroy
authorize! :delete, Spree::Product
@product = find_product(params[:id])
authorize! :delete, @product
@product.destroy
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
end
def bulk_products
product_query = OpenFoodNetwork::Permissions.
new(current_api_user).
editable_products.
merge(product_scope)
if params[:import_date].present?
product_query = product_query.
imported_on(params[:import_date]).
group_by_products_id
end
@products = product_query.
ransack(query_params_with_defaults).
result.
page(params[:page] || 1).
per(params[:per_page] || DEFAULT_PER_PAGE)
render_paged_products @products
end
def overridable
producer_ids = OpenFoodNetwork::Permissions.new(current_api_user).
variant_override_producers.by_name.select('enterprises.id')
@products = paged_products_for_producers producer_ids
render_paged_products @products, ::Api::Admin::ProductSimpleSerializer
end
# POST /api/products/:product_id/clone
#
def clone
authorize! :create, Spree::Product
original_product = find_product(params[:product_id])
authorize! :update, original_product
@product = original_product.duplicate
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
end
private
def find_product(id)
product_scope.find_by!(permalink: id.to_s)
rescue ActiveRecord::RecordNotFound
product_scope.find(id)
end
def product_scope
if current_api_user.has_spree_role?("admin") || current_api_user.enterprises.present?
scope = Spree::Product
if params[:show_deleted]
scope = scope.with_deleted
end
else
scope = Spree::Product.active
end
scope.includes(product_query_includes)
end
def product_query_includes
[
master: [:images],
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
{ option_values: :option_type }]
]
end
def paged_products_for_producers(producer_ids)
Spree::Product.where(nil).
merge(product_scope).
includes(variants: [:product, :default_price, :stock_items]).
where(supplier_id: producer_ids).
by_producer.by_name.
ransack(params[:q]).result.
page(params[:page]).per(params[:per_page])
end
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
serialized_products = ActiveModel::ArraySerializer.new(
products,
each_serializer: product_serializer
)
render json: {
products: serialized_products,
pagination: pagination_data(products)
}
end
def query_params_with_defaults
(params[:q] || {}).reverse_merge(s: 'created_at desc')
end
def product_params
@product_params ||=
params.permit(product: PermittedAttributes::Product.attributes)[:product].to_h
end
def set_default_available_on
product_params[:available_on] ||= Time.zone.now
end
end
end

View File

@@ -1,16 +0,0 @@
module Api
class PromoImagesController < Api::EnterpriseAttachmentController
private
def attachment_name
:promo_image
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_promo_image
end
end
end
end

View File

@@ -1,112 +0,0 @@
require 'open_food_network/scope_variant_to_hub'
module Api
class ShipmentsController < Api::BaseController
respond_to :json
before_action :find_order
before_action :find_and_update_shipment, only: [:ship, :ready, :add, :remove]
def create
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@shipment = get_or_create_shipment(params[:stock_location_id])
@order.contents.add(variant, quantity, nil, @shipment)
@shipment.refresh_rates
@shipment.save!
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
end
def update
authorize! :read, Spree::Shipment
@shipment = @order.shipments.find_by!(number: params[:id])
params[:shipment] ||= []
unlock = params[:shipment].delete(:unlock)
if unlock == 'yes'
@shipment.fee_adjustment.open
end
@shipment.update(shipment_params[:shipment])
if unlock == 'yes'
@shipment.fee_adjustment.close
end
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
end
def ready
authorize! :read, Spree::Shipment
unless @shipment.ready?
if @shipment.can_ready?
@shipment.ready!
else
render(json: { error: I18n.t(:cannot_ready, scope: "spree.api.shipment") },
status: :unprocessable_entity) && return
end
end
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def ship
authorize! :read, Spree::Shipment
unless @shipment.shipped?
@shipment.ship!
end
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def add
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@order.contents.add(variant, quantity, nil, @shipment)
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def remove
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@order.contents.remove(variant, quantity, @shipment)
@shipment.reload if @shipment.persisted?
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
private
def find_order
@order = Spree::Order.find_by!(number: params[:order_id])
authorize! :read, @order
end
def find_and_update_shipment
@shipment = @order.shipments.find_by!(number: params[:id])
@shipment.update(shipment_params[:shipment]) if shipment_params[:shipment].present?
@shipment.reload
end
def scoped_variant(variant_id)
variant = Spree::Variant.find(variant_id)
OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant)
variant
end
def get_or_create_shipment(stock_location_id)
@order.shipment || @order.shipments.create(stock_location_id: stock_location_id)
end
def shipment_params
params.permit(
[:id, :order_id, :variant_id, :quantity,
{ shipment: [:tracking, :selected_shipping_rate_id] }]
)
end
end
end

View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true
module Api
class ShopsController < BaseController
respond_to :json
skip_authorization_check only: [:show, :closed_shops]
def show
enterprise = Enterprise.find_by(id: params[:id])
render plain: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
end
def closed_shops
@active_distributor_ids = []
@earliest_closing_times = []
serialized_closed_shops = ActiveModel::ArraySerializer.new(
ShopsListService.new.closed_shops,
each_serializer: Api::EnterpriseSerializer,
data: OpenFoodNetwork::EnterpriseInjectionData.new
)
render json: serialized_closed_shops
end
end
end

View File

@@ -1,44 +0,0 @@
# frozen_string_literal: true
module Api
class StatesController < Api::BaseController
respond_to :json
skip_authorization_check
def index
render json: states, each_serializer: Api::StateSerializer, status: :ok
end
def show
@state = scope.find(params[:id])
render json: @state, serializer: Api::StateSerializer, status: :ok
end
private
def scope
if params[:country_id]
@country = Spree::Country.find(params[:country_id])
@country.states
else
Spree::State.all
end
end
def states
states = scope.ransack(params[:q]).result.
includes(:country).order('name ASC')
if pagination?
states = states.page(params[:page]).per(params[:per_page])
end
states
end
def pagination?
params[:page] || params[:per_page]
end
end
end

View File

@@ -1,16 +0,0 @@
module Api
class StatusesController < ::BaseController
respond_to :json
def job_queue
render json: { alive: job_queue_alive? }
end
private
def job_queue_alive?
Spree::Config.last_job_queue_heartbeat_at.present? &&
Time.parse(Spree::Config.last_job_queue_heartbeat_at).in_time_zone > 6.minutes.ago
end
end
end

View File

@@ -1,12 +0,0 @@
module Api
class TaxonomiesController < Api::BaseController
respond_to :json
skip_authorization_check only: :jstree
def jstree
@taxonomy = Spree::Taxonomy.find(params[:id])
render json: @taxonomy.root, serializer: Api::TaxonJstreeSerializer
end
end
end

View File

@@ -1,76 +0,0 @@
module Api
class TaxonsController < Api::BaseController
respond_to :json
skip_authorization_check only: [:index, :show, :jstree]
def index
@taxons = if taxonomy
taxonomy.root.children
elsif params[:ids]
Spree::Taxon.where(id: raw_params[:ids].split(","))
else
Spree::Taxon.ransack(raw_params[:q]).result
end
render json: @taxons, each_serializer: Api::TaxonSerializer
end
def jstree
@taxon = taxon
render json: @taxon.children, each_serializer: Api::TaxonJstreeSerializer
end
def create
authorize! :create, Spree::Taxon
@taxon = Spree::Taxon.new(taxon_params)
@taxon.taxonomy_id = params[:taxonomy_id]
taxonomy = Spree::Taxonomy.find_by(id: params[:taxonomy_id])
if taxonomy.nil?
@taxon.errors[:taxonomy_id] = I18n.t(:invalid_taxonomy_id, scope: 'spree.api')
invalid_resource!(@taxon) && return
end
@taxon.parent_id = taxonomy.root.id unless params.dig(:taxon, :parent_id)
if @taxon.save
render json: @taxon, serializer: Api::TaxonSerializer, status: :created
else
invalid_resource!(@taxon)
end
end
def update
authorize! :update, Spree::Taxon
if taxon.update(taxon_params)
render json: taxon, serializer: Api::TaxonSerializer, status: :ok
else
invalid_resource!(taxon)
end
end
def destroy
authorize! :delete, Spree::Taxon
taxon.destroy
render json: taxon, serializer: Api::TaxonSerializer, status: :no_content
end
private
def taxonomy
return if params[:taxonomy_id].blank?
@taxonomy ||= Spree::Taxonomy.find(params[:taxonomy_id])
end
def taxon
@taxon ||= taxonomy.taxons.find(params[:id])
end
def taxon_params
return if params[:taxon].blank?
params.require(:taxon).permit([:name, :parent_id])
end
end
end

View File

@@ -1,18 +0,0 @@
# frozen_string_literal: true
module Api
class TermsAndConditionsController < Api::EnterpriseAttachmentController
private
def attachment_name
:terms_and_conditions
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_terms_and_conditions
end
end
end
end

View File

@@ -0,0 +1,107 @@
# frozen_string_literal: true
# Base controller for OFN's API
require_dependency 'spree/api/controller_setup'
require "spree/core/controller_helpers/ssl"
module Api
module V0
class BaseController < ActionController::Metal
include RawParams
include ActionController::StrongParameters
include ActionController::RespondWith
include Spree::Api::ControllerSetup
include Spree::Core::ControllerHelpers::SSL
include ::ActionController::Head
include ::ActionController::ConditionalGet
include ActionView::Layouts
layout false
attr_accessor :current_api_user
before_action :set_content_type
before_action :authenticate_user
rescue_from Exception, with: :error_during_processing
rescue_from CanCan::AccessDenied, with: :unauthorized
rescue_from ActiveRecord::RecordNotFound, with: :not_found
helper Spree::Api::ApiHelpers
ssl_allowed
# Include these because we inherit from ActionController::Metal
# rather than ActionController::Base and these are required for AMS
include ActionController::Serialization
include ActionController::UrlFor
include Rails.application.routes.url_helpers
use_renderers :json
check_authorization
def respond_with_conflict(json_hash)
render json: json_hash, status: :conflict
end
private
# Use logged in user (spree_current_user) for API authentication (current_api_user)
def authenticate_user
return if @current_api_user = spree_current_user
if api_key.blank?
# An anonymous user
@current_api_user = Spree.user_class.new
return
end
return if @current_api_user = Spree.user_class.find_by(spree_api_key: api_key.to_s)
invalid_api_key
end
def set_content_type
headers["Content-Type"] = "application/json"
end
def error_during_processing(exception)
Bugsnag.notify(exception)
render(json: { exception: exception.message },
status: :unprocessable_entity) && return
end
def current_ability
Spree::Ability.new(current_api_user)
end
def api_key
request.headers["X-Spree-Token"] || params[:token]
end
helper_method :api_key
def invalid_resource!(resource)
@resource = resource
render(json: { error: I18n.t(:invalid_resource, scope: "spree.api"),
errors: @resource.errors },
status: :unprocessable_entity)
end
def invalid_api_key
render(json: { error: I18n.t(:invalid_api_key, key: api_key, scope: "spree.api") },
status: :unauthorized) && return
end
def unauthorized
render(json: { error: I18n.t(:unauthorized, scope: "spree.api") },
status: :unauthorized) && return
end
def not_found
render(json: { error: I18n.t(:resource_not_found, scope: "spree.api") },
status: :not_found) && return
end
end
end
end

View File

@@ -0,0 +1,41 @@
# frozen_string_literal: true
module Api
module V0
class CustomersController < Api::V0::BaseController
skip_authorization_check only: :index
def index
@customers = current_api_user.customers
render json: @customers, each_serializer: CustomerSerializer
end
def update
@customer = Customer.find(params[:id])
authorize! :update, @customer
client_secret = RecurringPayments.setup_for(@customer) if params[:customer][:allow_charges]
if @customer.update(customer_params)
add_recurring_payment_info(client_secret)
render json: @customer, serializer: CustomerSerializer, status: :ok
else
invalid_resource!(@customer)
end
end
private
def add_recurring_payment_info(client_secret)
return unless client_secret
@customer.gateway_recurring_payment_client_secret = client_secret
@customer.gateway_shop_id = @customer.enterprise.stripe_account&.stripe_user_id
end
def customer_params
params.require(:customer).permit(:code, :email, :enterprise_id, :allow_charges)
end
end
end
end

View File

@@ -0,0 +1,49 @@
# frozen_string_literal: true
require 'api/admin/enterprise_serializer'
module Api
module V0
class EnterpriseAttachmentController < Api::V0::BaseController
class MissingImplementationError < StandardError; end
class UnknownEnterpriseAuthorizationActionError < StandardError; end
before_action :load_enterprise
respond_to :json
def destroy
unless @enterprise.public_send("#{attachment_name}?")
return respond_with_conflict(error: destroy_attachment_does_not_exist_error_message)
end
@enterprise.update!(attachment_name => nil)
render json: @enterprise,
serializer: Admin::EnterpriseSerializer,
spree_current_user: spree_current_user
end
protected
def attachment_name
raise MissingImplementationError, "Method attachment_name should be defined"
end
def enterprise_authorize_action
raise MissingImplementationError, "Method enterprise_authorize_action should be defined"
end
def load_enterprise
@enterprise = Enterprise.find_by(permalink: params[:enterprise_id].to_s)
raise UnknownEnterpriseAuthorizationActionError if enterprise_authorize_action.blank?
authorize!(enterprise_authorize_action, @enterprise)
end
def destroy_attachment_does_not_exist_error_message
I18n.t("api.enterprise_#{attachment_name}.destroy_attachment_does_not_exist")
end
end
end
end

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: true
module Api
module V0
class EnterpriseFeesController < Api::V0::BaseController
respond_to :json
def destroy
authorize! :destroy, enterprise_fee
if enterprise_fee.destroy
render plain: I18n.t(:successfully_removed), status: :no_content
else
render plain: enterprise_fee.errors.full_messages.first, status: :forbidden
end
end
private
def enterprise_fee
@enterprise_fee ||= EnterpriseFee.find_by id: params[:id]
end
end
end
end

View File

@@ -0,0 +1,82 @@
# frozen_string_literal: true
module Api
module V0
class EnterprisesController < Api::V0::BaseController
before_action :override_owner, only: [:create, :update]
before_action :check_type, only: :update
before_action :override_sells, only: [:create, :update]
before_action :override_visible, only: [:create, :update]
respond_to :json
def create
authorize! :create, Enterprise
# params[:user_ids] breaks the enterprise creation
# We remove them from params and save them after creating the enterprise
user_ids = enterprise_params.delete(:user_ids)
@enterprise = Enterprise.new(enterprise_params)
if @enterprise.save
@enterprise.user_ids = user_ids
render json: @enterprise.id, status: :created
else
invalid_resource!(@enterprise)
end
end
def update
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
authorize! :update, @enterprise
if @enterprise.update(enterprise_params)
render json: @enterprise.id, status: :ok
else
invalid_resource!(@enterprise)
end
end
def update_image
@enterprise = Enterprise.find_by(permalink: params[:id]) || Enterprise.find(params[:id])
authorize! :update, @enterprise
if params[:logo] && @enterprise.update( logo: params[:logo] )
render plain: @enterprise.logo.url(:medium), status: :ok
elsif params[:promo] && @enterprise.update( promo_image: params[:promo] )
render plain: @enterprise.promo_image.url(:medium), status: :ok
else
invalid_resource!(@enterprise)
end
end
private
def override_owner
enterprise_params[:owner_id] = current_api_user.id
end
def check_type
enterprise_params.delete :type unless current_api_user.admin?
end
def override_sells
has_hub = current_api_user.owned_enterprises.is_hub.any?
new_enterprise_is_producer = !!enterprise_params[:is_primary_producer]
enterprise_params[:sells] = if has_hub && !new_enterprise_is_producer
'any'
else
'unspecified'
end
end
def override_visible
enterprise_params[:visible] = false
end
def enterprise_params
@enterprise_params ||= PermittedAttributes::Enterprise.new(params).call.
to_h.with_indifferent_access
end
end
end
end

View File

@@ -0,0 +1,102 @@
# frozen_string_literal: true
# This controller lists products that can be added to an exchange
#
# Pagination is optional and can be required by using param[:page]
module Api
module V0
class ExchangeProductsController < Api::V0::BaseController
include PaginationData
DEFAULT_PER_PAGE = 100
skip_authorization_check only: [:index]
# If exchange_id is present in the URL:
# Lists Products that can be added to that Exchange
#
# If exchange_id is not present in the URL:
# Lists Products of the Enterprise given that can be added to the given Order Cycle
# In this case parameters are: enterprise_id, order_cycle_id and incoming
# (order_cycle_id is not necessary for incoming exchanges)
def index
if exchange_params[:exchange_id].present?
load_data_from_exchange
else
load_data_from_other_params
end
render_variant_count && return if params[:action_name] == "variant_count"
render_paginated_products paginated_products
end
private
def render_variant_count
render plain: {
count: variants.count
}.to_json
end
def variants
renderer.exchange_variants(@incoming, @enterprise)
end
def products
renderer.exchange_products(@incoming, @enterprise)
end
def renderer
@renderer ||= ExchangeProductsRenderer.
new(@order_cycle, spree_current_user)
end
def paginated_products
return products unless pagination_required?
products.
page(params[:page]).
per(params[:per_page] || DEFAULT_PER_PAGE)
end
def load_data_from_exchange
exchange = Exchange.find_by(id: exchange_params[:exchange_id])
@order_cycle = exchange.order_cycle
@incoming = exchange.incoming
@enterprise = exchange.sender
end
def load_data_from_other_params
@enterprise = Enterprise.find_by(id: exchange_params[:enterprise_id])
# This will be a string (eg "true") when it arrives via params, but we want a boolean
@incoming = ActiveModel::Type::Boolean.new.cast exchange_params[:incoming]
if exchange_params[:order_cycle_id]
@order_cycle = OrderCycle.find_by(id: exchange_params[:order_cycle_id])
elsif !@incoming
raise "order_cycle_id is required to list products for new outgoing exchange"
end
end
def render_paginated_products(paginated_products)
serialized_products = ActiveModel::ArraySerializer.new(
paginated_products,
each_serializer: Api::Admin::ForOrderCycle::SuppliedProductSerializer,
order_cycle: @order_cycle
)
render json: {
products: serialized_products,
pagination: pagination_data(paginated_products)
}
end
def exchange_params
params.permit(:enterprise_id, :exchange_id, :order_cycle_id, :incoming).
to_h.with_indifferent_access
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module Api
module V0
class LogosController < Api::V0::EnterpriseAttachmentController
private
def attachment_name
:logo
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_logo
end
end
end
end
end

View File

@@ -0,0 +1,105 @@
# frozen_string_literal: true
module Api
module V0
class OrderCyclesController < Api::V0::BaseController
include EnterprisesHelper
include ApiActionCaching
skip_authorization_check
skip_before_action :authenticate_user, :ensure_api_key, only: [:taxons, :properties]
caches_action :taxons, :properties,
expires_in: CacheService::FILTERS_EXPIRY,
cache_path: proc { |controller| controller.request.url }
def products
return render_no_products unless order_cycle.open?
products = ProductsRenderer.new(
distributor,
order_cycle,
customer,
search_params
).products_json
render plain: products
rescue ProductsRenderer::NoProducts
render_no_products
end
def taxons
taxons = Spree::Taxon.
joins(:products).
where(spree_products: { id: distributed_products }).
select('DISTINCT spree_taxons.*')
render plain: ActiveModel::ArraySerializer.new(
taxons, each_serializer: Api::TaxonSerializer
).to_json
end
def properties
render plain: ActiveModel::ArraySerializer.new(
product_properties | producer_properties, each_serializer: Api::PropertySerializer
).to_json
end
private
def render_no_products
render status: :not_found, json: {}
end
def product_properties
Spree::Property.
joins(:products).
where(spree_products: { id: distributed_products }).
select('DISTINCT spree_properties.*')
end
def producer_properties
producers = Enterprise.
joins(:supplied_products).
where(spree_products: { id: distributed_products })
Spree::Property.
joins(:producer_properties).
where(producer_properties: { producer_id: producers }).
select('DISTINCT spree_properties.*')
end
def search_params
permitted_search_params = params.slice :q, :page, :per_page
if permitted_search_params.key? :q
permitted_search_params[:q].slice!(*permitted_ransack_params)
end
permitted_search_params
end
def permitted_ransack_params
[:name_or_meta_keywords_or_variants_display_as_or_variants_display_name_or_supplier_name_cont,
:properties_id_or_supplier_properties_id_in_any,
:primary_taxon_id_in_any]
end
def distributor
@distributor ||= Enterprise.find_by(id: params[:distributor])
end
def order_cycle
@order_cycle ||= OrderCycle.find_by(id: params[:id])
end
def customer
@current_api_user.andand.customer_of(distributor) || nil
end
def distributed_products
OrderCycleDistributedProducts.new(distributor, order_cycle, customer).products_relation
end
end
end
end

View File

@@ -0,0 +1,72 @@
# frozen_string_literal: true
module Api
module V0
class OrdersController < Api::V0::BaseController
include PaginationData
def show
authorize! :read, order
render json: order, serializer: Api::OrderDetailedSerializer, current_order: order
end
def index
authorize! :admin, Spree::Order
orders = SearchOrders.new(params, current_api_user).orders
render json: {
orders: serialized_orders(orders),
pagination: pagination_data(orders)
}
end
def ship
authorize! :admin, order
if order.ship
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
render json: { error: I18n.t('api.orders.failed_to_update') },
status: :unprocessable_entity
end
end
def capture
authorize! :admin, order
pending_payment = order.pending_payments.first
return payment_capture_failed unless order.payment_required? && pending_payment
if pending_payment.capture!
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
else
payment_capture_failed
end
rescue Spree::Core::GatewayError => e
error_during_processing(e)
end
private
def payment_capture_failed
render json: { error: I18n.t(:payment_processing_failed) }, status: :unprocessable_entity
end
def serialized_orders(orders)
ActiveModel::ArraySerializer.new(
orders,
each_serializer: Api::Admin::OrderSerializer
)
end
def order
@order ||= Spree::Order.
where(number: params[:id]).
includes(line_items: { variant: [:product, :stock_items, :default_price] }).
first!
end
end
end
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
module Api
module V0
class ProductImagesController < Api::V0::BaseController
respond_to :json
def update_product_image
@product = Spree::Product.find(params[:product_id])
authorize! :update, @product
if @product.images.first.nil?
@image = Spree::Image.create(
attachment: params[:file],
viewable_id: @product.master.id,
viewable_type: 'Spree::Variant'
)
render json: @image, serializer: ImageSerializer, status: :created
else
@image = @product.images.first
@image.update(attachment: params[:file])
render json: @image, serializer: ImageSerializer, status: :ok
end
end
end
end
end

View File

@@ -0,0 +1,163 @@
# frozen_string_literal: true
require 'open_food_network/permissions'
require 'spree/core/product_duplicator'
module Api
module V0
class ProductsController < Api::V0::BaseController
include PaginationData
respond_to :json
DEFAULT_PER_PAGE = 15
before_action :set_default_available_on, only: :create
skip_authorization_check only: [:show, :bulk_products, :overridable]
def show
@product = find_product(params[:id])
render json: @product, serializer: Api::Admin::ProductSerializer
end
def create
authorize! :create, Spree::Product
@product = Spree::Product.new(product_params)
begin
if @product.save
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
else
invalid_resource!(@product)
end
rescue ActiveRecord::RecordNotUnique
@product.permalink = nil
retry
end
end
def update
authorize! :update, Spree::Product
@product = find_product(params[:id])
if @product.update(product_params)
render json: @product, serializer: Api::Admin::ProductSerializer, status: :ok
else
invalid_resource!(@product)
end
end
def destroy
authorize! :delete, Spree::Product
@product = find_product(params[:id])
authorize! :delete, @product
@product.destroy
render json: @product, serializer: Api::Admin::ProductSerializer, status: :no_content
end
def bulk_products
product_query = OpenFoodNetwork::Permissions.
new(current_api_user).
editable_products.
merge(product_scope)
if params[:import_date].present?
product_query = product_query.
imported_on(params[:import_date]).
group_by_products_id
end
@products = product_query.
ransack(query_params_with_defaults).
result.
page(params[:page] || 1).
per(params[:per_page] || DEFAULT_PER_PAGE)
render_paged_products @products
end
def overridable
producer_ids = OpenFoodNetwork::Permissions.new(current_api_user).
variant_override_producers.by_name.select('enterprises.id')
@products = paged_products_for_producers producer_ids
render_paged_products @products, ::Api::Admin::ProductSimpleSerializer
end
# POST /api/products/:product_id/clone
#
def clone
authorize! :create, Spree::Product
original_product = find_product(params[:product_id])
authorize! :update, original_product
@product = original_product.duplicate
render json: @product, serializer: Api::Admin::ProductSerializer, status: :created
end
private
def find_product(id)
product_scope.find_by!(permalink: id.to_s)
rescue ActiveRecord::RecordNotFound
product_scope.find(id)
end
def product_scope
if current_api_user.has_spree_role?("admin") || current_api_user.enterprises.present?
scope = Spree::Product
if params[:show_deleted]
scope = scope.with_deleted
end
else
scope = Spree::Product.active
end
scope.includes(product_query_includes)
end
def product_query_includes
[
master: [:images],
variants: [:default_price, :stock_locations, :stock_items, :variant_overrides,
{ option_values: :option_type }]
]
end
def paged_products_for_producers(producer_ids)
Spree::Product.where(nil).
merge(product_scope).
includes(variants: [:product, :default_price, :stock_items]).
where(supplier_id: producer_ids).
by_producer.by_name.
ransack(params[:q]).result.
page(params[:page]).per(params[:per_page])
end
def render_paged_products(products, product_serializer = ::Api::Admin::ProductSerializer)
serialized_products = ActiveModel::ArraySerializer.new(
products,
each_serializer: product_serializer
)
render json: {
products: serialized_products,
pagination: pagination_data(products)
}
end
def query_params_with_defaults
(params[:q] || {}).reverse_merge(s: 'created_at desc')
end
def product_params
@product_params ||=
params.permit(product: PermittedAttributes::Product.attributes)[:product].to_h
end
def set_default_available_on
product_params[:available_on] ||= Time.zone.now
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module Api
module V0
class PromoImagesController < Api::V0::EnterpriseAttachmentController
private
def attachment_name
:promo_image
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_promo_image
end
end
end
end
end

View File

@@ -0,0 +1,116 @@
# frozen_string_literal: true
require 'open_food_network/scope_variant_to_hub'
module Api
module V0
class ShipmentsController < Api::V0::BaseController
respond_to :json
before_action :find_order
before_action :find_and_update_shipment, only: [:ship, :ready, :add, :remove]
def create
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@shipment = get_or_create_shipment(params[:stock_location_id])
@order.contents.add(variant, quantity, nil, @shipment)
@shipment.refresh_rates
@shipment.save!
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
end
def update
authorize! :read, Spree::Shipment
@shipment = @order.shipments.find_by!(number: params[:id])
params[:shipment] ||= []
unlock = params[:shipment].delete(:unlock)
if unlock == 'yes'
@shipment.fee_adjustment.open
end
@shipment.update(shipment_params[:shipment])
if unlock == 'yes'
@shipment.fee_adjustment.close
end
render json: @shipment.reload, serializer: Api::ShipmentSerializer, status: :ok
end
def ready
authorize! :read, Spree::Shipment
unless @shipment.ready?
if @shipment.can_ready?
@shipment.ready!
else
render(json: { error: I18n.t(:cannot_ready, scope: "spree.api.shipment") },
status: :unprocessable_entity) && return
end
end
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def ship
authorize! :read, Spree::Shipment
unless @shipment.shipped?
@shipment.ship!
end
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def add
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@order.contents.add(variant, quantity, nil, @shipment)
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
def remove
variant = scoped_variant(params[:variant_id])
quantity = params[:quantity].to_i
@order.contents.remove(variant, quantity, @shipment)
@shipment.reload if @shipment.persisted?
render json: @shipment, serializer: Api::ShipmentSerializer, status: :ok
end
private
def find_order
@order = Spree::Order.find_by!(number: params[:order_id])
authorize! :read, @order
end
def find_and_update_shipment
@shipment = @order.shipments.find_by!(number: params[:id])
@shipment.update(shipment_params[:shipment]) if shipment_params[:shipment].present?
@shipment.reload
end
def scoped_variant(variant_id)
variant = Spree::Variant.find(variant_id)
OpenFoodNetwork::ScopeVariantToHub.new(@order.distributor).scope(variant)
variant
end
def get_or_create_shipment(stock_location_id)
@order.shipment || @order.shipments.create(stock_location_id: stock_location_id)
end
def shipment_params
params.permit(
[:id, :order_id, :variant_id, :quantity,
{ shipment: [:tracking, :selected_shipping_rate_id] }]
)
end
end
end
end

View File

@@ -0,0 +1,29 @@
# frozen_string_literal: true
module Api
module V0
class ShopsController < BaseController
respond_to :json
skip_authorization_check only: [:show, :closed_shops]
def show
enterprise = Enterprise.find_by(id: params[:id])
render plain: Api::EnterpriseShopfrontSerializer.new(enterprise).to_json, status: :ok
end
def closed_shops
@active_distributor_ids = []
@earliest_closing_times = []
serialized_closed_shops = ActiveModel::ArraySerializer.new(
ShopsListService.new.closed_shops,
each_serializer: Api::EnterpriseSerializer,
data: OpenFoodNetwork::EnterpriseInjectionData.new
)
render json: serialized_closed_shops
end
end
end
end

View File

@@ -0,0 +1,46 @@
# frozen_string_literal: true
module Api
module V0
class StatesController < Api::V0::BaseController
respond_to :json
skip_authorization_check
def index
render json: states, each_serializer: Api::StateSerializer, status: :ok
end
def show
@state = scope.find(params[:id])
render json: @state, serializer: Api::StateSerializer, status: :ok
end
private
def scope
if params[:country_id]
@country = Spree::Country.find(params[:country_id])
@country.states
else
Spree::State.all
end
end
def states
states = scope.ransack(params[:q]).result.
includes(:country).order('name ASC')
if pagination?
states = states.page(params[:page]).per(params[:per_page])
end
states
end
def pagination?
params[:page] || params[:per_page]
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module Api
module V0
class StatusesController < ::BaseController
respond_to :json
def job_queue
render json: { alive: job_queue_alive? }
end
private
def job_queue_alive?
Spree::Config.last_job_queue_heartbeat_at.present? &&
Time.parse(Spree::Config.last_job_queue_heartbeat_at).in_time_zone > 6.minutes.ago
end
end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
module Api
module V0
class TaxonomiesController < Api::V0::BaseController
respond_to :json
skip_authorization_check only: :jstree
def jstree
@taxonomy = Spree::Taxonomy.find(params[:id])
render json: @taxonomy.root, serializer: Api::TaxonJstreeSerializer
end
end
end
end

View File

@@ -0,0 +1,78 @@
module Api
module V0
class TaxonsController < Api::V0::BaseController
respond_to :json
skip_authorization_check only: [:index, :show, :jstree]
def index
@taxons = if taxonomy
taxonomy.root.children
elsif params[:ids]
Spree::Taxon.where(id: raw_params[:ids].split(","))
else
Spree::Taxon.ransack(raw_params[:q]).result
end
render json: @taxons, each_serializer: Api::TaxonSerializer
end
def jstree
@taxon = taxon
render json: @taxon.children, each_serializer: Api::TaxonJstreeSerializer
end
def create
authorize! :create, Spree::Taxon
@taxon = Spree::Taxon.new(taxon_params)
@taxon.taxonomy_id = params[:taxonomy_id]
taxonomy = Spree::Taxonomy.find_by(id: params[:taxonomy_id])
if taxonomy.nil?
@taxon.errors[:taxonomy_id] = I18n.t(:invalid_taxonomy_id, scope: 'spree.api')
invalid_resource!(@taxon) && return
end
@taxon.parent_id = taxonomy.root.id unless params.dig(:taxon, :parent_id)
if @taxon.save
render json: @taxon, serializer: Api::TaxonSerializer, status: :created
else
invalid_resource!(@taxon)
end
end
def update
authorize! :update, Spree::Taxon
if taxon.update(taxon_params)
render json: taxon, serializer: Api::TaxonSerializer, status: :ok
else
invalid_resource!(taxon)
end
end
def destroy
authorize! :delete, Spree::Taxon
taxon.destroy
render json: taxon, serializer: Api::TaxonSerializer, status: :no_content
end
private
def taxonomy
return if params[:taxonomy_id].blank?
@taxonomy ||= Spree::Taxonomy.find(params[:taxonomy_id])
end
def taxon
@taxon ||= taxonomy.taxons.find(params[:id])
end
def taxon_params
return if params[:taxon].blank?
params.require(:taxon).permit([:name, :parent_id])
end
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
module Api
module V0
class TermsAndConditionsController < Api::V0::EnterpriseAttachmentController
private
def attachment_name
:terms_and_conditions
end
def enterprise_authorize_action
case action_name.to_sym
when :destroy
:remove_terms_and_conditions
end
end
end
end
end

View File

@@ -0,0 +1,81 @@
# frozen_string_literal: true
module Api
module V0
class VariantsController < Api::V0::BaseController
respond_to :json
skip_authorization_check only: [:index, :show]
before_action :product
def index
@variants = scope.includes(option_values: :option_type).ransack(params[:q]).result
render json: @variants, each_serializer: Api::VariantSerializer
end
def show
@variant = scope.includes(option_values: :option_type).find(params[:id])
render json: @variant, serializer: Api::VariantSerializer
end
def create
authorize! :create, Spree::Variant
@variant = scope.new(variant_params)
if @variant.save
render json: @variant, serializer: Api::VariantSerializer, status: :created
else
invalid_resource!(@variant)
end
end
def update
authorize! :update, Spree::Variant
@variant = scope.find(params[:id])
if @variant.update(variant_params)
render json: @variant, serializer: Api::VariantSerializer, status: :ok
else
invalid_resource!(@product)
end
end
def destroy
authorize! :delete, Spree::Variant
@variant = scope.find(params[:id])
authorize! :delete, @variant
VariantDeleter.new.delete(@variant)
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
end
private
def product
@product ||= Spree::Product.find_by(permalink: params[:product_id]) if params[:product_id]
end
def scope
if @product
variants = if current_api_user.has_spree_role?("admin") || params[:show_deleted]
@product.variants_including_master.with_deleted
else
@product.variants_including_master
end
else
variants = Spree::Variant.where(nil)
if current_api_user.has_spree_role?("admin")
unless params[:show_deleted]
variants = Spree::Variant.active
end
else
variants = variants.active
end
end
variants
end
def variant_params
params.require(:variant).permit(PermittedAttributes::Variant.attributes)
end
end
end
end

View File

@@ -1,77 +0,0 @@
module Api
class VariantsController < Api::BaseController
respond_to :json
skip_authorization_check only: [:index, :show]
before_action :product
def index
@variants = scope.includes(option_values: :option_type).ransack(params[:q]).result
render json: @variants, each_serializer: Api::VariantSerializer
end
def show
@variant = scope.includes(option_values: :option_type).find(params[:id])
render json: @variant, serializer: Api::VariantSerializer
end
def create
authorize! :create, Spree::Variant
@variant = scope.new(variant_params)
if @variant.save
render json: @variant, serializer: Api::VariantSerializer, status: :created
else
invalid_resource!(@variant)
end
end
def update
authorize! :update, Spree::Variant
@variant = scope.find(params[:id])
if @variant.update(variant_params)
render json: @variant, serializer: Api::VariantSerializer, status: :ok
else
invalid_resource!(@product)
end
end
def destroy
authorize! :delete, Spree::Variant
@variant = scope.find(params[:id])
authorize! :delete, @variant
VariantDeleter.new.delete(@variant)
render json: @variant, serializer: Api::VariantSerializer, status: :no_content
end
private
def product
@product ||= Spree::Product.find_by(permalink: params[:product_id]) if params[:product_id]
end
def scope
if @product
variants = if current_api_user.has_spree_role?("admin") || params[:show_deleted]
@product.variants_including_master.with_deleted
else
@product.variants_including_master
end
else
variants = Spree::Variant.where(nil)
if current_api_user.has_spree_role?("admin")
unless params[:show_deleted]
variants = Spree::Variant.active
end
else
variants = variants.active
end
end
variants
end
def variant_params
params.require(:variant).permit(PermittedAttributes::Variant.attributes)
end
end
end

View File

@@ -80,7 +80,7 @@ module Spree
product_set.collection.each { |p| authorize! :update, p }
if product_set.save
redirect_to main_app.bulk_products_api_products_path(bulk_index_query)
redirect_to main_app.bulk_products_api_v0_products_path(bulk_index_query)
elsif product_set.errors.present?
render json: { errors: product_set.errors }, status: :bad_request
else

View File

@@ -7,8 +7,8 @@
}
Spree.routes = <%== {
:variants_search => spree.admin_search_variants_path(:format => 'json'),
:taxons_search => main_app.api_taxons_path(:format => 'json'),
:orders_api => main_app.api_orders_path,
:states_search => main_app.api_states_path(:format => 'json')
:taxons_search => main_app.api_v0_taxons_path(:format => 'json'),
:orders_api => main_app.api_v0_orders_path,
:states_search => main_app.api_v0_states_path(:format => 'json')
}.to_json %>;
</script>

View File

@@ -17,7 +17,7 @@
= label_tag nil, t("spree.tree")
%br/
:javascript
Spree.routes.taxonomy_taxons_path = "#{main_app.api_taxonomy_taxons_path(@taxonomy)}";
Spree.routes.taxonomy_taxons_path = "#{main_app.api_v0_taxonomy_taxons_path(@taxonomy)}";
Spree.routes.admin_taxonomy_taxons_path = "#{spree.admin_taxonomy_taxons_path(@taxonomy)}";
#taxonomy_tree.tree
#progress{style: "display:none;"}

View File

@@ -1,80 +1,84 @@
Openfoodnetwork::Application.routes.draw do
namespace :api do
resources :products do
collection do
get :bulk_products
get :overridable
end
post :clone
namespace :v0 do
resources :products do
collection do
get :bulk_products
get :overridable
end
post :clone
resources :variants
end
resources :variants, :only => [:index]
resources :orders, only: [:index, :show] do
member do
put :capture
put :ship
resources :variants
end
resources :shipments, :only => [:create, :update] do
resources :variants, :only => [:index]
resources :orders, only: [:index, :show] do
member do
put :ready
put :capture
put :ship
put :add
put :remove
end
resources :shipments, :only => [:create, :update] do
member do
put :ready
put :ship
put :add
put :remove
end
end
end
end
resources :enterprises do
post :update_image, on: :member
resources :enterprises do
post :update_image, on: :member
resource :logo, only: [:destroy]
resource :promo_image, only: [:destroy]
resource :terms_and_conditions, only: [:destroy]
end
resources :shops, only: [:show] do
collection do
get :closed_shops
resource :logo, only: [:destroy]
resource :promo_image, only: [:destroy]
resource :terms_and_conditions, only: [:destroy]
end
end
resources :order_cycles do
get :products, on: :member
get :taxons, on: :member
get :properties, on: :member
end
resources :exchanges, only: [:show], to: 'exchange_products#index' do
get :products, to: 'exchange_products#index'
end
resource :status do
get :job_queue
end
resources :customers, only: [:index, :update]
resources :enterprise_fees, only: [:destroy]
post '/product_images/:product_id', to: 'product_images#update_product_image'
resources :states, :only => [:index, :show]
resources :taxons, :only => [:index]
resources :taxonomies do
member do
get :jstree
resources :shops, only: [:show] do
collection do
get :closed_shops
end
end
resources :taxons do
resources :order_cycles do
get :products, on: :member
get :taxons, on: :member
get :properties, on: :member
end
resources :exchanges, only: [:show], to: 'exchange_products#index' do
get :products, to: 'exchange_products#index'
end
resource :status do
get :job_queue
end
resources :customers, only: [:index, :update]
resources :enterprise_fees, only: [:destroy]
post '/product_images/:product_id', to: 'product_images#update_product_image'
resources :states, :only => [:index, :show]
resources :taxons, :only => [:index]
resources :taxonomies do
member do
get :jstree
end
resources :taxons do
member do
get :jstree
end
end
end
end
match '*path', to: redirect(path: "/api/v0/%{path}"), via: :all, constraints: { path: /(?!v[0-9]).+/ }
end
end

View File

@@ -1,6 +1,6 @@
Darkswarm.controller "CookiesBannerCtrl", ($scope, CookiesBannerService, $http, $window)->
$scope.acceptCookies = ->
$http.post('/api/cookies/consent')
$http.post('/api/v0/cookies/consent')
CookiesBannerService.close()
CookiesBannerService.disable()

View File

@@ -1,30 +0,0 @@
require_dependency 'web/cookies_consent'
module Web
module Api
class CookiesConsentController < BaseController
include ActionController::Cookies
respond_to :json
def show
render json: { cookies_consent: cookies_consent.exists? }
end
def create
cookies_consent.set
show
end
def destroy
cookies_consent.destroy
show
end
private
def cookies_consent
@cookies_consent ||= Web::CookiesConsent.new(cookies, request.host)
end
end
end
end

View File

@@ -0,0 +1,34 @@
# frozen_string_literal: true
require_dependency 'web/cookies_consent'
module Web
module Api
module V0
class CookiesConsentController < BaseController
include ActionController::Cookies
respond_to :json
def show
render json: { cookies_consent: cookies_consent.exists? }
end
def create
cookies_consent.set
show
end
def destroy
cookies_consent.destroy
show
end
private
def cookies_consent
@cookies_consent ||= Web::CookiesConsent.new(cookies, request.host)
end
end
end
end
end

View File

@@ -1,8 +1,8 @@
# frozen_string_literal: true
Openfoodnetwork::Application.routes.append do
scope '/api/cookies' do
resource :consent, only: [:show, :create, :destroy], controller: "web/api/cookies_consent"
scope '/api/v0/cookies' do
resource :consent, only: [:show, :create, :destroy], controller: "web/api/v0/cookies_consent"
end
get "/angular-templates/:id", to: "web/angular_templates#show", constraints: { name: %r{[\/\w\.]+} }

View File

@@ -2,9 +2,9 @@
require 'spec_helper'
describe Api::BaseController do
describe Api::V0::BaseController do
render_views
controller(Api::BaseController) do
controller(Api::V0::BaseController) do
skip_authorization_check only: :index
def index

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe CustomersController, type: :controller do
describe V0::CustomersController, type: :controller do
include AuthenticationHelper
render_views

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe EnterpriseFeesController, type: :controller do
describe V0::EnterpriseFeesController, type: :controller do
include AuthenticationHelper
let!(:unreferenced_fee) { create(:enterprise_fee) }

View File

@@ -2,7 +2,7 @@
require 'spec_helper'
describe Api::EnterprisesController, type: :controller do
describe Api::V0::EnterprisesController, type: :controller do
render_views
let(:enterprise) { create(:distributor_enterprise) }

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe ExchangeProductsController, type: :controller do
describe V0::ExchangeProductsController, type: :controller do
include AuthenticationHelper
let(:order_cycle) { create(:order_cycle) }
@@ -54,7 +54,7 @@ module Api
let(:products_relation) { Spree::Product.includes(:variants).where("spree_variants.id": exchange.variants.map(&:id)) }
before do
stub_const("#{Api::ExchangeProductsController}::DEFAULT_PER_PAGE", 1)
stub_const("#{Api::V0::ExchangeProductsController}::DEFAULT_PER_PAGE", 1)
end
describe "when a specific page is requested" do

View File

@@ -3,7 +3,7 @@
require "spec_helper"
module Api
describe LogosController, type: :controller do
describe V0::LogosController, type: :controller do
include AuthenticationHelper
let(:admin_user) { create(:admin_user) }

View File

@@ -3,7 +3,7 @@
require "spec_helper"
module Api
describe OrderCyclesController, type: :controller do
describe V0::OrderCyclesController, type: :controller do
let!(:distributor) { create(:distributor_enterprise) }
let!(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
let!(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe OrdersController, type: :controller do
describe V0::OrdersController, type: :controller do
include AuthenticationHelper
render_views

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe ProductImagesController, type: :controller do
describe V0::ProductImagesController, type: :controller do
include AuthenticationHelper
render_views

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
require 'spree/core/product_duplicator'
describe Api::ProductsController, type: :controller do
describe Api::V0::ProductsController, type: :controller do
render_views
let(:supplier) { create(:supplier_enterprise) }

View File

@@ -3,7 +3,7 @@
require "spec_helper"
module Api
describe PromoImagesController, type: :controller do
describe V0::PromoImagesController, type: :controller do
include AuthenticationHelper
let(:admin_user) { create(:admin_user) }

View File

@@ -2,7 +2,7 @@
require 'spec_helper'
describe Api::ShipmentsController, type: :controller do
describe Api::V0::ShipmentsController, type: :controller do
render_views
let!(:shipment) { create(:shipment) }

View File

@@ -2,7 +2,7 @@
require 'spec_helper'
describe Api::ShopsController, type: :controller do
describe Api::V0::ShopsController, type: :controller do
include AuthenticationHelper
render_views

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe StatesController do
describe V0::StatesController do
render_views
let!(:state) { create(:state, name: "Victoria") }

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe StatusesController, type: :controller do
describe V0::StatusesController, type: :controller do
render_views
describe "job queue status" do

View File

@@ -3,7 +3,7 @@
require 'spec_helper'
module Api
describe TaxonomiesController do
describe V0::TaxonomiesController do
render_views
let(:taxonomy) { create(:taxonomy) }

View File

@@ -2,7 +2,7 @@
require 'spec_helper'
describe Api::TaxonsController do
describe Api::V0::TaxonsController do
render_views
let(:taxonomy) { create(:taxonomy) }

View File

@@ -3,7 +3,7 @@
require "spec_helper"
module Api
describe TermsAndConditionsController, type: :controller do
describe V0::TermsAndConditionsController, type: :controller do
include AuthenticationHelper
let(:enterprise_owner) { create(:user) }

View File

@@ -2,7 +2,7 @@
require 'spec_helper'
describe Api::VariantsController, type: :controller do
describe Api::V0::VariantsController, type: :controller do
render_views
let(:supplier) { FactoryBot.create(:supplier_enterprise) }

View File

@@ -66,7 +66,7 @@ describe Spree::Admin::ProductsController, type: :controller do
]
expect(response).to redirect_to(
'/api/products/bulk_products'
'/api/v0/products/bulk_products'
)
end
end

View File

@@ -16,7 +16,7 @@ feature '
let!(:new_product) { create(:product, supplier: supplier_enterprise) }
before do
stub_const("#{Api::ExchangeProductsController}::DEFAULT_PER_PAGE", 1)
stub_const("#{Api::V0::ExchangeProductsController}::DEFAULT_PER_PAGE", 1)
login_as_admin_and_visit admin_order_cycle_incoming_path(order_cycle)
expect(page).to have_content "1 / 2 selected"

View File

@@ -47,8 +47,8 @@ feature "Shops caching", js: true, caching: true do
let(:exchange) { order_cycle.exchanges.to_enterprises(distributor).outgoing.first }
let(:test_domain) { "#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}" }
let(:taxons_key) { "views/#{test_domain}/api/order_cycles/#{order_cycle.id}/taxons.json?distributor=#{distributor.id}" }
let(:properties_key) { "views/#{test_domain}/api/order_cycles/#{order_cycle.id}/properties.json?distributor=#{distributor.id}" }
let(:taxons_key) { "views/#{test_domain}/api/v0/order_cycles/#{order_cycle.id}/taxons.json?distributor=#{distributor.id}" }
let(:properties_key) { "views/#{test_domain}/api/v0/order_cycles/#{order_cycle.id}/properties.json?distributor=#{distributor.id}" }
let(:options) { { expires_in: CacheService::FILTERS_EXPIRY } }
before do

View File

@@ -820,7 +820,7 @@ describe "AdminProductEditCtrl", ->
}
]
$scope.dirtyProducts = {}
$httpBackend.expectDELETE("/api/products/13").respond 200, "data"
$httpBackend.expectDELETE("/api/v0/products/13").respond 200, "data"
$scope.deleteProduct $scope.products[1]
$httpBackend.flush()
@@ -839,7 +839,7 @@ describe "AdminProductEditCtrl", ->
DirtyProducts.addProductProperty 9, "someProperty", "something"
DirtyProducts.addProductProperty 13, "name", "P1"
$httpBackend.expectDELETE("/api/products/13").respond 200, "data"
$httpBackend.expectDELETE("/api/v0/products/13").respond 200, "data"
$scope.deleteProduct $scope.products[1]
$httpBackend.flush()
expect($scope.products).toEqual [
@@ -901,7 +901,7 @@ describe "AdminProductEditCtrl", ->
}
]
$scope.dirtyProducts = {}
$httpBackend.expectDELETE("/api/products/apples/variants/3").respond 200, "data"
$httpBackend.expectDELETE("/api/v0/products/apples/variants/3").respond 200, "data"
$scope.deleteVariant $scope.products[0], $scope.products[0].variants[0]
$httpBackend.flush()
@@ -931,7 +931,7 @@ describe "AdminProductEditCtrl", ->
DirtyProducts.addVariantProperty 9, 4, "price", 6.0
DirtyProducts.addProductProperty 13, "name", "P1"
$httpBackend.expectDELETE("/api/products/apples/variants/3").respond 200, "data"
$httpBackend.expectDELETE("/api/v0/products/apples/variants/3").respond 200, "data"
$scope.deleteVariant $scope.products[0], $scope.products[0].variants[0]
$httpBackend.flush()
expect($scope.products[0].variants).toEqual [

View File

@@ -127,7 +127,7 @@ describe "Enterprises service", ->
beforeEach ->
enterprise = new EnterpriseResource({ id: 15, permalink: "enterprise1", name: "Enterprise 1", logo: {} })
$httpBackend.expectDELETE("/api/enterprises/enterprise1/logo.json").respond 200, { id: 15, name: "Enterprise 1"}
$httpBackend.expectDELETE("/api/v0/enterprises/enterprise1/logo.json").respond 200, { id: 15, name: "Enterprise 1"}
Enterprises.removeLogo(enterprise).then( -> resolved = true)
$httpBackend.flush()
@@ -144,7 +144,7 @@ describe "Enterprises service", ->
beforeEach ->
enterprise = new EnterpriseResource( { id: 15, permalink: "enterprise1", name: "Enterprise 1" } )
$httpBackend.expectDELETE("/api/enterprises/enterprise1/logo.json").respond 409, { error: "obj" }
$httpBackend.expectDELETE("/api/v0/enterprises/enterprise1/logo.json").respond 409, { error: "obj" }
Enterprises.removeLogo(enterprise).catch( -> rejected = true)
$httpBackend.flush()
@@ -162,7 +162,7 @@ describe "Enterprises service", ->
beforeEach ->
enterprise = new EnterpriseResource({ id: 15, permalink: "enterprise1", name: "Enterprise 1", promo_image: {} })
$httpBackend.expectDELETE("/api/enterprises/enterprise1/promo_image.json").respond 200, { id: 15, name: "Enterprise 1"}
$httpBackend.expectDELETE("/api/v0/enterprises/enterprise1/promo_image.json").respond 200, { id: 15, name: "Enterprise 1"}
Enterprises.removePromoImage(enterprise).then( -> resolved = true)
$httpBackend.flush()
@@ -179,7 +179,7 @@ describe "Enterprises service", ->
beforeEach ->
enterprise = new EnterpriseResource( { id: 15, permalink: "enterprise1", name: "Enterprise 1" } )
$httpBackend.expectDELETE("/api/enterprises/enterprise1/promo_image.json").respond 409, { error: "obj" }
$httpBackend.expectDELETE("/api/v0/enterprises/enterprise1/promo_image.json").respond 409, { error: "obj" }
Enterprises.removePromoImage(enterprise).catch( -> rejected = true)
$httpBackend.flush()

View File

@@ -45,7 +45,7 @@ describe "LineItemsCtrl", ->
index: jasmine.createSpy('index').and.returnValue(lineItem)
all: [lineItem]
httpBackend.expectGET("/api/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bdistributor_id_eq%5D=&q%5Border_cycle_id_eq%5D=&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}}
httpBackend.expectGET("/api/v0/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bdistributor_id_eq%5D=&q%5Border_cycle_id_eq%5D=&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}}
httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor]
httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle]
httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bis_primary_producer_eq%5D=true").respond [supplier]

View File

@@ -19,7 +19,7 @@ describe "Orders service", ->
beforeEach ->
response = { orders: [{ id: 5, name: 'Order 1'}], pagination: {page: 1, pages: 1, results: 1} }
$httpBackend.expectGET('/api/orders.json').respond 200, response
$httpBackend.expectGET('/api/v0/orders.json').respond 200, response
result = Orders.index()
$httpBackend.flush()

View File

@@ -13,9 +13,9 @@ describe "BulkProducts service", ->
BulkProducts.products = [
id: 13
]
$httpBackend.expectPOST("/api/products/13/clone").respond 201,
$httpBackend.expectPOST("/api/v0/products/13/clone").respond 201,
id: 17
$httpBackend.expectGET("/api/products/17?template=bulk_show").respond 200, [
$httpBackend.expectGET("/api/v0/products/17?template=bulk_show").respond 200, [
id: 17
]
BulkProducts.cloneProduct BulkProducts.products[0]
@@ -30,8 +30,8 @@ describe "BulkProducts service", ->
spyOn(BulkProducts, "insertProductAfter")
spyOn(BulkProducts, "unpackProduct")
BulkProducts.products = [originalProduct]
$httpBackend.expectPOST("/api/products/16/clone").respond 201, clonedProduct
$httpBackend.expectGET("/api/products/17?template=bulk_show").respond 200, clonedProduct
$httpBackend.expectPOST("/api/v0/products/16/clone").respond 201, clonedProduct
$httpBackend.expectGET("/api/v0/products/17?template=bulk_show").respond 200, clonedProduct
BulkProducts.cloneProduct BulkProducts.products[0]
$httpBackend.flush()
expect(BulkProducts.unpackProduct).toHaveBeenCalledWith clonedProduct

View File

@@ -36,7 +36,7 @@ describe 'CreditCards service', ->
it "loads a success flash", ->
CreditCards.setDefault(card2)
$httpBackend.expectGET('/api/customers.json').respond 200, []
$httpBackend.expectGET('/api/v0/customers.json').respond 200, []
$httpBackend.flush()
expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({success: t('js.default_card_updated')})

View File

@@ -17,14 +17,14 @@ describe 'Customer', ->
it "nests the params inside 'customer'", ->
$httpBackend
.expectPUT('/api/customers/3.json', { customer: { id: 3 } })
.expectPUT('/api/v0/customers/3.json', { customer: { id: 3 } })
.respond 200, response
customer.update()
$httpBackend.flush()
describe "when the request succeeds", ->
it "shows a success flash", ->
$httpBackend.expectPUT('/api/customers/3.json').respond 200, response
$httpBackend.expectPUT('/api/v0/customers/3.json').respond 200, response
customer.update()
$httpBackend.flush()
expect(RailsFlashLoaderMock.loadFlash)
@@ -32,7 +32,7 @@ describe 'Customer', ->
describe "when the request fails", ->
it "shows a error flash", ->
$httpBackend.expectPUT('/api/customers/3.json').respond 400, { error: 'Some error' }
$httpBackend.expectPUT('/api/v0/customers/3.json').respond 400, { error: 'Some error' }
customer.update()
$httpBackend.flush()
expect(RailsFlashLoaderMock.loadFlash)

View File

@@ -16,7 +16,7 @@ describe 'Customers', ->
it "asks for customers and returns @all, promises to populate via @load", ->
spyOn(Customers,'load').and.callThrough()
$httpBackend.expectGET('/api/customers.json').respond 200, customerList
$httpBackend.expectGET('/api/v0/customers.json').respond 200, customerList
result = Customers.index()
$httpBackend.flush()
expect(Customers.load).toHaveBeenCalled()

View File

@@ -32,7 +32,7 @@ describe "EnterpriseRegistrationService", ->
describe "success", ->
beforeEach ->
spyOn(RegistrationServiceMock, "select")
$httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 200, 6
$httpBackend.expectPOST("/api/v0/enterprises?token=keykeykeykey").respond 200, 6
EnterpriseRegistrationService.create()
$httpBackend.flush()
@@ -46,7 +46,7 @@ describe "EnterpriseRegistrationService", ->
beforeEach ->
spyOn(RegistrationServiceMock, "select")
spyOn(window, "alert")
$httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 400, 6
$httpBackend.expectPOST("/api/v0/enterprises?token=keykeykeykey").respond 400, 6
EnterpriseRegistrationService.create()
$httpBackend.flush()
@@ -60,7 +60,7 @@ describe "EnterpriseRegistrationService", ->
beforeEach ->
spyOn(RegistrationServiceMock, "select")
spyOn(window, "alert")
$httpBackend.expectPOST("/api/enterprises?token=keykeykeykey").respond 400, {"error": "Invalid resource. Please fix errors and try again.", "errors": {"name": ["has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com."], "permalink": [] }}
$httpBackend.expectPOST("/api/v0/enterprises?token=keykeykeykey").respond 400, {"error": "Invalid resource. Please fix errors and try again.", "errors": {"name": ["has already been taken. If this is your enterprise and you would like to claim ownership, please contact the current manager of this profile at owner@example.com."], "permalink": [] }}
EnterpriseRegistrationService.create()
$httpBackend.flush()
@@ -78,7 +78,7 @@ describe "EnterpriseRegistrationService", ->
describe "success", ->
beforeEach ->
$httpBackend.expectPUT("/api/enterprises/78?token=keykeykeykey").respond 200, 6
$httpBackend.expectPUT("/api/v0/enterprises/78?token=keykeykeykey").respond 200, 6
EnterpriseRegistrationService.update('step')
$httpBackend.flush()
@@ -88,7 +88,7 @@ describe "EnterpriseRegistrationService", ->
describe "failure", ->
beforeEach ->
spyOn(window, "alert")
$httpBackend.expectPUT("/api/enterprises/78?token=keykeykeykey").respond 400, 6
$httpBackend.expectPUT("/api/v0/enterprises/78?token=keykeykeykey").respond 400, 6
EnterpriseRegistrationService.update('step')
$httpBackend.flush()

View File

@@ -13,7 +13,7 @@ describe 'Products service', ->
properties = null
taxons = null
GmapsGeo = {}
endpoint = "/api/order_cycles/1/products.json?distributor=1"
endpoint = "/api/v0/order_cycles/1/products.json?distributor=1"
beforeEach ->
product =

View File

@@ -2,8 +2,8 @@
require 'swagger_helper'
describe 'api/orders', type: :request do
path '/api/orders' do
describe 'api/v0/orders', type: :request do
path '/api/v0/orders' do
get('list orders') do
tags 'Orders'
# type should be replaced with swagger 3.01 valid schema: {type: string} when rswag #317 is resolved:

Some files were not shown because too many files have changed in this diff Show More