From 0c3463a2ec56a854d9621a404bd5717919e2f07e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Fri, 3 Mar 2023 10:09:07 +0100 Subject: [PATCH] (api) required pagination + new data --- CHANGELOG.md | 11 ++++++- .../open_api/v1/events_controller.rb | 8 +++-- .../open_api/v1/invoices_controller.rb | 4 +-- .../open_api/v1/reservations_controller.rb | 10 ++++--- .../open_api/v1/subscriptions_controller.rb | 11 ------- .../open_api/v1/users_controller.rb | 6 +++- app/doc/open_api/v1/events_doc.rb | 2 +- app/doc/open_api/v1/invoices_doc.rb | 2 +- app/doc/open_api/v1/reservations_doc.rb | 29 ++++++++++++++++--- app/doc/open_api/v1/subscriptions_doc.rb | 10 ++----- app/doc/open_api/v1/users_doc.rb | 2 +- .../open_api/v1/invoices/index.json.jbuilder | 2 +- .../v1/reservations/index.json.jbuilder | 12 ++++++-- .../v1/subscriptions/index.json.jbuilder | 6 +--- .../v1/trainings/_training.json.jbuilder | 2 ++ .../open_api/v1/trainings/index.json.jbuilder | 2 ++ config/initializers/apipie.rb | 1 + test/integration/open_api/events_test.rb | 6 ++-- test/integration/open_api/invoices_test.rb | 13 +++++---- .../integration/open_api/reservations_test.rb | 4 +-- .../open_api/subscriptions_test.rb | 7 +++-- 21 files changed, 92 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcb04cfad..8b3eda504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/app/controllers/open_api/v1/events_controller.rb b/app/controllers/open_api/v1/events_controller.rb index 3933abefb..3f4c61a12 100644 --- a/app/controllers/open_api/v1/events_controller.rb +++ b/app/controllers/open_api/v1/events_controller.rb @@ -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 diff --git a/app/controllers/open_api/v1/invoices_controller.rb b/app/controllers/open_api/v1/invoices_controller.rb index a4e9cf7d8..e2adec7de 100644 --- a/app/controllers/open_api/v1/invoices_controller.rb +++ b/app/controllers/open_api/v1/invoices_controller.rb @@ -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 diff --git a/app/controllers/open_api/v1/reservations_controller.rb b/app/controllers/open_api/v1/reservations_controller.rb index 93322bc3c..e8347abaa 100644 --- a/app/controllers/open_api/v1/reservations_controller.rb +++ b/app/controllers/open_api/v1/reservations_controller.rb @@ -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 diff --git a/app/controllers/open_api/v1/subscriptions_controller.rb b/app/controllers/open_api/v1/subscriptions_controller.rb index d6feceb8d..c9555e4e7 100644 --- a/app/controllers/open_api/v1/subscriptions_controller.rb +++ b/app/controllers/open_api/v1/subscriptions_controller.rb @@ -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 diff --git a/app/controllers/open_api/v1/users_controller.rb b/app/controllers/open_api/v1/users_controller.rb index 0d7b32811..c52109dfc 100644 --- a/app/controllers/open_api/v1/users_controller.rb +++ b/app/controllers/open_api/v1/users_controller.rb @@ -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 diff --git a/app/doc/open_api/v1/events_doc.rb b/app/doc/open_api/v1/events_doc.rb index dc1a5262b..4e3855b02 100644 --- a/app/doc/open_api/v1/events_doc.rb +++ b/app/doc/open_api/v1/events_doc.rb @@ -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 { diff --git a/app/doc/open_api/v1/invoices_doc.rb b/app/doc/open_api/v1/invoices_doc.rb index a2a880d73..ecf8f5f32 100644 --- a/app/doc/open_api/v1/invoices_doc.rb +++ b/app/doc/open_api/v1/invoices_doc.rb @@ -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 diff --git a/app/doc/open_api/v1/reservations_doc.rb b/app/doc/open_api/v1/reservations_doc.rb index 977e1c7e0..20ce4be73 100644 --- a/app/doc/open_api/v1/reservations_doc.rb +++ b/app/doc/open_api/v1/reservations_doc.rb @@ -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" + } + ] } ] } diff --git a/app/doc/open_api/v1/subscriptions_doc.rb b/app/doc/open_api/v1/subscriptions_doc.rb index 341285352..fbaef949f 100644 --- a/app/doc/open_api/v1/subscriptions_doc.rb +++ b/app/doc/open_api/v1/subscriptions_doc.rb @@ -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 diff --git a/app/doc/open_api/v1/users_doc.rb b/app/doc/open_api/v1/users_doc.rb index b55e78680..0dde4c2ad 100644 --- a/app/doc/open_api/v1/users_doc.rb +++ b/app/doc/open_api/v1/users_doc.rb @@ -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.' diff --git a/app/views/open_api/v1/invoices/index.json.jbuilder b/app/views/open_api/v1/invoices/index.json.jbuilder index e4def16bd..1a9630145 100644 --- a/app/views/open_api/v1/invoices/index.json.jbuilder +++ b/app/views/open_api/v1/invoices/index.json.jbuilder @@ -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 diff --git a/app/views/open_api/v1/reservations/index.json.jbuilder b/app/views/open_api/v1/reservations/index.json.jbuilder index dc4b24e5b..6e25bf003 100644 --- a/app/views/open_api/v1/reservations/index.json.jbuilder +++ b/app/views/open_api/v1/reservations/index.json.jbuilder @@ -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 diff --git a/app/views/open_api/v1/subscriptions/index.json.jbuilder b/app/views/open_api/v1/subscriptions/index.json.jbuilder index 7e5d5b9a1..3600ba508 100644 --- a/app/views/open_api/v1/subscriptions/index.json.jbuilder +++ b/app/views/open_api/v1/subscriptions/index.json.jbuilder @@ -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] diff --git a/app/views/open_api/v1/trainings/_training.json.jbuilder b/app/views/open_api/v1/trainings/_training.json.jbuilder index 02f0afbe2..d7b4ce8ed 100644 --- a/app/views/open_api/v1/trainings/_training.json.jbuilder +++ b/app/views/open_api/v1/trainings/_training.json.jbuilder @@ -1 +1,3 @@ +# frozen_string_literal: true + json.extract! training, :id, :name, :slug, :disabled, :updated_at, :created_at diff --git a/app/views/open_api/v1/trainings/index.json.jbuilder b/app/views/open_api/v1/trainings/index.json.jbuilder index 8028c6779..0e027dd8a 100644 --- a/app/views/open_api/v1/trainings/index.json.jbuilder +++ b/app/views/open_api/v1/trainings/index.json.jbuilder @@ -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 diff --git a/config/initializers/apipie.rb b/config/initializers/apipie.rb index 49f2ae5f7..4f0e86ed6 100644 --- a/config/initializers/apipie.rb +++ b/config/initializers/apipie.rb @@ -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*. diff --git a/test/integration/open_api/events_test.rb b/test/integration/open_api/events_test.rb index d8cfd2d55..09b9b6229 100644 --- a/test/integration/open_api/events_test.rb +++ b/test/integration/open_api/events_test.rb @@ -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 diff --git a/test/integration/open_api/invoices_test.rb b/test/integration/open_api/invoices_test.rb index 2455b03dd..eb19a02e6 100644 --- a/test/integration/open_api/invoices_test.rb +++ b/test/integration/open_api/invoices_test.rb @@ -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 diff --git a/test/integration/open_api/reservations_test.rb b/test/integration/open_api/reservations_test.rb index ed316bb5b..590da3c3c 100644 --- a/test/integration/open_api/reservations_test.rb +++ b/test/integration/open_api/reservations_test.rb @@ -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 diff --git a/test/integration/open_api/subscriptions_test.rb b/test/integration/open_api/subscriptions_test.rb index a6b676cf8..1eae38cb5 100644 --- a/test/integration/open_api/subscriptions_test.rb +++ b/test/integration/open_api/subscriptions_test.rb @@ -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