1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

(api) required pagination + new data

This commit is contained in:
Sylvain 2023-03-03 10:09:07 +01:00
parent 5e186ebf14
commit 0c3463a2ec
21 changed files with 92 additions and 58 deletions

View File

@ -1,11 +1,20 @@
# Changelog Fab-manager
- Extended OpenAPI endpoint to list events
- OpenAPI events endpoint returns category, theme and age_range
- OpenAPI reservation endpoint will return details for the reserved slots
- Fix a bug: some OpenAPI endpoints struggle and expire with timeout
- Fix a bug: OpenAPI events endpoint documentation does not refect the returned data
- Fix a bug: members can't change/cancel their reservations
- Fix a bug: admin events view should default to the list tab
- Fix a bug: event creation form should not allow setting multiple times the same price category
- Fix a bug: MAX_SIZE env varibles should not be quoted (#438)
- Fix a bug: unable to add OIDC scopes without discovery
- [BREAKING CHANGE] GET `open_api/v1/events` will necessarily be paginated
- [BREAKING CHANGE] GET `open_api/v1/invoices` will necessarily be paginated
- [BREAKING CHANGE] GET `open_api/v1/reservations` will necessarily be paginated
- [BREAKING CHANGE] GET `open_api/v1/users` will necessarily be paginated
- [BREAKING CHANGE] GET `open_api/v1/subscriptions` won't return `total_count`, `total_pages`, `page` or `page_siez` anymore. RFC-5988 headers (*Link*, *Total* and *Per-Page*) will continue to provide these same data.
- [BREAKING CHANGE] GET `open_api/v1/subscriptions` will return a `subscriptions` array instead of a `data` array.
## v5.7.2 2023 February 24

View File

@ -19,14 +19,16 @@ class OpenAPI::V1::EventsController < OpenAPI::V1::BaseController
@events = @events.where(id: may_array(params[:id])) if params[:id].present?
return if params[:page].blank?
@events = @events.page(params[:page]).per(per_page)
@events = @events.page(page).per(per_page)
paginate @events, per_page: per_page
end
private
def page
params[:page] || 1
end
def per_page
params[:per_page] || 20
end

View File

@ -8,13 +8,11 @@ class OpenAPI::V1::InvoicesController < OpenAPI::V1::BaseController
def index
@invoices = Invoice.order(created_at: :desc)
.includes(invoicing_profile: :user)
.includes(:payment_gateway_object, :invoicing_profile)
.references(:invoicing_profiles)
@invoices = @invoices.where(invoicing_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
return if params[:page].blank?
@invoices = @invoices.page(params[:page]).per(per_page)
paginate @invoices, per_page: per_page
end

View File

@ -8,16 +8,14 @@ class OpenAPI::V1::ReservationsController < OpenAPI::V1::BaseController
def index
@reservations = Reservation.order(created_at: :desc)
.includes(statistic_profile: :user)
.includes(slots_reservations: :slot, statistic_profile: :user)
.references(:statistic_profiles)
@reservations = @reservations.where(statistic_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
@reservations = @reservations.where(reservable_type: format_type(params[:reservable_type])) if params[:reservable_type].present?
@reservations = @reservations.where(reservable_id: may_array(params[:reservable_id])) if params[:reservable_id].present?
return if params[:page].blank?
@reservations = @reservations.page(params[:page]).per(per_page)
@reservations = @reservations.page(page).per(per_page)
paginate @reservations, per_page: per_page
end
@ -27,6 +25,10 @@ class OpenAPI::V1::ReservationsController < OpenAPI::V1::BaseController
type.singularize.classify
end
def page
params[:page] || 1
end
def per_page
params[:per_page] || 20
end

View File

@ -17,7 +17,6 @@ class OpenAPI::V1::SubscriptionsController < OpenAPI::V1::BaseController
@subscriptions = @subscriptions.where(statistic_profiles: { user_id: may_array(params[:user_id]) }) if params[:user_id].present?
@subscriptions = @subscriptions.page(page).per(per_page)
@pageination_meta = pageination_meta
paginate @subscriptions, per_page: per_page
end
@ -30,14 +29,4 @@ class OpenAPI::V1::SubscriptionsController < OpenAPI::V1::BaseController
def per_page
params[:per_page] || 20
end
def pageination_meta
total_count = Subscription.count
{
total_count: total_count,
total_pages: (total_count / per_page.to_f).ceil,
page: page.to_i,
page_size: per_page.to_i
}
end
end

View File

@ -18,12 +18,16 @@ class OpenAPI::V1::UsersController < OpenAPI::V1::BaseController
return if params[:page].blank?
@users = @users.page(params[:page]).per(per_page)
@users = @users.page(page).per(per_page)
paginate @users, per_page: per_page
end
private
def page
params[:page] || 1
end
def per_page
params[:per_page] || 20
end

View File

@ -16,7 +16,7 @@ class OpenAPI::V1::EventsDoc < OpenAPI::V1::BaseDoc
param_group :pagination
param :id, [Integer, Array], optional: true, desc: 'Scope the request to one or various events.'
param :upcoming, [FalseClass, TrueClass], optional: true, desc: 'Scope for the upcoming events.'
description 'Events index. Order by *created_at* desc.'
description 'Events index, pagniated. Ordered by *created_at* desc.'
example <<-EVENTS
# /open_api/v1/events?page=1&per_page=2
{

View File

@ -13,7 +13,7 @@ class OpenAPI::V1::InvoicesDoc < OpenAPI::V1::BaseDoc
doc_for :index do
api :GET, "/#{API_VERSION}/invoices", 'Invoices index'
description "Index of users' invoices, with optional pagination. Order by *created_at* descendant."
description 'Index of invoices, paginated. Ordered by *created_at* descendant.'
param_group :pagination
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
example <<-INVOICES

View File

@ -13,7 +13,7 @@ class OpenAPI::V1::ReservationsDoc < OpenAPI::V1::BaseDoc
doc_for :index do
api :GET, "/#{API_VERSION}/reservations", 'Reservations index'
description 'Index of reservations made by users, with optional pagination. Order by *created_at* descendant.'
description 'Index of reservations made by users, paginated. Ordered by *created_at* descendant.'
param_group :pagination
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
param :reservable_type, %w[Event Machine Space Training], optional: true, desc: 'Scope the request to a specific type of reservable.'
@ -42,7 +42,14 @@ class OpenAPI::V1::ReservationsDoc < OpenAPI::V1::BaseDoc
"description": "A partir de 15 ans : \r\n\r\nDécouvrez le Fab Lab, familiarisez-vous avec les découpeuses laser, les imprimantes 3D, la découpeuse vinyle ... ! Fabriquez un objet simple, à ramener chez vous ! \r\n\r\nAdoptez la Fab Lab attitude !",
"updated_at": "2016-03-21T15:55:56.306+01:00",
"created_at": "2016-03-21T15:55:56.306+01:00"
}
},
"reserved_slots": [
{
"canceled_at": "2016-05-20T09:40:12.201+01:00",
"start_at": "2016-06-03T14:00:00.000+01:00",
"end_at": "2016-06-03T15:00:00.000+01:00"
}
]
},
{
"id": 3252,
@ -63,7 +70,14 @@ class OpenAPI::V1::ReservationsDoc < OpenAPI::V1::BaseDoc
"description": "A partir de 15 ans : \r\n\r\nDécouvrez le Fab Lab, familiarisez-vous avec les découpeuses laser, les imprimantes 3D, la découpeuse vinyle ... ! Fabriquez un objet simple, à ramener chez vous ! \r\n\r\nAdoptez la Fab Lab attitude !",
"updated_at": "2016-05-03T13:53:47.172+02:00",
"created_at": "2016-03-07T15:58:14.113+01:00"
}
},
"reserved_slots": [
{
"canceled_at": null,
"start_at": "2016-06-02T16:00:00.000+01:00",
"end_at": "2016-06-02T17:00:00.000+01:00"
}
]
},
{
"id": 3251,
@ -84,7 +98,14 @@ class OpenAPI::V1::ReservationsDoc < OpenAPI::V1::BaseDoc
"description": "A partir de 15 ans : \r\n\r\nDécouvrez le Fab Lab, familiarisez-vous avec les découpeuses laser, les imprimantes 3D, la découpeuse vinyle ... ! Fabriquez un objet simple, à ramener chez vous ! \r\n\r\nAdoptez la Fab Lab attitude !",
"updated_at": "2016-03-21T15:55:56.306+01:00",
"created_at": "2016-03-21T15:55:56.306+01:00"
}
},
"reserved_slots": [
{
"canceled_at": null,
"start_at": "2016-06-03T14:00:00.000+01:00",
"end_at": "2016-06-03T15:00:00.000+01:00"
}
]
}
]
}

View File

@ -13,14 +13,14 @@ class OpenAPI::V1::SubscriptionsDoc < OpenAPI::V1::BaseDoc
doc_for :index do
api :GET, "/#{API_VERSION}/subscriptions", 'Subscriptions index'
description "Index of users' subscriptions, with optional pagination. Order by *created_at* descendant."
description "Index of users' subscriptions, paginated. Order by *created_at* descendant."
param_group :pagination
param :user_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various users.'
param :plan_id, [Integer, Array], optional: true, desc: 'Scope the request to one or various plans.'
example <<-SUBSCRIPTIONS
# /open_api/v1/subscriptions?user_id=211&page=1&per_page=3
{
"data": [
"subscriptions": [
{
"id": 2809,
"user_id": 211,
@ -45,11 +45,7 @@ class OpenAPI::V1::SubscriptionsDoc < OpenAPI::V1::BaseDoc
"canceled_at": null,
"plan_id": 1
}
],
"total_pages": 3,
"total_count": 9,
"page": 1,
"page_siez": 3
]
}
SUBSCRIPTIONS
end

View File

@ -13,7 +13,7 @@ class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc
doc_for :index do
api :GET, "/#{API_VERSION}/users", 'Users index'
description 'Users index, with optional pagination. Order by *created_at* descendant.'
description 'Users index, paginated. Ordered by *created_at* descendant.'
param_group :pagination
param :email, [String, Array], optional: true, desc: 'Filter users by *email* using strict matching.'
param :user_id, [Integer, Array], optional: true, desc: 'Filter users by *id* using strict matching.'

View File

@ -2,7 +2,7 @@
json.invoices @invoices do |invoice|
json.extract! invoice, :id, :reference, :total, :type, :description
json.user_id invoice.statistic_profile.user_id
json.user_id invoice.invoicing_profile.user_id
if invoice.payment_gateway_object
json.payment_gateway_object do
json.id invoice.payment_gateway_object.gateway_object_id

View File

@ -13,12 +13,18 @@ json.reservations @reservations do |reservation|
end
json.reservable do
if reservation.reservable_type == 'Training'
case reservation.reservable_type
when 'Training'
json.partial! 'open_api/v1/trainings/training', training: reservation.reservable
elsif reservation.reservable_type == 'Machine'
when 'Machine'
json.partial! 'open_api/v1/machines/machine', machine: reservation.reservable
elsif reservation.reservable_type == 'Event'
when 'Event'
json.partial! 'open_api/v1/events/event', event: reservation.reservable
end
end
json.reserved_slots reservation.slots_reservations do |slot_reservation|
json.extract! slot_reservation, :canceled_at
json.extract! slot_reservation.slot, :start_at, :end_at
end
end

View File

@ -1,10 +1,6 @@
# frozen_string_literal: true
json.data @subscriptions do |subscription|
json.subscriptions @subscriptions do |subscription|
json.extract! subscription, :id, :created_at, :expiration_date, :canceled_at, :plan_id
json.user_id subscription.statistic_profile.user_id
end
json.total_pages @pageination_meta[:total_pages]
json.total_count @pageination_meta[:total_count]
json.page @pageination_meta[:page]
json.page_siez @pageination_meta[:page_size]

View File

@ -1 +1,3 @@
# frozen_string_literal: true
json.extract! training, :id, :name, :slug, :disabled, :updated_at, :created_at

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
json.trainings @trainings do |training|
json.partial! 'open_api/v1/trainings/training', training: training
json.extract! training, :nb_total_places, :description

View File

@ -12,6 +12,7 @@ Apipie.configure do |config|
config.app_info['v1'] = <<-RDOC
= Pagination
---
Some endpoints are paginated, because they provides many data. Other provides optional pagination.
You can ask for pagination on your requests, by providing the GET parameters *page* and *per_page* (when it's available).
The meta-data about pagination will be returned in the headers, following RFC-5988 standard for web linking.
It uses headers *Link*, *Total* and *Per-Page*.

View File

@ -9,7 +9,7 @@ class OpenApi::EventsTest < ActionDispatch::IntegrationTest
@token = OpenAPI::Client.find_by(name: 'minitest').token
end
test 'list all events' do
test 'list events' do
get '/open_api/v1/events', headers: open_api_headers(@token)
assert_response :success
events = json_response(response.body)
@ -29,7 +29,7 @@ class OpenApi::EventsTest < ActionDispatch::IntegrationTest
assert(events[:events].all? { |event| !event[:url].nil? })
end
test 'list all events with pagination' do
test 'list events with pagination details' do
get '/open_api/v1/events?page=1&per_page=5', headers: open_api_headers(@token)
assert_response :success
end
@ -44,7 +44,7 @@ class OpenApi::EventsTest < ActionDispatch::IntegrationTest
assert_response :success
end
test 'list all upcoming events with pagination' do
test 'list all upcoming events with pagination details' do
get '/open_api/v1/events?upcoming=true&page=1&per_page=5', headers: open_api_headers(@token)
assert_response :success
end

View File

@ -9,22 +9,25 @@ class OpenApi::InvoicesTest < ActionDispatch::IntegrationTest
@token = OpenAPI::Client.find_by(name: 'minitest').token
end
test 'list all invoices' do
test 'list invoices' do
get '/open_api/v1/invoices', headers: open_api_headers(@token)
assert_response :success
assert_equal Mime[:json], response.content_type
assert_not_empty json_response(response.body)[:invoices]
end
test 'list all invoices with pagination' do
test 'list invoices with pagination details' do
get '/open_api/v1/invoices?page=1&per_page=5', headers: open_api_headers(@token)
assert_response :success
end
test 'list all invoices for a user' do
test 'list invoices for a user' do
get '/open_api/v1/invoices?user_id=3', headers: open_api_headers(@token)
assert_response :success
end
test 'list all invoices for a user with pagination' do
test 'list invoices for a user with pagination details' do
get '/open_api/v1/invoices?user_id=3&page=1&per_page=5', headers: open_api_headers(@token)
assert_response :success
end
@ -32,6 +35,6 @@ class OpenApi::InvoicesTest < ActionDispatch::IntegrationTest
test 'download an invoice' do
get '/open_api/v1/invoices/3/download', headers: open_api_headers(@token)
assert_response :success
assert_match /^inline; filename=/, response.headers['Content-Disposition']
assert_match(/^inline; filename=/, response.headers['Content-Disposition'])
end
end

View File

@ -9,12 +9,12 @@ class OpenApi::ReservationsTest < ActionDispatch::IntegrationTest
@token = OpenAPI::Client.find_by(name: 'minitest').token
end
test 'list all reservations' do
test 'list reservations ' do
get '/open_api/v1/reservations', headers: open_api_headers(@token)
assert_response :success
assert_equal Mime[:json], response.content_type
assert_equal Reservation.count, json_response(response.body)[:reservations].length
assert_not_empty json_response(response.body)[:reservations]
end
test 'list all reservations with pagination' do

View File

@ -9,12 +9,15 @@ class OpenApi::SubscriptionsTest < ActionDispatch::IntegrationTest
@token = OpenAPI::Client.find_by(name: 'minitest').token
end
test 'list all subscriptions' do
test 'list subscriptions' do
get '/open_api/v1/subscriptions', headers: open_api_headers(@token)
assert_response :success
assert_equal Mime[:json], response.content_type
assert_not_empty json_response(response.body)[:subscriptions]
end
test 'list all subscriptions with pagination' do
test 'list subscriptions with pagination' do
get '/open_api/v1/subscriptions?page=1&per_page=5', headers: open_api_headers(@token)
assert_response :success
end