From 92b3471032debb81ee2e14769fe353e15035ae37 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Wed, 4 May 2016 11:48:14 +0200 Subject: [PATCH 01/30] creates models open_api client and calls_count_tracing, adds worker to trace calls count --- Gemfile | 2 ++ Gemfile.lock | 3 +++ app/models/open_api.rb | 5 +++++ app/models/open_api/calls_count_tracing.rb | 4 ++++ app/models/open_api/client.rb | 9 ++++++++ app/models/open_api/parameter_error.rb | 1 + .../open_api_trace_calls_count_worker.rb | 10 +++++++++ config/schedule.yml | 4 ++++ .../20160504085703_create_open_api_clients.rb | 10 +++++++++ ...05_create_open_api_calls_count_tracings.rb | 10 +++++++++ db/schema.rb | 21 ++++++++++++++++++- 11 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 app/models/open_api.rb create mode 100644 app/models/open_api/calls_count_tracing.rb create mode 100644 app/models/open_api/client.rb create mode 100644 app/models/open_api/parameter_error.rb create mode 100644 app/workers/open_api_trace_calls_count_worker.rb create mode 100644 db/migrate/20160504085703_create_open_api_clients.rb create mode 100644 db/migrate/20160504085905_create_open_api_calls_count_tracings.rb diff --git a/Gemfile b/Gemfile index b8aa689be4..4fe2bcb562 100644 --- a/Gemfile +++ b/Gemfile @@ -139,3 +139,5 @@ gem 'protected_attributes' gem 'message_format' gem 'openlab_ruby' + +gem 'has_secure_token' diff --git a/Gemfile.lock b/Gemfile.lock index 5b49a40db3..62a082fab9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -164,6 +164,8 @@ GEM activerecord (>= 4.0.0) globalid (0.3.6) activesupport (>= 4.1.0) + has_secure_token (1.0.0) + activerecord (>= 3.0) hashdiff (0.3.0) hashie (3.4.2) highline (1.7.1) @@ -452,6 +454,7 @@ DEPENDENCIES foreman forgery friendly_id (~> 5.1.0) + has_secure_token jbuilder (~> 2.0) jquery-rails kaminari diff --git a/app/models/open_api.rb b/app/models/open_api.rb new file mode 100644 index 0000000000..1e18b02f20 --- /dev/null +++ b/app/models/open_api.rb @@ -0,0 +1,5 @@ +module OpenAPI + def self.table_name_prefix + 'open_api_' + end +end diff --git a/app/models/open_api/calls_count_tracing.rb b/app/models/open_api/calls_count_tracing.rb new file mode 100644 index 0000000000..c460e06f46 --- /dev/null +++ b/app/models/open_api/calls_count_tracing.rb @@ -0,0 +1,4 @@ +class OpenAPI::CallsCountTracing < ActiveRecord::Base + belongs_to :client, foreign_key: :open_api_client_id + validates :client, :at, presence: true +end diff --git a/app/models/open_api/client.rb b/app/models/open_api/client.rb new file mode 100644 index 0000000000..05d2483353 --- /dev/null +++ b/app/models/open_api/client.rb @@ -0,0 +1,9 @@ +class OpenAPI::Client < ActiveRecord::Base + has_many :calls_count_tracings, foreign_key: :open_api_client_id, dependent: :destroy + has_secure_token + validates :name, presence: true + + def increment_calls_count + update_column(:calls_count, calls_count+1) + end +end diff --git a/app/models/open_api/parameter_error.rb b/app/models/open_api/parameter_error.rb new file mode 100644 index 0000000000..fd758af247 --- /dev/null +++ b/app/models/open_api/parameter_error.rb @@ -0,0 +1 @@ +class OpenAPI::ParameterError < StandardError; end diff --git a/app/workers/open_api_trace_calls_count_worker.rb b/app/workers/open_api_trace_calls_count_worker.rb new file mode 100644 index 0000000000..1120ddb8b5 --- /dev/null +++ b/app/workers/open_api_trace_calls_count_worker.rb @@ -0,0 +1,10 @@ +class OpenAPITraceCallsCountWorker < ActiveJob::Base + include Sidekiq::Worker + sidekiq_options queue: 'default', retry: true + + def perform + OpenAPI::Client.find_each do |client| + OpenAPI::CallsCountTracing.create!(client: client, calls_count: client.calls_count, at: DateTime.now) + end + end +end diff --git a/config/schedule.yml b/config/schedule.yml index 09e819987a..32ecc8fcb8 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -14,3 +14,7 @@ generate_statistic: cron: "0 1 * * *" class: "StatisticWorker" queue: default + +open_api_trace_calls_count: + cron: "0 4 * * 0" # every sunday at 4am + class: "OpenAPITraceCallsCountWorker" diff --git a/db/migrate/20160504085703_create_open_api_clients.rb b/db/migrate/20160504085703_create_open_api_clients.rb new file mode 100644 index 0000000000..fd070547d3 --- /dev/null +++ b/db/migrate/20160504085703_create_open_api_clients.rb @@ -0,0 +1,10 @@ +class CreateOpenAPIClients < ActiveRecord::Migration + def change + create_table :open_api_clients do |t| + t.string :name + t.integer :calls_count, default: 0 + t.string :token + t.timestamps null: false + end + end +end diff --git a/db/migrate/20160504085905_create_open_api_calls_count_tracings.rb b/db/migrate/20160504085905_create_open_api_calls_count_tracings.rb new file mode 100644 index 0000000000..35e2139302 --- /dev/null +++ b/db/migrate/20160504085905_create_open_api_calls_count_tracings.rb @@ -0,0 +1,10 @@ +class CreateOpenAPICallsCountTracings < ActiveRecord::Migration + def change + create_table :open_api_calls_count_tracings do |t| + t.belongs_to :open_api_client, foreign_key: true, index: true + t.integer :calls_count, null: false + t.datetime :at, null: false + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f346c4f84b..67ee62ce5c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160119131623) do +ActiveRecord::Schema.define(version: 20160504085905) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -267,6 +267,24 @@ add_index "offer_days", ["subscription_id"], name: "index_offer_days_on_subscription_id", using: :btree + create_table "open_api_calls_count_tracings", force: :cascade do |t| + t.integer "open_api_client_id" + t.integer "calls_count", null: false + t.datetime "at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "open_api_calls_count_tracings", ["open_api_client_id"], name: "index_open_api_calls_count_tracings_on_open_api_client_id", using: :btree + + create_table "open_api_clients", force: :cascade do |t| + t.string "name" + t.integer "calls_count", default: 0 + t.string "token" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "plans", force: :cascade do |t| t.string "name" t.integer "amount" @@ -643,6 +661,7 @@ add_foreign_key "availability_tags", "availabilities" add_foreign_key "availability_tags", "tags" add_foreign_key "o_auth2_mappings", "o_auth2_providers" + add_foreign_key "open_api_calls_count_tracings", "open_api_clients" add_foreign_key "prices", "groups" add_foreign_key "prices", "plans" add_foreign_key "user_tags", "tags" From 1b0f9575b574fcd5f86a547b24045490bd9de118 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Wed, 4 May 2016 11:59:51 +0200 Subject: [PATCH 02/30] adds a base controller for open_api/v1 --- .../open_api/v1/base_controller.rb | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/controllers/open_api/v1/base_controller.rb diff --git a/app/controllers/open_api/v1/base_controller.rb b/app/controllers/open_api/v1/base_controller.rb new file mode 100644 index 0000000000..0fc1118930 --- /dev/null +++ b/app/controllers/open_api/v1/base_controller.rb @@ -0,0 +1,43 @@ +class OpenAPI::V1::BaseController < ActionController::Base + protect_from_forgery with: :null_session + before_action :authenticate + before_action :increment_calls_count + + rescue_from ActiveRecord::RecordNotFound, with: :not_found + rescue_from OpenAPI::ParameterError, with: :bad_request + rescue_from ActionController::ParameterMissing, with: :bad_request + + helper_method :current_api_client + + protected + def not_found + render json: { errors: ["Not found"] }, status: :not_found + end + + def bad_request + render json: { errors: ["Bad request"] }, status: :bad_request + end + + def authenticate + authenticate_token || render_unauthorized + end + + def authenticate_token + authenticate_with_http_token do |token, options| + @open_api_client = OpenAPI::Client.find_by(token: token) + end + end + + def current_api_client + @open_api_client + end + + def render_unauthorized + render json: { errors: ['Bad credentials'] }, status: 401 + end + + private + def increment_calls_count + @open_api_client.increment_calls_count + end +end From 7f032526cbf6fd26995974ecaef11a7f2b38eecc Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Wed, 4 May 2016 18:17:50 +0200 Subject: [PATCH 03/30] implements open_api various endpoints --- CHANGELOG.md | 7 +- Gemfile | 1 + Gemfile.lock | 2 + .../v1/bookable_machines_controller.rb | 37 ++++ .../open_api/v1/events_controller.rb | 15 ++ .../open_api/v1/invoices_controller.rb | 24 +++ .../open_api/v1/machines_controller.rb | 5 + .../open_api/v1/reservations_controller.rb | 33 ++++ .../open_api/v1/trainings_controller.rb | 5 + .../open_api/v1/user_trainings_controller.rb | 27 +++ .../open_api/v1/users_controller.rb | 23 +++ app/models/event.rb | 7 +- .../v1/bookable_machines/index.json.jbuilder | 5 + .../open_api/v1/events/_event.json.jbuilder | 1 + .../open_api/v1/events/index.json.jbuilder | 4 + .../open_api/v1/invoices/index.json.jbuilder | 8 + .../v1/machines/_machine.json.jbuilder | 1 + .../open_api/v1/machines/index.json.jbuilder | 4 + .../v1/reservations/index.json.jbuilder | 19 ++ .../v1/trainings/_training.json.jbuilder | 1 + .../open_api/v1/trainings/index.json.jbuilder | 4 + .../v1/user_trainings/index.json.jbuilder | 15 ++ .../open_api/v1/users/_user.json.jbuilder | 15 ++ .../open_api/v1/users/index.json.jbuilder | 3 + config/routes.rb | 22 ++- db/schema.rb | 176 +++++++++--------- lib/tasks/fablab/fix.rake | 12 ++ 27 files changed, 383 insertions(+), 93 deletions(-) create mode 100644 app/controllers/open_api/v1/bookable_machines_controller.rb create mode 100644 app/controllers/open_api/v1/events_controller.rb create mode 100644 app/controllers/open_api/v1/invoices_controller.rb create mode 100644 app/controllers/open_api/v1/machines_controller.rb create mode 100644 app/controllers/open_api/v1/reservations_controller.rb create mode 100644 app/controllers/open_api/v1/trainings_controller.rb create mode 100644 app/controllers/open_api/v1/user_trainings_controller.rb create mode 100644 app/controllers/open_api/v1/users_controller.rb create mode 100644 app/views/open_api/v1/bookable_machines/index.json.jbuilder create mode 100644 app/views/open_api/v1/events/_event.json.jbuilder create mode 100644 app/views/open_api/v1/events/index.json.jbuilder create mode 100644 app/views/open_api/v1/invoices/index.json.jbuilder create mode 100644 app/views/open_api/v1/machines/_machine.json.jbuilder create mode 100644 app/views/open_api/v1/machines/index.json.jbuilder create mode 100644 app/views/open_api/v1/reservations/index.json.jbuilder create mode 100644 app/views/open_api/v1/trainings/_training.json.jbuilder create mode 100644 app/views/open_api/v1/trainings/index.json.jbuilder create mode 100644 app/views/open_api/v1/user_trainings/index.json.jbuilder create mode 100644 app/views/open_api/v1/users/_user.json.jbuilder create mode 100644 app/views/open_api/v1/users/index.json.jbuilder create mode 100644 lib/tasks/fablab/fix.rake diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d37ccc05..175e52b61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog Fab Manager +## next deploy + +- fix some reservations was referencing reservable not present in databse +run `bundle exec rake fablab:fix:reservations_not_existing_reservable` to fix it + ## v2.1.1 2016 May 3 - Fix a bug concerning openlab projects initialization in production env - Fix a bug: user is not redirected after changing is duplicated e-mail on the SSO provider @@ -14,4 +19,4 @@ - Fix a bug: custom asset favicon-file favicon file is not set - Fix a security issue: stripe card token is now checked on server side on new/renew subscription - Translated notification e-mails into english language -- Subscription extension logic has been extracted into a microservice \ No newline at end of file +- Subscription extension logic has been extracted into a microservice diff --git a/Gemfile b/Gemfile index 4fe2bcb562..89209a76e7 100644 --- a/Gemfile +++ b/Gemfile @@ -140,4 +140,5 @@ gem 'message_format' gem 'openlab_ruby' +gem 'api-pagination' gem 'has_secure_token' diff --git a/Gemfile.lock b/Gemfile.lock index 62a082fab9..dc25cdef4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -42,6 +42,7 @@ GEM tzinfo (~> 1.1) addressable (2.3.8) ansi (1.5.0) + api-pagination (4.3.0) arel (6.0.3) autoprefixer-rails (5.1.8) execjs @@ -432,6 +433,7 @@ DEPENDENCIES aasm actionpack-page_caching active_record_query_trace + api-pagination awesome_print bootstrap-sass byebug diff --git a/app/controllers/open_api/v1/bookable_machines_controller.rb b/app/controllers/open_api/v1/bookable_machines_controller.rb new file mode 100644 index 0000000000..4909d3a1d3 --- /dev/null +++ b/app/controllers/open_api/v1/bookable_machines_controller.rb @@ -0,0 +1,37 @@ +class OpenAPI::V1::BookableMachinesController < OpenAPI::V1::BaseController + def index + raise ActionController::ParameterMissing if params[:user_id].blank? + + @machines = Machine.all + + @machines = @machines.where(id: params[:machine_id]) if params[:machine_id].present? + + @machines = @machines.to_a + + user = User.find(params[:user_id]) + + @machines.delete_if do |machine| + (machine.trainings.count != 0) and !user.is_training_machine?(machine) + end + + + @hours_remaining = Hash[@machines.map { |m| [m.id, 0] }] + + + + if user.subscription + plan_id = user.subscription.plan_id + + @machines.each do |machine| + credit = Credit.find_by(plan_id: plan_id, creditable: machine) + users_credit = user.users_credits.find_by(credit: credit) if credit + + if credit + @hours_remaining[machine.id] = credit.hours - (users_credit.try(:hours_used) || 0) + else + @hours_remaining[machine.id] = 0 + end + end + end + end +end diff --git a/app/controllers/open_api/v1/events_controller.rb b/app/controllers/open_api/v1/events_controller.rb new file mode 100644 index 0000000000..a1eac367f6 --- /dev/null +++ b/app/controllers/open_api/v1/events_controller.rb @@ -0,0 +1,15 @@ +class OpenAPI::V1::EventsController < OpenAPI::V1::BaseController + def index + @events = Event.order(created_at: :desc) + + if params[:page].present? + @events = @events.page(params[:page]).per(per_page) + paginate @events, per_page: per_page + end + end + + private + def per_page + params[:per_page] || 20 + end +end diff --git a/app/controllers/open_api/v1/invoices_controller.rb b/app/controllers/open_api/v1/invoices_controller.rb new file mode 100644 index 0000000000..0b02702481 --- /dev/null +++ b/app/controllers/open_api/v1/invoices_controller.rb @@ -0,0 +1,24 @@ +class OpenAPI::V1::InvoicesController < OpenAPI::V1::BaseController + def index + @invoices = Invoice.order(created_at: :desc) + + if params[:user_id].present? + @invoices = @invoices.where(user_id: params[:user_id]) + end + + if params[:page].present? + @invoices = @invoices.page(params[:page]).per(per_page) + paginate @invoices, per_page: per_page + end + end + + def download + @invoice = Invoice.find(params[:id]) + send_file File.join(Rails.root, @invoice.file), type: 'application/pdf', disposition: 'inline', filename: @invoice.filename + end + + private + def per_page + params[:per_page] || 20 + end +end diff --git a/app/controllers/open_api/v1/machines_controller.rb b/app/controllers/open_api/v1/machines_controller.rb new file mode 100644 index 0000000000..be55c8d26a --- /dev/null +++ b/app/controllers/open_api/v1/machines_controller.rb @@ -0,0 +1,5 @@ +class OpenAPI::V1::MachinesController < OpenAPI::V1::BaseController + def index + @machines = Machine.order(:created_at) + end +end diff --git a/app/controllers/open_api/v1/reservations_controller.rb b/app/controllers/open_api/v1/reservations_controller.rb new file mode 100644 index 0000000000..82935eda87 --- /dev/null +++ b/app/controllers/open_api/v1/reservations_controller.rb @@ -0,0 +1,33 @@ +class OpenAPI::V1::ReservationsController < OpenAPI::V1::BaseController + def index + @reservations = Reservation.order(created_at: :desc) + + if params[:user_id].present? + @reservations = @reservations.where(user_id: params[:user_id]) + else + @reservations = @reservations.includes(user: :profile) + end + + if params[:reservable_type].present? + @reservations = @reservations.where(reservable_type: format_type(params[:reservable_type])) + end + + if params[:reservable_id].present? + @reservations = @reservations.where(reservable_id: params[:reservable_id]) + end + + if params[:page].present? + @reservations = @reservations.page(params[:page]).per(per_page) + paginate @reservations, per_page: per_page + end + end + + private + def format_type(type) + type.singularize.classify + end + + def per_page + params[:per_page] || 20 + end +end diff --git a/app/controllers/open_api/v1/trainings_controller.rb b/app/controllers/open_api/v1/trainings_controller.rb new file mode 100644 index 0000000000..d0d1cba356 --- /dev/null +++ b/app/controllers/open_api/v1/trainings_controller.rb @@ -0,0 +1,5 @@ +class OpenAPI::V1::TrainingsController < OpenAPI::V1::BaseController + def index + @trainings = Training.order(:created_at) + end +end diff --git a/app/controllers/open_api/v1/user_trainings_controller.rb b/app/controllers/open_api/v1/user_trainings_controller.rb new file mode 100644 index 0000000000..9fc4e7d1fb --- /dev/null +++ b/app/controllers/open_api/v1/user_trainings_controller.rb @@ -0,0 +1,27 @@ +class OpenAPI::V1::UserTrainingsController < OpenAPI::V1::BaseController + def index + @user_trainings = UserTraining.order(created_at: :desc) + + if params[:user_id].present? + @user_trainings = @user_trainings.where(user_id: params[:user_id]) + else + @user_trainings = @user_trainings.includes(user: :profile) + end + + if params[:training_id].present? + @user_trainings = @user_trainings.where(training_id: params[:training_id]) + else + @user_trainings = @user_trainings.includes(:training) + end + + if params[:page].present? + @user_trainings = @user_trainings.page(params[:page]).per(per_page) + paginate @user_trainings, per_page: per_page + end + end + + private + def per_page + params[:per_page] || 20 + end +end diff --git a/app/controllers/open_api/v1/users_controller.rb b/app/controllers/open_api/v1/users_controller.rb new file mode 100644 index 0000000000..d3767e808b --- /dev/null +++ b/app/controllers/open_api/v1/users_controller.rb @@ -0,0 +1,23 @@ +class OpenAPI::V1::UsersController < OpenAPI::V1::BaseController + def index + @users = User.order(created_at: :desc).includes(:group, :profile) + + if params[:email].present? + @users = @users.where(email: params[:email].downcase) + end + + if params[:user_id].present? + @users = @users.where(id: params[:user_id]) + end + + if params[:page].present? + @users = @users.page(params[:page]).per(per_page) + paginate @users, per_page: per_page + end + end + + private + def per_page + params[:per_page] || 20 + end +end diff --git a/app/models/event.rb b/app/models/event.rb index 8007f6a4eb..6b96a7d46a 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -6,6 +6,7 @@ class Event < ActiveRecord::Base has_many :event_files, as: :viewable, dependent: :destroy accepts_nested_attributes_for :event_files, allow_destroy: true, reject_if: :all_blank has_and_belongs_to_many :categories, join_table: :events_categories + has_many :reservations, as: :reservable, dependent: :destroy belongs_to :availability, dependent: :destroy accepts_nested_attributes_for :availability @@ -32,9 +33,9 @@ def safe_destroy end end - def reservations - Reservation.where(reservable: self) - end + # def reservations + # Reservation.where(reservable: self) + # end private def event_recurrence diff --git a/app/views/open_api/v1/bookable_machines/index.json.jbuilder b/app/views/open_api/v1/bookable_machines/index.json.jbuilder new file mode 100644 index 0000000000..49618a9cf9 --- /dev/null +++ b/app/views/open_api/v1/bookable_machines/index.json.jbuilder @@ -0,0 +1,5 @@ +json.machines @machines do |machine| + json.partial! 'open_api/v1/machines/machine', machine: machine + json.extract! machine, :description, :spec + json.hours_remaining @hours_remaining[machine.id] +end diff --git a/app/views/open_api/v1/events/_event.json.jbuilder b/app/views/open_api/v1/events/_event.json.jbuilder new file mode 100644 index 0000000000..e49ab27472 --- /dev/null +++ b/app/views/open_api/v1/events/_event.json.jbuilder @@ -0,0 +1 @@ +json.extract! event, :id, :title, :description, :updated_at, :created_at diff --git a/app/views/open_api/v1/events/index.json.jbuilder b/app/views/open_api/v1/events/index.json.jbuilder new file mode 100644 index 0000000000..7cc7c2db80 --- /dev/null +++ b/app/views/open_api/v1/events/index.json.jbuilder @@ -0,0 +1,4 @@ +json.events @events do |event| + json.partial! 'open_api/v1/events/event', event: event + json.extract! event, :amount, :reduced_amount, :nb_total_places, :nb_free_places +end diff --git a/app/views/open_api/v1/invoices/index.json.jbuilder b/app/views/open_api/v1/invoices/index.json.jbuilder new file mode 100644 index 0000000000..a3a4a508b5 --- /dev/null +++ b/app/views/open_api/v1/invoices/index.json.jbuilder @@ -0,0 +1,8 @@ +json.invoices @invoices do |invoice| + json.extract! invoice, :id, :invoiced_id, :user_id, :invoiced_type, :stp_invoice_id, :reference, :total, :type, :description + + json.invoice_url download_open_api_v1_invoice_path(invoice) + json.invoiced do + json.created_at invoice.invoiced.created_at + end +end diff --git a/app/views/open_api/v1/machines/_machine.json.jbuilder b/app/views/open_api/v1/machines/_machine.json.jbuilder new file mode 100644 index 0000000000..b8d99963e9 --- /dev/null +++ b/app/views/open_api/v1/machines/_machine.json.jbuilder @@ -0,0 +1 @@ +json.extract! machine, :id, :name, :slug, :updated_at, :created_at diff --git a/app/views/open_api/v1/machines/index.json.jbuilder b/app/views/open_api/v1/machines/index.json.jbuilder new file mode 100644 index 0000000000..d6f67d1ee2 --- /dev/null +++ b/app/views/open_api/v1/machines/index.json.jbuilder @@ -0,0 +1,4 @@ +json.machines @machines do |machine| + json.partial! 'open_api/v1/machines/machine', machine: machine + json.extract! machine, :description, :spec +end diff --git a/app/views/open_api/v1/reservations/index.json.jbuilder b/app/views/open_api/v1/reservations/index.json.jbuilder new file mode 100644 index 0000000000..04d20bd801 --- /dev/null +++ b/app/views/open_api/v1/reservations/index.json.jbuilder @@ -0,0 +1,19 @@ +json.reservations @reservations do |reservation| + json.extract! reservation, :id, :user_id, :reservable_id, :reservable_type, :updated_at, :created_at + + if reservation.association(:user).loaded? + json.user do + json.partial! 'open_api/v1/users/user', user: reservation.user + end + end + + json.reservable do + if reservation.reservable_type == "Training" + json.partial! 'open_api/v1/trainings/training', training: reservation.reservable + elsif reservation.reservable_type == "Machine" + json.partial! 'open_api/v1/machines/machine', machine: reservation.reservable + elsif reservation.reservable_type == "Event" + json.partial! 'open_api/v1/events/event', event: reservation.reservable + end + end +end diff --git a/app/views/open_api/v1/trainings/_training.json.jbuilder b/app/views/open_api/v1/trainings/_training.json.jbuilder new file mode 100644 index 0000000000..857337eda9 --- /dev/null +++ b/app/views/open_api/v1/trainings/_training.json.jbuilder @@ -0,0 +1 @@ +json.extract! training, :id, :name, :slug, :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 new file mode 100644 index 0000000000..8028c67793 --- /dev/null +++ b/app/views/open_api/v1/trainings/index.json.jbuilder @@ -0,0 +1,4 @@ +json.trainings @trainings do |training| + json.partial! 'open_api/v1/trainings/training', training: training + json.extract! training, :nb_total_places, :description +end diff --git a/app/views/open_api/v1/user_trainings/index.json.jbuilder b/app/views/open_api/v1/user_trainings/index.json.jbuilder new file mode 100644 index 0000000000..868d73752a --- /dev/null +++ b/app/views/open_api/v1/user_trainings/index.json.jbuilder @@ -0,0 +1,15 @@ +json.user_trainings @user_trainings do |user_training| + json.extract! user_training, :id, :user_id, :training_id, :updated_at, :created_at + + if user_training.association(:user).loaded? + json.user do + json.partial! 'open_api/v1/users/user', user: user_training.user + end + end + + if user_training.association(:training).loaded? + json.training do + json.partial! 'open_api/v1/trainings/training', training: user_training.training + end + end +end diff --git a/app/views/open_api/v1/users/_user.json.jbuilder b/app/views/open_api/v1/users/_user.json.jbuilder new file mode 100644 index 0000000000..28e5ef69c8 --- /dev/null +++ b/app/views/open_api/v1/users/_user.json.jbuilder @@ -0,0 +1,15 @@ +json.extract! user, :id, :email, :created_at + +if user.association(:profile).loaded? + json.full_name user.profile.full_name +end + +if user.association(:group).loaded? + json.group do + if user.group_id? + json.extract! user.group, :id, :name, :slug + else + json.nil! + end + end +end diff --git a/app/views/open_api/v1/users/index.json.jbuilder b/app/views/open_api/v1/users/index.json.jbuilder new file mode 100644 index 0000000000..5eda45dbb3 --- /dev/null +++ b/app/views/open_api/v1/users/index.json.jbuilder @@ -0,0 +1,3 @@ +json.users @users do |user| + json.partial! 'open_api/v1/users/user', user: user +end diff --git a/config/routes.rb b/config/routes.rb index 02a99c20a2..30d4ce12cb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -77,7 +77,7 @@ end resources :invoices, only: [:index, :show, :create] do - get ':id/download', action: 'download', on: :collection + get 'download', action: 'download', on: :member end # for admin @@ -98,6 +98,26 @@ get 'translations/:locale/:state' => 'translations#show', :constraints => { :state => /[^\/]+/ } # allow dots in URL for 'state' end + # open_api + + namespace :open_api do + namespace :v1 do + scope only: :index do + resources :users + resources :trainings + resources :user_trainings + resources :reservations + resources :machines + resources :bookable_machines + resources :invoices do + get :download, on: :member + end + resources :events + resources :availabilities + end + end + end + %w(account event machine project subscription training user).each do |path| post "/stats/#{path}/_search", to: "api/statistics##{path}" end diff --git a/db/schema.rb b/db/schema.rb index 67ee62ce5c..54b8c4eb6f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -30,23 +30,23 @@ add_index "abuses", ["signaled_type", "signaled_id"], name: "index_abuses_on_signaled_type_and_signaled_id", using: :btree create_table "addresses", force: :cascade do |t| - t.string "address" - t.string "street_number" - t.string "route" - t.string "locality" - t.string "country" - t.string "postal_code" + t.string "address", limit: 255 + t.string "street_number", limit: 255 + t.string "route", limit: 255 + t.string "locality", limit: 255 + t.string "country", limit: 255 + t.string "postal_code", limit: 255 t.integer "placeable_id" - t.string "placeable_type" + t.string "placeable_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end create_table "assets", force: :cascade do |t| t.integer "viewable_id" - t.string "viewable_type" - t.string "attachment" - t.string "type" + t.string "viewable_type", limit: 255 + t.string "attachment", limit: 255 + t.string "type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -63,11 +63,11 @@ create_table "availabilities", force: :cascade do |t| t.datetime "start_at" t.datetime "end_at" - t.string "available_type" + t.string "available_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.integer "nb_total_places" - t.boolean "destroying", default: false + t.boolean "destroying", default: false end create_table "availability_tags", force: :cascade do |t| @@ -81,18 +81,18 @@ add_index "availability_tags", ["tag_id"], name: "index_availability_tags_on_tag_id", using: :btree create_table "categories", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.datetime "created_at" t.datetime "updated_at" end create_table "components", force: :cascade do |t| - t.string "name", null: false + t.string "name", limit: 255, null: false end create_table "credits", force: :cascade do |t| t.integer "creditable_id" - t.string "creditable_type" + t.string "creditable_type", limit: 255 t.integer "plan_id" t.integer "hours" t.datetime "created_at" @@ -113,7 +113,7 @@ end create_table "events", force: :cascade do |t| - t.string "title" + t.string "title", limit: 255 t.text "description" t.datetime "created_at" t.datetime "updated_at" @@ -139,10 +139,10 @@ add_index "events_categories", ["event_id"], name: "index_events_categories_on_event_id", using: :btree create_table "friendly_id_slugs", force: :cascade do |t| - t.string "slug", null: false - t.integer "sluggable_id", null: false + t.string "slug", limit: 255, null: false + t.integer "sluggable_id", null: false t.string "sluggable_type", limit: 50 - t.string "scope" + t.string "scope", limit: 255 t.datetime "created_at" end @@ -152,17 +152,17 @@ add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type", using: :btree create_table "groups", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug", limit: 255 end add_index "groups", ["slug"], name: "index_groups_on_slug", unique: true, using: :btree create_table "invoice_items", force: :cascade do |t| t.integer "invoice_id" - t.string "stp_invoice_item_id" + t.string "stp_invoice_item_id", limit: 255 t.integer "amount" t.datetime "created_at" t.datetime "updated_at" @@ -175,17 +175,17 @@ create_table "invoices", force: :cascade do |t| t.integer "invoiced_id" - t.string "invoiced_type" - t.string "stp_invoice_id" + t.string "invoiced_type", limit: 255 + t.string "stp_invoice_id", limit: 255 t.integer "total" t.datetime "created_at" t.datetime "updated_at" t.integer "user_id" - t.string "reference" - t.string "avoir_mode" + t.string "reference", limit: 255 + t.string "avoir_mode", limit: 255 t.datetime "avoir_date" t.integer "invoice_id" - t.string "type" + t.string "type", limit: 255 t.boolean "subscription_to_expire" t.text "description" end @@ -194,17 +194,17 @@ add_index "invoices", ["user_id"], name: "index_invoices_on_user_id", using: :btree create_table "licences", force: :cascade do |t| - t.string "name", null: false + t.string "name", limit: 255, null: false t.text "description" end create_table "machines", force: :cascade do |t| - t.string "name", null: false + t.string "name", limit: 255, null: false t.text "description" t.text "spec" t.datetime "created_at" t.datetime "updated_at" - t.string "slug" + t.string "slug", limit: 255 end add_index "machines", ["slug"], name: "index_machines_on_slug", unique: true, using: :btree @@ -220,14 +220,14 @@ create_table "notifications", force: :cascade do |t| t.integer "receiver_id" t.integer "attached_object_id" - t.string "attached_object_type" + t.string "attached_object_type", limit: 255 t.integer "notification_type_id" - t.boolean "is_read", default: false + t.boolean "is_read", default: false t.datetime "created_at" t.datetime "updated_at" t.string "receiver_type" - t.boolean "is_send", default: false - t.jsonb "meta_data", default: {} + t.boolean "is_send", default: false + t.jsonb "meta_data", default: {} end add_index "notifications", ["notification_type_id"], name: "index_notifications_on_notification_type_id", using: :btree @@ -286,20 +286,20 @@ end create_table "plans", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.integer "amount" - t.string "interval" + t.string "interval", limit: 255 t.integer "group_id" - t.string "stp_plan_id" + t.string "stp_plan_id", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.integer "training_credit_nb", default: 0 - t.boolean "is_rolling", default: true + t.integer "training_credit_nb", default: 0 + t.boolean "is_rolling", default: true t.text "description" t.string "type" t.string "base_name" - t.integer "ui_weight", default: 0 - t.integer "interval_count", default: 1 + t.integer "ui_weight", default: 0 + t.integer "interval_count", default: 1 end add_index "plans", ["group_id"], name: "index_plans_on_group_id", using: :btree @@ -320,11 +320,11 @@ create_table "profiles", force: :cascade do |t| t.integer "user_id" - t.string "first_name" - t.string "last_name" + t.string "first_name", limit: 255 + t.string "last_name", limit: 255 t.boolean "gender" t.date "birthday" - t.string "phone" + t.string "phone", limit: 255 t.text "interest" t.text "software_mastered" t.datetime "created_at" @@ -338,7 +338,7 @@ t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "title" + t.string "title", limit: 255 end add_index "project_steps", ["project_id"], name: "index_project_steps_on_project_id", using: :btree @@ -348,27 +348,27 @@ t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "is_valid", default: false - t.string "valid_token" + t.boolean "is_valid", default: false + t.string "valid_token", limit: 255 end add_index "project_users", ["project_id"], name: "index_project_users_on_project_id", using: :btree add_index "project_users", ["user_id"], name: "index_project_users_on_user_id", using: :btree create_table "projects", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.text "description" t.datetime "created_at" t.datetime "updated_at" t.integer "author_id" t.text "tags" t.integer "licence_id" - t.string "state" - t.string "slug" + t.string "state", limit: 255 + t.string "slug", limit: 255 t.datetime "published_at" end - add_index "projects", ["slug"], name: "index_projects_on_slug", unique: true, using: :btree + add_index "projects", ["slug"], name: "index_projects_on_slug", using: :btree create_table "projects_components", force: :cascade do |t| t.integer "project_id" @@ -400,20 +400,20 @@ t.datetime "created_at" t.datetime "updated_at" t.integer "reservable_id" - t.string "reservable_type" - t.string "stp_invoice_id" + t.string "reservable_type", limit: 255 + t.string "stp_invoice_id", limit: 255 t.integer "nb_reserve_places" t.integer "nb_reserve_reduced_places" end - add_index "reservations", ["reservable_type", "reservable_id"], name: "index_reservations_on_reservable_type_and_reservable_id", using: :btree + add_index "reservations", ["reservable_id", "reservable_type"], name: "index_reservations_on_reservable_id_and_reservable_type", using: :btree add_index "reservations", ["stp_invoice_id"], name: "index_reservations_on_stp_invoice_id", using: :btree add_index "reservations", ["user_id"], name: "index_reservations_on_user_id", using: :btree create_table "roles", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.integer "resource_id" - t.string "resource_type" + t.string "resource_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -448,18 +448,18 @@ create_table "statistic_fields", force: :cascade do |t| t.integer "statistic_index_id" - t.string "key" - t.string "label" + t.string "key", limit: 255 + t.string "label", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "data_type" + t.string "data_type", limit: 255 end add_index "statistic_fields", ["statistic_index_id"], name: "index_statistic_fields_on_statistic_index_id", using: :btree create_table "statistic_graphs", force: :cascade do |t| t.integer "statistic_index_id" - t.string "chart_type" + t.string "chart_type", limit: 255 t.integer "limit" t.datetime "created_at" t.datetime "updated_at" @@ -468,17 +468,17 @@ add_index "statistic_graphs", ["statistic_index_id"], name: "index_statistic_graphs_on_statistic_index_id", using: :btree create_table "statistic_indices", force: :cascade do |t| - t.string "es_type_key" - t.string "label" + t.string "es_type_key", limit: 255 + t.string "label", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.boolean "table", default: true - t.boolean "ca", default: true + t.boolean "table", default: true + t.boolean "ca", default: true end create_table "statistic_sub_types", force: :cascade do |t| - t.string "key" - t.string "label" + t.string "key", limit: 255 + t.string "label", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -495,8 +495,8 @@ create_table "statistic_types", force: :cascade do |t| t.integer "statistic_index_id" - t.string "key" - t.string "label" + t.string "key", limit: 255 + t.string "label", limit: 255 t.boolean "graph" t.datetime "created_at" t.datetime "updated_at" @@ -514,7 +514,7 @@ create_table "subscriptions", force: :cascade do |t| t.integer "plan_id" t.integer "user_id" - t.string "stp_subscription_id" + t.string "stp_subscription_id", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.datetime "expired_at" @@ -533,15 +533,15 @@ add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "themes", force: :cascade do |t| - t.string "name", null: false + t.string "name", limit: 255, null: false end create_table "trainings", force: :cascade do |t| - t.string "name" + t.string "name", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.integer "nb_total_places" - t.string "slug" + t.string "slug", limit: 255 t.text "description" end @@ -597,32 +597,32 @@ add_index "user_trainings", ["user_id"], name: "index_user_trainings_on_user_id", using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + t.string "email", limit: 255, default: "", null: false + t.string "encrypted_password", limit: 255, default: "", null: false + t.string "reset_password_token", limit: 255 t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.string "confirmation_token" + t.string "current_sign_in_ip", limit: 255 + t.string "last_sign_in_ip", limit: 255 + t.string "confirmation_token", limit: 255 t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email" - t.integer "failed_attempts", default: 0, null: false - t.string "unlock_token" + t.string "unconfirmed_email", limit: 255 + t.integer "failed_attempts", default: 0, null: false + t.string "unlock_token", limit: 255 t.datetime "locked_at" t.datetime "created_at" t.datetime "updated_at" - t.boolean "is_allow_contact", default: true + t.boolean "is_allow_contact", default: true t.integer "group_id" - t.string "stp_customer_id" - t.string "username" - t.string "slug" - t.boolean "is_active", default: true - t.boolean "invoicing_disabled", default: false + t.string "stp_customer_id", limit: 255 + t.string "username", limit: 255 + t.string "slug", limit: 255 + t.boolean "is_active", default: true + t.boolean "invoicing_disabled", default: false t.string "provider" t.string "uid" t.string "auth_token" diff --git a/lib/tasks/fablab/fix.rake b/lib/tasks/fablab/fix.rake new file mode 100644 index 0000000000..59e3d95cd8 --- /dev/null +++ b/lib/tasks/fablab/fix.rake @@ -0,0 +1,12 @@ +namespace :fablab do + namespace :fix do + task reservations_not_existing_reservable: :environment do + ActiveRecord::Base.logger = Logger.new(STDOUT) + ActiveRecord::Base.connection.execute( + 'UPDATE reservations SET reservable_type = NULL, reservable_id = NULL'\ + ' WHERE NOT EXISTS (SELECT 1 FROM events WHERE events.id = reservations.reservable_id)'\ + ' AND reservations.reservable_type = \'Event\'' + ) + end + end +end From e349adf252f841bb1aeeb052c5c1f90f506469e7 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Thu, 5 May 2016 15:02:02 +0200 Subject: [PATCH 04/30] adds open_api's documentation --- Gemfile | 1 + Gemfile.lock | 3 + .../v1/bookable_machines_controller.rb | 3 + .../open_api/v1/events_controller.rb | 3 + .../open_api/v1/invoices_controller.rb | 3 + .../open_api/v1/machines_controller.rb | 3 + .../open_api/v1/reservations_controller.rb | 3 + .../open_api/v1/trainings_controller.rb | 3 + .../open_api/v1/user_trainings_controller.rb | 3 + .../open_api/v1/users_controller.rb | 6 +- app/doc/open_api/api_doc.rb | 60 +++++++++++ app/doc/open_api/application_doc.rb | 92 +++++++++++++++++ app/doc/open_api/v1/base_doc.rb | 5 + app/doc/open_api/v1/bookable_machines_doc.rb | 72 ++++++++++++++ app/doc/open_api/v1/concerns/param_groups.rb | 30 ++++++ app/doc/open_api/v1/events_doc.rb | 45 +++++++++ app/doc/open_api/v1/invoices_doc.rb | 78 +++++++++++++++ app/doc/open_api/v1/machines_doc.rb | 77 +++++++++++++++ app/doc/open_api/v1/reservations_doc.rb | 90 +++++++++++++++++ app/doc/open_api/v1/trainings_doc.rb | 74 ++++++++++++++ app/doc/open_api/v1/user_trainings_doc.rb | 99 +++++++++++++++++++ app/doc/open_api/v1/users_doc.rb | 97 ++++++++++++++++++ config/application.rb | 1 - config/initializers/apipie.rb | 19 ++++ config/routes.rb | 1 + 25 files changed, 869 insertions(+), 2 deletions(-) create mode 100644 app/doc/open_api/api_doc.rb create mode 100644 app/doc/open_api/application_doc.rb create mode 100644 app/doc/open_api/v1/base_doc.rb create mode 100644 app/doc/open_api/v1/bookable_machines_doc.rb create mode 100644 app/doc/open_api/v1/concerns/param_groups.rb create mode 100644 app/doc/open_api/v1/events_doc.rb create mode 100644 app/doc/open_api/v1/invoices_doc.rb create mode 100644 app/doc/open_api/v1/machines_doc.rb create mode 100644 app/doc/open_api/v1/reservations_doc.rb create mode 100644 app/doc/open_api/v1/trainings_doc.rb create mode 100644 app/doc/open_api/v1/user_trainings_doc.rb create mode 100644 app/doc/open_api/v1/users_doc.rb create mode 100644 config/initializers/apipie.rb diff --git a/Gemfile b/Gemfile index 89209a76e7..0b20d6a609 100644 --- a/Gemfile +++ b/Gemfile @@ -142,3 +142,4 @@ gem 'openlab_ruby' gem 'api-pagination' gem 'has_secure_token' +gem 'apipie-rails' diff --git a/Gemfile.lock b/Gemfile.lock index dc25cdef4e..b4fe5bbf2a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,6 +43,8 @@ GEM addressable (2.3.8) ansi (1.5.0) api-pagination (4.3.0) + apipie-rails (0.3.6) + json arel (6.0.3) autoprefixer-rails (5.1.8) execjs @@ -434,6 +436,7 @@ DEPENDENCIES actionpack-page_caching active_record_query_trace api-pagination + apipie-rails awesome_print bootstrap-sass byebug diff --git a/app/controllers/open_api/v1/bookable_machines_controller.rb b/app/controllers/open_api/v1/bookable_machines_controller.rb index 4909d3a1d3..bbe6af19cf 100644 --- a/app/controllers/open_api/v1/bookable_machines_controller.rb +++ b/app/controllers/open_api/v1/bookable_machines_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::BookableMachinesController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index raise ActionController::ParameterMissing if params[:user_id].blank? diff --git a/app/controllers/open_api/v1/events_controller.rb b/app/controllers/open_api/v1/events_controller.rb index a1eac367f6..2dcba1e782 100644 --- a/app/controllers/open_api/v1/events_controller.rb +++ b/app/controllers/open_api/v1/events_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::EventsController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @events = Event.order(created_at: :desc) diff --git a/app/controllers/open_api/v1/invoices_controller.rb b/app/controllers/open_api/v1/invoices_controller.rb index 0b02702481..3dd1e9b15c 100644 --- a/app/controllers/open_api/v1/invoices_controller.rb +++ b/app/controllers/open_api/v1/invoices_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::InvoicesController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @invoices = Invoice.order(created_at: :desc) diff --git a/app/controllers/open_api/v1/machines_controller.rb b/app/controllers/open_api/v1/machines_controller.rb index be55c8d26a..c998afffe8 100644 --- a/app/controllers/open_api/v1/machines_controller.rb +++ b/app/controllers/open_api/v1/machines_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::MachinesController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @machines = Machine.order(:created_at) end diff --git a/app/controllers/open_api/v1/reservations_controller.rb b/app/controllers/open_api/v1/reservations_controller.rb index 82935eda87..b1218d9927 100644 --- a/app/controllers/open_api/v1/reservations_controller.rb +++ b/app/controllers/open_api/v1/reservations_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::ReservationsController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @reservations = Reservation.order(created_at: :desc) diff --git a/app/controllers/open_api/v1/trainings_controller.rb b/app/controllers/open_api/v1/trainings_controller.rb index d0d1cba356..ded339d632 100644 --- a/app/controllers/open_api/v1/trainings_controller.rb +++ b/app/controllers/open_api/v1/trainings_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::TrainingsController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @trainings = Training.order(:created_at) end diff --git a/app/controllers/open_api/v1/user_trainings_controller.rb b/app/controllers/open_api/v1/user_trainings_controller.rb index 9fc4e7d1fb..a8a9783914 100644 --- a/app/controllers/open_api/v1/user_trainings_controller.rb +++ b/app/controllers/open_api/v1/user_trainings_controller.rb @@ -1,4 +1,7 @@ class OpenAPI::V1::UserTrainingsController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @user_trainings = UserTraining.order(created_at: :desc) diff --git a/app/controllers/open_api/v1/users_controller.rb b/app/controllers/open_api/v1/users_controller.rb index d3767e808b..46a9bf4331 100644 --- a/app/controllers/open_api/v1/users_controller.rb +++ b/app/controllers/open_api/v1/users_controller.rb @@ -1,9 +1,13 @@ class OpenAPI::V1::UsersController < OpenAPI::V1::BaseController + extend OpenAPI::ApiDoc + expose_doc + def index @users = User.order(created_at: :desc).includes(:group, :profile) if params[:email].present? - @users = @users.where(email: params[:email].downcase) + email_param = params[:email].is_a?(String) ? params[:email].downcase : params[:email].map(&:downcase) + @users = @users.where(email: email_param) end if params[:user_id].present? diff --git a/app/doc/open_api/api_doc.rb b/app/doc/open_api/api_doc.rb new file mode 100644 index 0000000000..e984db5473 --- /dev/null +++ b/app/doc/open_api/api_doc.rb @@ -0,0 +1,60 @@ +# app/concerns/controllers/api_doc.rb +# +# Controller extension with common API documentation shortcuts +# + +module OpenAPI::ApiDoc + # Apipie doesn't allow to append anything to esisting + # description. It raises an error on double definition. + # + def append_desc(desc = "") + _apipie_dsl_data[:description] << desc << "\n" + end + + # Converts passed +code+ to the markdown + # by prepending 4 spaces to each line + # + # @param code [String] + # @return [String] + # + def to_markdown_code(code) + code.split("\n").map do |line| + (" " * 4) + line + end.join("\n") + end + + # Includes passed list of json schemas + # to method description + # + # @example + # include_response_schema 'users.json', '_user.json' + # + # @param schemas [Array] + # + def include_response_schema(*schemas) + root = Rails.root.join('app/doc/responses') + _apipie_dsl_data[:description] = _apipie_dsl_data[:description].strip_heredoc + append_desc("## Response schema") + + schemas.each do |relative_path| + append_desc MarkdownJsonSchema.read(relative_path) + end + end + + # Exports all documentation from provided class + # + # @example + # class ProfilesController < ApplicationController + # extend Controllers::ApiDoc + # expose_doc + # # exports all docs from ProfilesDoc class + # # that must be inherired from ApplicationDoc + # end + # + # @see ApplicationDoc + # + def expose_doc(doc_name = "#{controller_path}_doc") + doc_klass = doc_name.classify.constantize + doc_klass.apply(self) + end +end diff --git a/app/doc/open_api/application_doc.rb b/app/doc/open_api/application_doc.rb new file mode 100644 index 0000000000..e677037e5e --- /dev/null +++ b/app/doc/open_api/application_doc.rb @@ -0,0 +1,92 @@ +# app/docs/application_doc.rb +# +# A common class for defining API docs +# +# This class is abstract, to define your own doc +# for controller Api::ResourcesController, create a class +# +# class Api::ResourcesDoc < ApplicationDoc +# resource_description do +# # any method from Apipie +# end +# +# doc_for :action_name do +# # documentation for Api::ResourcesController#action_name +# # using Apipie methods +# end +# end +# + +class OpenAPI::ApplicationDoc + extend OpenAPI::ApiDoc + + class << self + # Stores provided resource description + # to include it later to the controller class + # + # @param block [Proc] + # + def resource_description(&block) + @_resource_description_block = block + end + + # Returns stored resource description (or empty proc) + # + # @return [Proc] + # + def resource_description_block + @_resource_description_block || proc {} + end + + # Defines documentation for provided +action_name+ + # + # @param action_name [#to_s] should match controller action name + # @param block [Proc] documentation for +action_name+ action + # + def doc_for(action_name, &block) + docs[action_name] = block + end + + # Returns mappign action_name => documentation + # + # @return [Hash] + # + def docs + @_docs ||= {} + end + + def define_param_group(param_group_name, &block) + param_groups[param_group_name] = block + end + + def param_groups + @_param_groups ||= {} + end + + # Applies all defined DSL to provided controller class + # + # @param controller [ActionController::Base] + # + def apply(controller) + resource_description_block = self.resource_description_block + docs = self.docs + param_groups = self.param_groups + + controller.class_eval do + resource_description(&resource_description_block) + + param_groups.each do |param_group_name, block| + instance_eval do + def_param_group param_group_name, &block + end + end + + docs.each do |action_name, block| + instance_eval(&block) + + define_method(action_name) {} + end + end + end + end +end diff --git a/app/doc/open_api/v1/base_doc.rb b/app/doc/open_api/v1/base_doc.rb new file mode 100644 index 0000000000..1772d0b5d0 --- /dev/null +++ b/app/doc/open_api/v1/base_doc.rb @@ -0,0 +1,5 @@ +class OpenAPI::V1::BaseDoc < OpenAPI::ApplicationDoc + API_VERSION = "v1" + FORMATS = ['json'] + PER_PAGE_DEFAULT = 20 +end diff --git a/app/doc/open_api/v1/bookable_machines_doc.rb b/app/doc/open_api/v1/bookable_machines_doc.rb new file mode 100644 index 0000000000..f47ae11cdc --- /dev/null +++ b/app/doc/open_api/v1/bookable_machines_doc.rb @@ -0,0 +1,72 @@ +class OpenAPI::V1::BookableMachinesDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Bookable machines' + desc 'Machines that a given user is allowed to book (allowed to make a reservation)' + formats FORMATS + api_version API_VERSION + end + + doc_for :index do + api :GET, "/#{API_VERSION}/bookable_machines", "Bookable machines index" + description "Machines that a given user is allowed to book." + param :user_id, Integer, required: true, desc: "Id of the given user." + example <<-EOS + # /open_api/v1/bookable_machines?user_id=522 + { + "machines": [ + { + "id": 3, + "name": "Shopbot / Grande fraiseuse", + "slug": "shopbot-grande-fraiseuse", + "updated_at": "2014-08-19T11:01:12.919+02:00", + "created_at": "2014-06-30T03:32:31.982+02:00", + "description": "La fraiseuse numériq ... ", + "spec": "Surface maximale de travail: 244 ... " + "hours_remaining": 0 + }, + { + "id": 5, + "name": "Petite Fraiseuse", + "slug": "petite-fraiseuse", + "updated_at": "2014-06-30T14:33:37.638+02:00", + "created_at": "2014-06-30T03:32:31.989+02:00", + "description": "La fraiseuse numérique Roland Modela MDX-20 ... ", + "spec": "Taille du plateau X/Y : 220 mm x 1 ... " + "hours_remaining": 0 + }, + { + "id": 2, + "name": "Découpeuse vinyle", + "slug": "decoupeuse-vinyle", + "updated_at": "2014-06-30T15:10:14.272+02:00", + "created_at": "2014-06-30T03:32:31.977+02:00", + "description": "La découpeuse Vinyle, Roland CAMM ...", + "spec": "Largeurs de support acceptées: de 50 mm à 70 ... 50 cm/sec ... mécanique: 0,0125 mm/pas\r\n", + "hours_remaining": 0 + }, + { + "id": 1, + "name": "Epilog EXT36 Laser", + "slug": "decoupeuse-laser", + "updated_at": "2015-02-17T11:06:00.495+01:00", + "created_at": "2014-06-30T03:32:31.972+02:00", + "description": "La découpeuse Laser, ... ", + "spec": "Puissance : 40W Surface de trav ... ", + "hours_remaining": 0 + }, + { + "id": 4, + "name": "Imprimante 3D - Ultimaker", + "slug": "imprimante-3d", + "updated_at": "2014-12-11T15:47:02.215+01:00", + "created_at": "2014-06-30T03:32:31.986+02:00", + "description": "L'imprimante 3D U ... ", + "spec": "Surface maximale de travai sés: PLA (en stock).", + "hours_remaining": 10 + }, + # ... + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/concerns/param_groups.rb b/app/doc/open_api/v1/concerns/param_groups.rb new file mode 100644 index 0000000000..d43efda6cc --- /dev/null +++ b/app/doc/open_api/v1/concerns/param_groups.rb @@ -0,0 +1,30 @@ +module OpenAPI::V1::Concerns::ParamGroups + extend ActiveSupport::Concern + + included do + define_param_group :pagination do + param :page, Integer, desc: "Page number", optional: true + param :per_page, Integer, desc: "Number of objects per page. Default is #{OpenAPI::V1::BaseDoc::PER_PAGE_DEFAULT}.", optional: true + end + + # define_param_group :order_type do + # param :order_type, ['asc', 'desc'], desc: "order type: descendant or ascendant. Default value is *desc*." + # end + # + # define_param_group :filter_by_tags do + # param :tagged_with, [String, Array], desc: 'If multiple tags are given, we use an *OR* function. See parameter *order_by_matching_tag_count* to order the result. It can also be a *comma* *separated* *string*. Example: tagged_with=science,museum' + # param :order_by_matching_tag_count, ['t',1,'true'], desc: "You can use this parameter if you are sending a parameter *tagged_with*. Send this parameter to order by number of matching tags (descendant): result will be sort firstly by matching tags and secondly by order given by *order_by* parameter. Default to *false*." + # end + # + # define_param_group :filter_by_blog do + # param :blog_slug, String, desc: "Send the blog's *slug* to only return articles belonging to specific blog." + # end + # + # define_param_group :filter_by_geolocation do + # param :latitude, Numeric, desc: "Latitude. Example: *45.166670*" + # param :longitude, Numeric, desc: "Longitude. Example: *5.7166700*" + # param :radius, Numeric, desc: "To be combined with parameters latitude and longitude. Default to *10*." + # param :order_by_distance, ['t',1,'true'], desc: "You can use this parameter if you are sending parameters *latitude* and *longitude*. Send this parameter to order by distance (descendant): result will be sort firstly by distance and secondly by order given by *order_by* parameter. Default to *false*." + # end + end +end diff --git a/app/doc/open_api/v1/events_doc.rb b/app/doc/open_api/v1/events_doc.rb new file mode 100644 index 0000000000..e7d1a640e8 --- /dev/null +++ b/app/doc/open_api/v1/events_doc.rb @@ -0,0 +1,45 @@ +class OpenAPI::V1::EventsDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Events' + desc 'Events of Fab-manager' + formats FORMATS + api_version API_VERSION + end + + include OpenAPI::V1::Concerns::ParamGroups + + doc_for :index do + api :GET, "/#{API_VERSION}/events", "Events index" + param_group :pagination + description "Events index. Order by *created_at* desc." + example <<-EOS + # /open_api/v1/events?page=1&per_page=2 + { + "events": [ + { + "id": 183, + "title": "OPEN LAB", + "description": "Que vous soyez Fab user, visiteur, curieux ou bricoleur, l’atelier de fabrication numérique vous ouvre ses portes les mercredis soirs pour avancer vos projets ou rencontrer la «communauté» Fab Lab. \r\n\r\nCe soir, venez spécialement découvrir les machines à commandes numérique du Fab Lab de La Casemate, venez comprendre ce lieux ouvert à tous. \r\n\r\n\r\nVenez découvrir un concept, une organisation, des machines, pour stimuler votre sens de la créativité.", + "updated_at": "2016-04-25T10:49:40.055+02:00", + "created_at": "2016-04-25T10:49:40.055+02:00", + "amount": 0, + "reduced_amount": 0, + "nb_total_places": 18, + "nb_free_places": 16 + }, + { + "id": 182, + "title": "ATELIER SKATE : SEANCE 1", + "description": "Envie de rider à travers Grenoble sur une planche unique ? Envie de découvrir la fabrication éco-responsable d'un skate ? Alors bienvenue à l'atelier Skate Board du Fablab ! Encadré par Ivan Mago et l'équipe du FabLab, vous réaliserez votre planche (skate, longboard,...) depuis son design jusqu'à sa décoration sur 4 séances.\r\n\r\nLe tarif 50€ inclut la participation aux ateliers, l'utilisations des machines, et tout le matériel de fabrication (bois+colle+grip+vinyle).\r\n\r\nCette première séance sera consacré au design de votre planche et à la découpe des gabarits. N'hésitez pas à venir avec votre ordinateur et vos logiciels de création 2D si vous le souhaitez.\r\n\r\nNous vous attendons nombreux !", + "updated_at": "2016-04-11T17:40:15.146+02:00", + "created_at": "2016-04-11T17:40:15.146+02:00", + "amount": 5000, + "reduced_amount": null, + "nb_total_places": 8, + "nb_free_places": 0 + } + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/invoices_doc.rb b/app/doc/open_api/v1/invoices_doc.rb new file mode 100644 index 0000000000..abc3605966 --- /dev/null +++ b/app/doc/open_api/v1/invoices_doc.rb @@ -0,0 +1,78 @@ +class OpenAPI::V1::InvoicesDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Invoices' + desc 'Invoices' + formats FORMATS + api_version API_VERSION + end + + include OpenAPI::V1::Concerns::ParamGroups + + doc_for :index do + api :GET, "/#{API_VERSION}/invoices", "Invoices index" + description "Index of users' invoices, with optional pagination. Order by *created_at* descendant." + param_group :pagination + param :user_id, [Integer, Array], optional: true, desc: "Scope the request to one or various users." + example <<-EOS + # /open_api/v1/invoices?user_id=211&page=1&per_page=3 + { + "invoices": [ + { + "id": 2809, + "invoiced_id": 3257, + "user_id": 211, + "invoiced_type": "Reservation", + "stp_invoice_id": "in_187DLE4zBvgjueAZ6L7SyQlU", + "reference": "1605017/VL", + "total": 1000, + "type": null, + "description": null, + "invoice_url": "/open_api/v1/invoices/2809/download", + "invoiced": { + "created_at": "2016-05-04T01:54:16.686+02:00" + } + }, + { + "id": 2783, + "invoiced_id": 3229, + "user_id": 211, + "invoiced_type": "Reservation", + "stp_invoice_id": "in_185Hmt4zBvgjueAZl5lio1pK", + "reference": "1604176/VL", + "total": 2000, + "type": null, + "description": null, + "invoice_url": "/open_api/v1/invoices/2783/download", + "invoiced": { + "created_at": "2016-04-28T18:14:52.524+02:00" + } + }, + { + "id": 2773, + "invoiced_id": 3218, + "user_id": 211, + "invoiced_type": "Reservation", + "stp_invoice_id": "in_184oNK4zBvgjueAZJdOxHJjT", + "reference": "1604166/VL", + "total": 2000, + "type": null, + "description": null, + "invoice_url": "/open_api/v1/invoices/2773/download", + "invoiced": { + "created_at": "2016-04-27T10:50:30.806+02:00" + } + } + ] + } + EOS + end + + doc_for :download do + api :GET, "/#{API_VERSION}/invoices/:id/download", "Download an invoice" + param :id, Integer, desc: "Invoice id", required: true + + example <<-EOS + # /open_api/v1/invoices/2809/download + EOS + end +end diff --git a/app/doc/open_api/v1/machines_doc.rb b/app/doc/open_api/v1/machines_doc.rb new file mode 100644 index 0000000000..461d3b1c3a --- /dev/null +++ b/app/doc/open_api/v1/machines_doc.rb @@ -0,0 +1,77 @@ +class OpenAPI::V1::MachinesDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Machines' + desc 'Machines of Fab-manager' + formats FORMATS + api_version API_VERSION + end + + doc_for :index do + api :GET, "/#{API_VERSION}/machines", "Machines index" + description "Machines index. Order by *created_at* ascendant." + example <<-EOS + # /open_api/v1/machines + { + "machines": [ + { + "id": 1, + "name": "Epilog EXT36 Laser", + "slug": "decoupeuse-laser", + "updated_at": "2015-02-17T11:06:00.495+01:00", + "created_at": "2014-06-30T03:32:31.972+02:00", + "description": "La découpeuse Laser, EPILOG Legend 36EXT\r\n\r\nInformations générales :\r\nLa découpeuse laser vous permet de découper ou graver des matériaux. \r\n\r\nPour la découpe, il suffit d'apporter votre fichier vectorisé type illustrator, svg ou dxf avec des \"lignes de coupe\" d'une épaisseur inférieure à 0,01 mm et la machine s'occupera du reste!\r\n\r\nLa gravure est basée sur le spectre noir et blanc. Les nuances sont obtenues par différentes profondeurs de gravure correspondant aux niveaux de gris de votre image. Il suffit pour cela d'apporter une image scannée ou un fichier photo en noir et blanc pour pouvoir reproduire celle-ci sur votre support.\r\n\r\nTypes de matériaux gravables/découpeables ?\r\nDu bois au tissu, du plexiglass au cuir, cette machine permet de découper et graver la plupart des matériaux sauf les métaux. La gravure est néanmoins possible sur les métaux recouverts d'une couche de peinture ou les aluminiums anodisés. \r\nConcernant l'épaisseur des matériaux découpés, il est préférable de ne pas dépasser 5 mm pour le bois et 6 mm pour le plexiglass.\r\n", + "spec": "Puissance : 40W\r\nSurface de travail : 914x609 mm \r\nEpaisseur maximale de la matière : 305mm\r\nSource laser : tube laser type CO2\r\nContrôles de vitesse et de puissance : ces deux paramètres sont ajustables en fonction du matériau (de 1% à 100%) .\r\n" + }, + { + "id": 2, + "name": "Découpeuse vinyle", + "slug": "decoupeuse-vinyle", + "updated_at": "2014-06-30T15:10:14.272+02:00", + "created_at": "2014-06-30T03:32:31.977+02:00", + "description": "La découpeuse Vinyle, Roland CAMM-1 GX24\r\n\r\nInformations générales :\r\nEnvie de réaliser un tee shirt personnalisé ? Un sticker à l'effigie votre groupe préféré? Un masque pour la réalisation d'un circuit imprimé? Pour cela, il suffit simplement de venir avec votre fichier vectorisé (ne pas oublier de vectoriser les textes) type illustrator svg ou dxf.\r\n \r\nMatériaux utilisés :\r\nCette machine permet de découper principalement : vinyle, vinyle réfléchissant et flex.\r\n", + "spec": "Largeurs de support acceptées: de 50 mm à 700 mm\r\nVitesse de découpe: 50 cm/sec\r\nRésolution mécanique: 0,0125 mm/pas\r\n" + }, + { + "id": 3, + "name": "Shopbot / Grande fraiseuse", + "slug": "shopbot-grande-fraiseuse", + "updated_at": "2014-08-19T11:01:12.919+02:00", + "created_at": "2014-06-30T03:32:31.982+02:00", + "description": "La fraiseuse numérique ShopBot PRS standard\r\n\r\nInformations générales :\r\nCette machine est une fraiseuse 3 axes, idéale pour l'usinage de pièces de grandes dimensions. De la réalisation d'une chaise ou d'un meuble à la construction d'une maison ou d'un assemblage immense, le ShopBot ouvre de nombreuses portes à votre imagination ! \r\n\r\nMatériaux usinables :\r\nLes principaux matériaux usinables sont le bois, le plastique, le laiton et bien d'autres.\r\nCette machine n'usine pas les métaux.\r\n", + "spec": "Surface maximale de travail: 2440x1220x150 (Z) mm\r\nLogiciel utilisé: Partworks 2D & 3D\r\nRésolution mécanique: 0,015 mm\r\nPrécision de la position: +/- 0,127mm\r\nFormats acceptés: DXF, STL \r\n" + }, + { + "id": 4, + "name": "Imprimante 3D - Ultimaker", + "slug": "imprimante-3d", + "updated_at": "2014-12-11T15:47:02.215+01:00", + "created_at": "2014-06-30T03:32:31.986+02:00", + "description": "L'imprimante 3D ULTIMAKER\r\n\r\nInformations générales :\r\nL'utimaker est une imprimante 3D peu chère utilisant une technologie FFF (Fused Filament Fabrication) avec extrusion thermoplastique.\r\nC'est une machine idéale pour réaliser rapidement des prototypes 3D dans des couleurs différentes.\r\n", + "spec": "Surface maximale de travail: 210x210x220mm \r\nRésolution méchanique: 0,02 mm \r\nPrécision de position: +/- 0,05 \r\nLogiciel utilisé: Cura\r\nFormats de fichier acceptés: STL \r\nMatériaux utilisés: PLA (en stock)." + }, + { + "id": 5, + "name": "Petite Fraiseuse", + "slug": "petite-fraiseuse", + "updated_at": "2014-06-30T14:33:37.638+02:00", + "created_at": "2014-06-30T03:32:31.989+02:00", + "description": "La fraiseuse numérique Roland Modela MDX-20\r\n\r\nInformations générales :\r\nCette machine est utilisée pour l'usinage et le scannage 3D de précision. Elle permet principalement d'usiner des circuits imprimés et des moules de petite taille. Le faible diamètre des fraises utilisées (Ø 0,3 mm à Ø 6mm) implique que certains temps d'usinages peuvent êtres long (> 12h), c'est pourquoi cette fraiseuse peut être laissée en autonomie toute une nuit afin d'obtenir le plus précis des usinages au FabLab.\r\n\r\nMatériaux usinables :\r\nLes principaux matériaux usinables sont : bois, plâtre, résine, cire usinable, cuivre.\r\n", + "spec": "Taille du plateau X/Y : 220 mm x 160 mm\r\nVolume maximal de travail: 203,2 mm (X), 152,4 mm (Y), 60,5 mm (Z)\r\nPrécision usinage: 0,00625 mm\r\nPrécision scannage: réglable de 0,05 à 5 mm (axes X,Y) et 0,025 mm (axe Z)\r\nVitesse d'analyse (scannage): 4-15 mm/sec\r\n \r\n \r\nLogiciel utilisé pour le fraisage: Roland Modela player 4 \r\nLogiciel utilisé pour l'usinage de circuits imprimés: Cad.py (linux)\r\nFormats acceptés: STL,PNG 3D\r\nFormat d'exportation des données scannées: DXF, VRML, STL, 3DMF, IGES, Grayscale, Point Group et BMP\r\n" + }, + # + # .... + # + { + "id": 18, + "name": "Canon IPF 750", + "slug": "canon-ipf-750", + "updated_at": "2015-10-12T18:00:24.254+02:00", + "created_at": "2015-10-12T18:00:24.254+02:00", + "description": "PROCHAINEMENT", + "spec": "36 pouces\r\nType d'encre: Encre pigment et colorant réactive, 5 couleurs (MBK x 2, BK, C, M, Y)\r\nRésolution d'impression maximale:\t2400 × 1200 dpi\r\nVitesse d'impression:\t(A0, Image polychrome)\r\nPapier ordinaire: 0:48 min (mode brouillon), 1:14 min (mode standard)\r\nPapier couché: 1:14 min (mode brouillon), 2:26 min (mode standard), 3:51 min (mode qualité élevée)" + } + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/reservations_doc.rb b/app/doc/open_api/v1/reservations_doc.rb new file mode 100644 index 0000000000..279e700624 --- /dev/null +++ b/app/doc/open_api/v1/reservations_doc.rb @@ -0,0 +1,90 @@ +class OpenAPI::V1::ReservationsDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Reservations' + desc 'Reservations made by users' + formats FORMATS + api_version API_VERSION + end + + include OpenAPI::V1::Concerns::ParamGroups + + 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." + param_group :pagination + param :user_id, [Integer, Array], optional: true, desc: "Scope the request to one or various users." + param :reservable_type, ['Event', 'Machine', 'Training'], optional: true, desc: "Scope the request to a specific type of reservable." + param :reservable_id, [Integer, Array], optional: true, desc: "Scope the request to one or various reservables." + + example <<-EOS + # /open_api/v1/reservations?reservable_type=Event&page=1&per_page=3 + { + "reservations": [ + { + "id": 3253, + "user_id": 1744, + "reservable_id": 162, + "reservable_type": "Event", + "updated_at": "2016-05-03T14:14:00.141+02:00", + "created_at": "2016-05-03T14:14:00.141+02:00", + "user": { + "id": 1744, + "email": "xxxxxxxxxxxx", + "created_at": "2016-05-03T13:51:03.223+02:00", + "full_name": "xxxxxxxxxxxx" + }, + "reservable": { + "id": 162, + "title": "INITIATION FAB LAB", + "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" + } + }, + { + "id": 3252, + "user_id": 1514, + "reservable_id": 137, + "reservable_type": "Event", + "updated_at": "2016-05-03T13:54:54.072+02:00", + "created_at": "2016-05-03T13:54:54.072+02:00", + "user": { + "id": 1514, + "email": "xxxxxxxxxxxx", + "created_at": "2016-02-24T08:45:09.050+01:00", + "full_name": "xxxxxxxxxxxx" + }, + "reservable": { + "id": 137, + "title": "INITIATION FAB LAB", + "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" + } + }, + { + "id": 3251, + "user_id": 1743, + "reservable_id": 162, + "reservable_type": "Event", + "updated_at": "2016-05-03T12:28:50.487+02:00", + "created_at": "2016-05-03T12:28:50.487+02:00", + "user": { + "id": 1743, + "email": "xxxxxxxxxxxx", + "created_at": "2016-05-03T12:24:38.724+02:00", + "full_name": "xxxxxxxxxxxx" + }, + "reservable": { + "id": 162, + "title": "INITIATION FAB LAB", + "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" + } + } + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/trainings_doc.rb b/app/doc/open_api/v1/trainings_doc.rb new file mode 100644 index 0000000000..8a167fe556 --- /dev/null +++ b/app/doc/open_api/v1/trainings_doc.rb @@ -0,0 +1,74 @@ +class OpenAPI::V1::TrainingsDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Trainings' + desc 'Trainings of Fab-manager' + formats FORMATS + api_version API_VERSION + end + + doc_for :index do + api :GET, "/#{API_VERSION}/trainings", "Trainings index" + description "Trainings index. Order by *created_at* ascendant." + example <<-EOS + # /open_api/v1/trainings + { + "trainings": [ + { + "id": 1, + "name": "Formation Imprimante 3D", + "slug": "formation-imprimante-3d", + "updated_at": "2015-02-05T13:49:15.025+01:00", + "created_at": "2014-06-30T03:32:32.126+02:00", + "nb_total_places": 8, + "description": null + }, + { + "id": 2, + "name": "Formation Laser / Vinyle", + "slug": "formation-laser-vinyle", + "updated_at": "2015-02-05T13:49:19.046+01:00", + "created_at": "2014-06-30T03:32:32.138+02:00", + "nb_total_places": 8, + "description": null + }, + { + "id": 3, + "name": "Formation Petite fraiseuse numerique", + "slug": "formation-petite-fraiseuse-numerique", + "updated_at": "2015-02-05T13:49:23.040+01:00", + "created_at": "2014-06-30T03:32:32.164+02:00", + "nb_total_places": 8, + "description": null + }, + { + "id": 4, + "name": "Formation Shopbot Grande Fraiseuse", + "slug": "formation-shopbot-grande-fraiseuse", + "updated_at": "2015-02-03T10:22:21.908+01:00", + "created_at": "2014-06-30T03:32:32.168+02:00", + "nb_total_places": 6, + "description": null + }, + { + "id": 5, + "name": "Formation logiciel 2D", + "slug": "formation-logiciel-2d", + "updated_at": "2015-02-05T13:49:27.460+01:00", + "created_at": "2014-06-30T09:37:42.778+02:00", + "nb_total_places": 8, + "description": null + }, + { + "id": 6, + "name": "Pas de Reservation", + "slug": "pas-de-reservation", + "updated_at": "2014-07-22T14:18:11.784+02:00", + "created_at": "2014-07-22T14:18:11.784+02:00", + "nb_total_places": null, + "description": null + } + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/user_trainings_doc.rb b/app/doc/open_api/v1/user_trainings_doc.rb new file mode 100644 index 0000000000..232505abb0 --- /dev/null +++ b/app/doc/open_api/v1/user_trainings_doc.rb @@ -0,0 +1,99 @@ +class OpenAPI::V1::UserTrainingsDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'User trainings' + desc 'Trainings validated by users' + formats FORMATS + api_version API_VERSION + end + + include OpenAPI::V1::Concerns::ParamGroups + + doc_for :index do + api :GET, "/#{API_VERSION}/user_trainings", "User trainings index" + description "Index of trainings accomplished by users, with optional pagination. Order by *created_at* descendant." + param_group :pagination + param :training_id, [Integer, Array], optional: true, desc: "Scope the request to one or various trainings." + param :user_id, [Integer, Array], optional: true, desc: "Scope the request to one or various users." + example <<-EOS + # /open_api/v1/user_trainings?training_id[]=3&training_id[]=4&page=1&per_page=2 + { + "user_trainings": [ + { + "id": 720, + "user_id": 1340, + "training_id": 3, + "updated_at": "2016-05-03T14:16:38.373+02:00", + "created_at": "2016-05-03T14:16:38.373+02:00", + "user": { + "id": 1340, + "email": "xxxxxxxxxxx", + "created_at": "2015-12-20T11:30:32.670+01:00", + "full_name": "xxxxxxxxxxx" + } + }, + { + "id": 719, + "user_id": 1118, + "training_id": 4, + "updated_at": "2016-04-29T16:55:24.651+02:00", + "created_at": "2016-04-29T16:55:24.651+02:00", + "user": { + "id": 1118, + "email": "xxxxxxxxxxx", + "created_at": "2015-10-08T19:18:26.188+02:00", + "full_name": "xxxxxxxxxxx" + } + } + ] + } + + # /open_api/v1/user_trainings?user_id=1340&page=1&per_page=3 + { + "user_trainings": [ + { + "id": 720, + "user_id": 1340, + "training_id": 3, + "updated_at": "2016-05-03T14:16:38.373+02:00", + "created_at": "2016-05-03T14:16:38.373+02:00", + "training": { + "id": 3, + "name": "Formation Petite fraiseuse numerique", + "slug": "formation-petite-fraiseuse-numerique", + "updated_at": "2015-02-05T13:49:23.040+01:00", + "created_at": "2014-06-30T03:32:32.164+02:00" + } + }, + { + "id": 700, + "user_id": 1340, + "training_id": 2, + "updated_at": "2016-04-19T22:02:17.083+02:00", + "created_at": "2016-04-19T22:02:17.083+02:00", + "training": { + "id": 2, + "name": "Formation Laser / Vinyle", + "slug": "formation-laser-vinyle", + "updated_at": "2015-02-05T13:49:19.046+01:00", + "created_at": "2014-06-30T03:32:32.138+02:00" + } + }, + { + "id": 694, + "user_id": 1340, + "training_id": 1, + "updated_at": "2016-04-13T09:22:49.633+02:00", + "created_at": "2016-04-13T09:22:49.633+02:00", + "training": { + "id": 1, + "name": "Formation Imprimante 3D", + "slug": "formation-imprimante-3d", + "updated_at": "2015-02-05T13:49:15.025+01:00", + "created_at": "2014-06-30T03:32:32.126+02:00" + } + } + ] + } + EOS + end +end diff --git a/app/doc/open_api/v1/users_doc.rb b/app/doc/open_api/v1/users_doc.rb new file mode 100644 index 0000000000..12f4b70f45 --- /dev/null +++ b/app/doc/open_api/v1/users_doc.rb @@ -0,0 +1,97 @@ +class OpenAPI::V1::UsersDoc < OpenAPI::V1::BaseDoc + resource_description do + short 'Users' + desc 'Users of Fab-manager' + formats FORMATS + api_version API_VERSION + end + + include OpenAPI::V1::Concerns::ParamGroups + + doc_for :index do + api :GET, "/#{API_VERSION}/users", "Users index" + description "Users index, with optional pagination. Order 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." + example <<-EOS + # /open_api/v1/users?page=1&per_page=4 + { + "users": [ + { + "id": 1746, + "email": "xxxxxxx@xxxx.com", + "created_at": "2016-05-04T17:21:48.403+02:00", + "full_name": "xxxx xxxx", + "group": { + "id": 1, + "name": "standard, association", + "slug": "standard" + } + }, + { + "id": 1745, + "email": "xxxxxxx@gmail.com", + "created_at": "2016-05-03T15:21:13.125+02:00", + "full_name": "xxxxx xxxxx", + "group": { + "id": 2, + "name": "étudiant, - de 25 ans, enseignant, demandeur d'emploi", + "slug": "student" + } + }, + { + "id": 1744, + "email": "xxxxxxx@gmail.com", + "created_at": "2016-05-03T13:51:03.223+02:00", + "full_name": "xxxxxxx xxxx", + "group": { + "id": 1, + "name": "standard, association", + "slug": "standard" + } + }, + { + "id": 1743, + "email": "xxxxxxxx@setecastronomy.eu", + "created_at": "2016-05-03T12:24:38.724+02:00", + "full_name": "xxx xxxxxxx", + "group": { + "id": 1, + "name": "standard, association", + "slug": "standard" + } + } + ] + } + + # /open_api/v1/users?user_id[]=1746&user_id[]=1745 + { + "users": [ + { + "id": 1746, + "email": "xxxxxxxxxxxx", + "created_at": "2016-05-04T17:21:48.403+02:00", + "full_name": "xxxx xxxxxx", + "group": { + "id": 1, + "name": "standard, association", + "slug": "standard" + } + }, + { + "id": 1745, + "email": "xxxxxxxxx@gmail.com", + "created_at": "2016-05-03T15:21:13.125+02:00", + "full_name": "xxxxx xxxxxx", + "group": { + "id": 2, + "name": "étudiant, - de 25 ans, enseignant, demandeur d'emploi", + "slug": "student" + } + } + ] + } + EOS + end +end diff --git a/config/application.rb b/config/application.rb index a48a2d29e1..1eab40b5af 100644 --- a/config/application.rb +++ b/config/application.rb @@ -34,7 +34,6 @@ class Application < Rails::Application # config.i18n.default_locale = Rails.application.secrets.rails_locale - config.assets.paths << Rails.root.join('vendor', 'assets', 'components').to_s # Do not swallow errors in after_commit/after_rollback callbacks. diff --git a/config/initializers/apipie.rb b/config/initializers/apipie.rb new file mode 100644 index 0000000000..0440bf304c --- /dev/null +++ b/config/initializers/apipie.rb @@ -0,0 +1,19 @@ +Apipie.configure do |config| + config.app_name = "Fab-manager" + config.api_base_url = "/open_api" + config.doc_base_url = "/open_api/doc" + # where is your API defined? + config.api_controllers_matcher = "#{Rails.root}/app/controllers/open_api/v1/*.rb" + config.validate = false + config.app_info['v1'] = <<-EOS + = Pagination + --- + Pagination is done using headers. Following RFC-5988 standard for web linking. + It uses headers *Link*, *Total* and *Per-Page*. + + = Authentification + --- + Authentification is done using *Authorization* header. + You just have to set header *Authorization* to Token token=YOUR_TOKEN for every request. + EOS +end diff --git a/config/routes.rb b/config/routes.rb index 30d4ce12cb..408ae44ff0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -128,4 +128,5 @@ mount Sidekiq::Web => '/admin/sidekiq' end + apipie end From 2c70903dfd1fb4b78318cf6251d28dad762272d8 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Mon, 9 May 2016 18:15:04 +0200 Subject: [PATCH 05/30] admin open_api_client managing --- .../admin/open_api_clients.coffee.erb | 87 +++++++++++++++++++ .../controllers/main_nav.coffee.erb | 5 ++ app/assets/javascripts/router.coffee.erb | 15 +++- .../services/open_api_client.coffee | 11 +++ .../admin/open_api_clients/index.html.erb | 74 ++++++++++++++++ .../templates/shared/_member_form.html.erb | 2 - .../api/open_api_clients_controller.rb | 46 ++++++++++ app/policies/open_api/client_policy.rb | 21 +++++ .../open_api_clients/_client.json.jbuilder | 1 + .../api/open_api_clients/create.json.jbuilder | 1 + .../api/open_api_clients/index.json.jbuilder | 3 + .../reset_token.json.jbuilder | 1 + .../api/open_api_clients/update.json.jbuilder | 1 + config/locales/app.admin.fr.yml | 14 +++ config/locales/app.public.en.yml | 1 + config/locales/app.public.fr.yml | 1 + config/routes.rb | 3 + 17 files changed, 284 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb create mode 100644 app/assets/javascripts/services/open_api_client.coffee create mode 100644 app/assets/templates/admin/open_api_clients/index.html.erb create mode 100644 app/controllers/api/open_api_clients_controller.rb create mode 100644 app/policies/open_api/client_policy.rb create mode 100644 app/views/api/open_api_clients/_client.json.jbuilder create mode 100644 app/views/api/open_api_clients/create.json.jbuilder create mode 100644 app/views/api/open_api_clients/index.json.jbuilder create mode 100644 app/views/api/open_api_clients/reset_token.json.jbuilder create mode 100644 app/views/api/open_api_clients/update.json.jbuilder diff --git a/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb b/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb new file mode 100644 index 0000000000..2f2cd6e243 --- /dev/null +++ b/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb @@ -0,0 +1,87 @@ +Application.Controllers.controller "OpenAPIClientsController", ["$scope", 'clientsPromise', 'growl', 'OpenAPIClient', 'dialogs', '_t' +, ($scope, clientsPromise, growl, OpenAPIClient, dialogs, _t) -> + + + + ### PUBLIC SCOPE ### + + ## clients list + $scope.clients = clientsPromise + $scope.order = null + $scope.clientFormVisible = false + $scope.client = {} + + $scope.toggleForm = -> + $scope.clientFormVisible = !$scope.clientFormVisible + + + # Change the order criterion to the one provided + # @param orderBy {string} ordering criterion + ## + $scope.setOrder = (orderBy)-> + if $scope.order == orderBy + $scope.order = '-'+orderBy + else + $scope.order = orderBy + + $scope.saveClient = (client)-> + if client.id? + OpenAPIClient.update { id: client.id }, open_api_client: client, (clientResp)-> + client = clientResp + growl.success(_t('client_successfully_updated')) + else + OpenAPIClient.save open_api_client: client, (client)-> + $scope.clients.push client + growl.success(_t('client_successfully_created')) + + + $scope.clientFormVisible = false + $scope.clientForm.$setPristine() + $scope.client = {} + + $scope.editClient = (client)-> + $scope.clientFormVisible = true + $scope.client = client + + $scope.deleteClient = (index)-> + dialogs.confirm + resolve: + object: -> + title: _t('confirmation_required') + msg: _t('do_you_really_want_to_delete_this_open_api_client') + , -> + OpenAPIClient.delete { id: $scope.clients[index].id }, -> + $scope.clients.splice(index, 1) + growl.success(_t('client_successfully_deleted')) + + $scope.resetToken = (client)-> + dialogs.confirm + resolve: + object: -> + title: _t('confirmation_required') + msg: _t('do_you_really_want_to_revoke_this_open_api_access') + , -> + OpenAPIClient.resetToken { id: client.id }, {}, (clientResp)-> + client.token = clientResp.token + growl.success(_t('access_successfully_revoked')) + + + ## + # Ask for confirmation then delete the specified administrator + # @param admins {Array} full list of administrators + # @param admin {Object} administrator to delete + ## + $scope.destroyAdmin = (admins, admin)-> + dialogs.confirm + resolve: + object: -> + title: _t('confirmation_required') + msg: _t('do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') + , -> # cancel confirmed + Admin.delete id: admin.id, -> + admins.splice(findAdminIdxById(admins, admin.id), 1) + growl.success(_t('administrator_successfully_deleted')) + , (error)-> + growl.error(_t('unable_to_delete_the_administrator')) + +] diff --git a/app/assets/javascripts/controllers/main_nav.coffee.erb b/app/assets/javascripts/controllers/main_nav.coffee.erb index 96b1c5775a..f69cbbf675 100644 --- a/app/assets/javascripts/controllers/main_nav.coffee.erb +++ b/app/assets/javascripts/controllers/main_nav.coffee.erb @@ -95,5 +95,10 @@ Application.Controllers.controller "MainNavController", ["$scope", "$location", linkText: 'customization' linkIcon: 'gear' } + { + state: 'app.admin.open_api_clients' + linkText: 'open_api_clients' + linkIcon: 'cloud' + } ] ] diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index ed28c67795..a647a295a8 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -352,7 +352,6 @@ angular.module('application.router', ['ui.router']). translations: [ 'Translations', (Translations) -> Translations.query(['app.admin.machines_edit', 'app.shared.machine']).$promise ] - # trainings .state 'app.logged.trainings_reserve', url: '/trainings/reserve' @@ -840,5 +839,19 @@ angular.module('application.router', ['ui.router']). Translations.query('app.admin.settings').$promise ] + # OpenAPI Clients + .state 'app.admin.open_api_clients', + url: '/open_api_clients' + views: + 'main@': + templateUrl: '<%= asset_path "admin/open_api_clients/index.html" %>' + controller: 'OpenAPIClientsController' + resolve: + clientsPromise: ['OpenAPIClient', (OpenAPIClient)-> + OpenAPIClient.query().$promise + ] + translations: [ 'Translations', (Translations) -> + Translations.query('app.admin.open_api_clients').$promise + ] ] diff --git a/app/assets/javascripts/services/open_api_client.coffee b/app/assets/javascripts/services/open_api_client.coffee new file mode 100644 index 0000000000..03e0f0abce --- /dev/null +++ b/app/assets/javascripts/services/open_api_client.coffee @@ -0,0 +1,11 @@ +'use strict' + +Application.Services.factory 'OpenAPIClient', ["$resource", ($resource)-> + $resource "/api/open_api_clients/:id", + {id: "@id"}, + resetToken: + method: 'PATCH' + url: "/api/open_api_clients/:id/reset_token" + update: + method: 'PUT' +] diff --git a/app/assets/templates/admin/open_api_clients/index.html.erb b/app/assets/templates/admin/open_api_clients/index.html.erb new file mode 100644 index 0000000000..d1ce762db0 --- /dev/null +++ b/app/assets/templates/admin/open_api_clients/index.html.erb @@ -0,0 +1,74 @@ +
+
+
+
+ +
+
+
+
+

{{ 'open_api_clients' }}

+
+
+ +
+
+ +
+
+
+
+ + + +
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'name' | translate }} {{ 'calls_count' | translate }} {{ 'token' | translate }}{{ 'created_at' | translate }}
{{ client.name }}{{ client.calls_count }}{{ client.token }}{{ client.created_at | amDateFormat: 'LL' }} +
+ + + + + +
+
+
+
+
+
diff --git a/app/assets/templates/shared/_member_form.html.erb b/app/assets/templates/shared/_member_form.html.erb index a62e0cf64d..6c8cd52f5c 100644 --- a/app/assets/templates/shared/_member_form.html.erb +++ b/app/assets/templates/shared/_member_form.html.erb @@ -239,5 +239,3 @@ - - diff --git a/app/controllers/api/open_api_clients_controller.rb b/app/controllers/api/open_api_clients_controller.rb new file mode 100644 index 0000000000..6a72ba4d1f --- /dev/null +++ b/app/controllers/api/open_api_clients_controller.rb @@ -0,0 +1,46 @@ +class API::OpenAPIClientsController < API::ApiController + before_action :authenticate_user! + + def index + authorize OpenAPI::Client + @clients = OpenAPI::Client.order(:created_at) + end + # add authorization + def create + @client = OpenAPI::Client.new(client_params) + authorize @client + if @client.save + render status: :created + else + render json: @client.errors, status: :unprocessable_entity + end + end + + def update + @client = OpenAPI::Client.find(params[:id]) + authorize @client + if @client.update(client_params) + render status: :ok + else + render json: @client.errors, status: :unprocessable_entity + end + end + + def reset_token + @client = OpenAPI::Client.find(params[:id]) + authorize @client + @client.regenerate_token + end + + def destroy + @client = OpenAPI::Client.find(params[:id]) + authorize @client + @client.destroy + head 204 + end + + private + def client_params + params.require(:open_api_client).permit(:name) + end +end diff --git a/app/policies/open_api/client_policy.rb b/app/policies/open_api/client_policy.rb new file mode 100644 index 0000000000..f2c850effd --- /dev/null +++ b/app/policies/open_api/client_policy.rb @@ -0,0 +1,21 @@ +class OpenAPI::ClientPolicy < ApplicationPolicy + def index? + user.has_role? :admin + end + + def create? + user.has_role? :admin + end + + def update? + user.has_role? :admin + end + + def reset_token? + user.has_role? :admin + end + + def destroy? + user.has_role? :admin and record.calls_count == 0 + end +end diff --git a/app/views/api/open_api_clients/_client.json.jbuilder b/app/views/api/open_api_clients/_client.json.jbuilder new file mode 100644 index 0000000000..d074fe8cb0 --- /dev/null +++ b/app/views/api/open_api_clients/_client.json.jbuilder @@ -0,0 +1 @@ +json.extract! client, :id, :name, :calls_count, :token, :created_at diff --git a/app/views/api/open_api_clients/create.json.jbuilder b/app/views/api/open_api_clients/create.json.jbuilder new file mode 100644 index 0000000000..467fb8a7aa --- /dev/null +++ b/app/views/api/open_api_clients/create.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/open_api_clients/client', client: @client diff --git a/app/views/api/open_api_clients/index.json.jbuilder b/app/views/api/open_api_clients/index.json.jbuilder new file mode 100644 index 0000000000..c1b98a76b3 --- /dev/null +++ b/app/views/api/open_api_clients/index.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @clients do |client| + json.partial! 'api/open_api_clients/client', client: client +end diff --git a/app/views/api/open_api_clients/reset_token.json.jbuilder b/app/views/api/open_api_clients/reset_token.json.jbuilder new file mode 100644 index 0000000000..467fb8a7aa --- /dev/null +++ b/app/views/api/open_api_clients/reset_token.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/open_api_clients/client', client: @client diff --git a/app/views/api/open_api_clients/update.json.jbuilder b/app/views/api/open_api_clients/update.json.jbuilder new file mode 100644 index 0000000000..467fb8a7aa --- /dev/null +++ b/app/views/api/open_api_clients/update.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'api/open_api_clients/client', client: @client diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index 6cbe34d37d..7eee350bcb 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -463,3 +463,17 @@ fr: reservations_cancelling: "Annulation des réservations" customization_of_SETTING_successfully_saved: "La personnalisation de {{SETTING}} a bien été enregistrée." # angular interpolation file_successfully_updated: "Le fichier a bien été mis à jour." + + open_api_clients: + add_new_client: "Créer un compte client" + open_api_clients: "Clients OpenAPI" + calls_count: "Nombre d'appels" + created_at: "Date de création" + reset_token: "Révoquer l'accès" + client_name: "Nom du client" + do_you_really_want_to_delete_this_open_api_client: "Voulez vous vraiment supprimer ce compte client OpenAPI ?" + do_you_really_want_to_revoke_this_open_api_access: "Voulez vous vraiment revoquer l'accès de ce compte OpenAPI ? Une confirmation aura pour effet la génération d'un nouveau token." + client_successfully_created: "Le compte client a bien été créé." + client_successfully_updated: "Les modifications ont été enregistrées." + client_successfully_deleted: "Le compte client a bien été supprimé." + access_successfully_revoked: "L'accès a bien été revoqué." diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index c5eba66dac..09f5f15709 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -43,6 +43,7 @@ en: manage_the_projects_elements: "Manage the Projects Elements" statistics: "Statistics" customization: "Customization" + open_api_clients: "OpenAPI clients" # account creation modal create_your_account: "Create your account" diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index a3c513cb9b..38352d2613 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -43,6 +43,7 @@ fr: manage_the_projects_elements: "Gérer les éléments projets" statistics: "Statistiques" customization: "Personnalisation" + open_api_clients: "Clients OpenAPI" # fenêtre de création de compte create_your_account: "Créer votre compte" diff --git a/config/routes.rb b/config/routes.rb index 408ae44ff0..e9a384f327 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,6 +93,9 @@ get 'active', action: 'active', on: :collection end resources :abuses, only: [:create] + resources :open_api_clients, only: [:index, :create, :update, :destroy] do + patch :reset_token, on: :member + end # i18n get 'translations/:locale/:state' => 'translations#show', :constraints => { :state => /[^\/]+/ } # allow dots in URL for 'state' From 761d2436314b3a2e64791062e558f0803d0ae027 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Tue, 10 May 2016 09:31:24 +0200 Subject: [PATCH 06/30] adds translations for admin/open_api_clients angularside --- config/locales/app.admin.en.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index f0964ee7a0..7afa7bbe77 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -463,3 +463,17 @@ en: reservations_cancelling: "Reservations cancelling" customization_of_SETTING_successfully_saved: "Customization of {{SETTING}} successfully saved." # angular interpolation file_successfully_updated: "File successfully updated." + + open_api_clients: + add_new_client: "Create new API client" + open_api_clients: "OpenAPI clients" + calls_count: "calls count" + created_at: "Creation date" + reset_token: "revoke access" + client_name: "Client's name" + do_you_really_want_to_delete_this_open_api_client: "Do you really want to delete this OpenAPI client?" + do_you_really_want_to_revoke_this_open_api_access: "Do you really want to revoke this access ? It will erase and replace the current token." + client_successfully_created: "Client successfully created." + client_successfully_updated: "Client successfully updated." + client_successfully_deleted: "Client successfully deleted." + access_successfully_revoked: "Access successfully revoked." From e7590997fc7e5144ec9e9dde3488f060d01828e2 Mon Sep 17 00:00:00 2001 From: Nicolas Florentin Date: Tue, 10 May 2016 09:32:09 +0200 Subject: [PATCH 07/30] rm dead code --- .../admin/open_api_clients.coffee.erb | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb b/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb index 2f2cd6e243..e12bde9964 100644 --- a/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb +++ b/app/assets/javascripts/controllers/admin/open_api_clients.coffee.erb @@ -66,22 +66,4 @@ Application.Controllers.controller "OpenAPIClientsController", ["$scope", 'clien growl.success(_t('access_successfully_revoked')) - ## - # Ask for confirmation then delete the specified administrator - # @param admins {Array} full list of administrators - # @param admin {Object} administrator to delete - ## - $scope.destroyAdmin = (admins, admin)-> - dialogs.confirm - resolve: - object: -> - title: _t('confirmation_required') - msg: _t('do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') - , -> # cancel confirmed - Admin.delete id: admin.id, -> - admins.splice(findAdminIdxById(admins, admin.id), 1) - growl.success(_t('administrator_successfully_deleted')) - , (error)-> - growl.error(_t('unable_to_delete_the_administrator')) - ] From 8b699153cc37165111ea8d057d016431a5735ba5 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 20 Jun 2016 19:41:05 +0200 Subject: [PATCH 08/30] update jbuilder and add cache in progress --- Gemfile | 3 ++- Gemfile.lock | 15 +++++++++------ app/views/api/groups/index.json.jbuilder | 4 +++- app/views/api/machines/index.json.jbuilder | 18 ++++++------------ app/views/api/prices/index.json.jbuilder | 4 +++- .../api/trainings_pricings/index.json.jbuilder | 4 +++- config/environments/development.rb | 2 +- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Gemfile b/Gemfile index b8aa689be4..6b43fa216e 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,8 @@ gem 'therubyracer', '= 0.12.0', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 2.0' +gem 'jbuilder', '~> 2.5' +gem 'jbuilder_cache_multi' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc #TODO remove unused ? diff --git a/Gemfile.lock b/Gemfile.lock index e113035141..655559e0a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -179,9 +179,11 @@ GEM multi_xml (>= 0.5.2) i18n (0.7.0) ice_nine (0.11.1) - jbuilder (2.2.12) - activesupport (>= 3.0.0, < 5) + jbuilder (2.5.0) + activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) + jbuilder_cache_multi (0.0.3) + jbuilder (>= 1.5.0, < 3) jquery-rails (4.0.3) rails-dom-testing (~> 1.0) railties (>= 4.2.0) @@ -208,13 +210,13 @@ GEM mime-types (2.99) mini_magick (4.2.0) mini_portile2 (2.0.0) - minitest (5.8.4) + minitest (5.9.0) minitest-reporters (1.1.8) ansi builder minitest (>= 5.0) ruby-progressbar - multi_json (1.11.2) + multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) naught (1.0.0) @@ -452,7 +454,8 @@ DEPENDENCIES foreman forgery friendly_id (~> 5.1.0) - jbuilder (~> 2.0) + jbuilder (~> 2.5) + jbuilder_cache_multi jquery-rails kaminari letter_opener @@ -497,4 +500,4 @@ DEPENDENCIES webmock BUNDLED WITH - 1.11.2 + 1.12.5 diff --git a/app/views/api/groups/index.json.jbuilder b/app/views/api/groups/index.json.jbuilder index 544b158eaa..fb5c92e772 100644 --- a/app/views/api/groups/index.json.jbuilder +++ b/app/views/api/groups/index.json.jbuilder @@ -1 +1,3 @@ -json.partial! 'api/groups/group', collection: @groups, as: :group +json.cache! @groups do + json.partial! 'api/groups/group', collection: @groups, as: :group +end diff --git a/app/views/api/machines/index.json.jbuilder b/app/views/api/machines/index.json.jbuilder index 25c92a1b37..c45e7965e7 100644 --- a/app/views/api/machines/index.json.jbuilder +++ b/app/views/api/machines/index.json.jbuilder @@ -1,13 +1,7 @@ -user_is_admin = (current_user and current_user.is_admin?) - -json.array!(@machines) do |machine| - json.extract! machine, :id, :name, :description, :spec, :slug - json.url machine_url(machine, format: :json) - json.machine_image machine.machine_image.attachment.medium.url if machine.machine_image - json.current_user_is_training current_user.is_training_machine?(machine) if current_user - json.current_user_training_reservation do - json.partial! 'api/reservations/reservation', reservation: current_user.training_reservation_by_machine(machine) - end if current_user and !current_user.is_training_machine?(machine) and current_user.training_reservation_by_machine(machine) - - json.plan_ids machine.plan_ids if user_is_admin +json.cache! @machines do + json.array!(@machines) do |machine| + json.extract! machine, :id, :name, :description, :spec, :slug + json.url machine_url(machine, format: :json) + json.machine_image machine.machine_image.attachment.medium.url if machine.machine_image + end end diff --git a/app/views/api/prices/index.json.jbuilder b/app/views/api/prices/index.json.jbuilder index 5cd43fe7be..4f77c5ab73 100644 --- a/app/views/api/prices/index.json.jbuilder +++ b/app/views/api/prices/index.json.jbuilder @@ -1 +1,3 @@ -json.prices @prices, partial: 'api/prices/price', as: :price +json.cache! @prices do + json.prices @prices, partial: 'api/prices/price', as: :price +end diff --git a/app/views/api/trainings_pricings/index.json.jbuilder b/app/views/api/trainings_pricings/index.json.jbuilder index a091263a6f..5ff1f0d04a 100644 --- a/app/views/api/trainings_pricings/index.json.jbuilder +++ b/app/views/api/trainings_pricings/index.json.jbuilder @@ -1 +1,3 @@ -json.partial! 'api/trainings_pricings/trainings_pricing', collection: @trainings_pricings, as: :trainings_pricing +json.cache! @trainings_pricings do + json.partial! 'api/trainings_pricings/trainings_pricing', collection: @trainings_pricings, as: :trainings_pricing +end diff --git a/config/environments/development.rb b/config/environments/development.rb index e24f8414a7..2e1262529e 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -11,7 +11,7 @@ # Show full error reports and disable caching. config.consider_all_requests_local = true - config.action_controller.perform_caching = false + config.action_controller.perform_caching = true # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false From 4d0d3d6af0d4fad44eb6dcf19b5b02724984de1a Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 21 Jun 2016 14:49:39 +0200 Subject: [PATCH 09/30] optimise load plans json --- .../controllers/admin/plans.coffee.erb | 19 ++++++++++--------- app/assets/javascripts/router.coffee.erb | 6 +++--- app/controllers/api/plans_controller.rb | 9 ++------- app/views/api/plans/_plan.json.jbuilder | 15 --------------- app/views/api/plans/index.json.jbuilder | 8 +++++++- app/views/api/prices/_price.json.jbuilder | 1 + 6 files changed, 23 insertions(+), 35 deletions(-) diff --git a/app/assets/javascripts/controllers/admin/plans.coffee.erb b/app/assets/javascripts/controllers/admin/plans.coffee.erb index c5bf1151b3..d7b266f1ae 100644 --- a/app/assets/javascripts/controllers/admin/plans.coffee.erb +++ b/app/assets/javascripts/controllers/admin/plans.coffee.erb @@ -175,8 +175,8 @@ Application.Controllers.controller 'NewPlanController', ['$scope', '$uibModal', ## # Controller used in the plan edition form ## -Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'prices', 'partners', 'CSRF', '$state', '$stateParams', 'growl', '$filter', '_t', '$locale' -, ($scope, groups, plans, planPromise, machines, prices, partners, CSRF, $state, $stateParams, growl, $filter, _t, $locale) -> +Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'prices', 'partners', 'CSRF', '$state', '$stateParams', 'growl', '$filter', '_t', '$locale', 'Plan' +, ($scope, groups, plans, planPromise, machines, prices, partners, CSRF, $state, $stateParams, growl, $filter, _t, $locale, Plan) -> @@ -207,12 +207,13 @@ Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'p ## $scope.copyPricesFromPlan = -> if $scope.plan.parent - parentPlan = $scope.getPlanFromId($scope.plan.parent) - for parentPrice in parentPlan.prices - for childKey, childPrice of $scope.plan.prices - if childPrice.priceable_type == parentPrice.priceable_type and childPrice.priceable_id == parentPrice.priceable_id - $scope.plan.prices[childKey].amount = parentPrice.amount - break + Plan.get {id: $scope.plan.parent}, (parentPlan) -> + for parentPrice in parentPlan.prices + for childKey, childPrice of $scope.plan.prices + if childPrice.priceable_type == parentPrice.priceable_type and childPrice.priceable_id == parentPrice.priceable_id + $scope.plan.prices[childKey].amount = parentPrice.amount + break + # if no plan were selected, unset every prices else for key, price of $scope.plan.prices @@ -257,4 +258,4 @@ Application.Controllers.controller 'EditPlanController', ['$scope', 'groups', 'p ## !!! MUST BE CALLED AT THE END of the controller initialize() -] \ No newline at end of file +] diff --git a/app/assets/javascripts/router.coffee.erb b/app/assets/javascripts/router.coffee.erb index 7f1416510d..e3d47ad4cd 100644 --- a/app/assets/javascripts/router.coffee.erb +++ b/app/assets/javascripts/router.coffee.erb @@ -330,7 +330,7 @@ angular.module('application.router', ['ui.router']). controller: 'ReserveMachineController' resolve: plansPromise: ['Plan', (Plan)-> - Plan.query(attributes_requested: "['machines_credits']").$promise + Plan.query().$promise ] groupsPromise: ['Group', (Group)-> Group.query().$promise @@ -375,7 +375,7 @@ angular.module('application.router', ['ui.router']). Setting.get(name: 'training_explications_alert').$promise ] plansPromise: ['Plan', (Plan)-> - Plan.query(attributes_requested: "['trainings_credits']").$promise + Plan.query().$promise ] groupsPromise: ['Group', (Group)-> Group.query().$promise @@ -423,7 +423,7 @@ angular.module('application.router', ['ui.router']). Setting.get(name: 'subscription_explications_alert').$promise ] plansPromise: ['Plan', (Plan)-> - Plan.query(shallow: true).$promise + Plan.query().$promise ] groupsPromise: ['Group', (Group)-> Group.query().$promise diff --git a/app/controllers/api/plans_controller.rb b/app/controllers/api/plans_controller.rb index b9b9221128..7b7f0d2031 100644 --- a/app/controllers/api/plans_controller.rb +++ b/app/controllers/api/plans_controller.rb @@ -2,14 +2,9 @@ class API::PlansController < API::ApiController before_action :authenticate_user!, except: [:index] def index - @attributes_requested = params[:attributes_requested] - @plans = Plan.all + @plans = Plan.includes(:plan_file) @plans = @plans.where(group_id: params[:group_id]) if params[:group_id] - if params[:shallow] - render :shallow_index - else - render :index - end + render :index end def show diff --git a/app/views/api/plans/_plan.json.jbuilder b/app/views/api/plans/_plan.json.jbuilder index bda984fbe7..ca0eb12922 100644 --- a/app/views/api/plans/_plan.json.jbuilder +++ b/app/views/api/plans/_plan.json.jbuilder @@ -6,23 +6,8 @@ json.plan_file_attributes do json.attachment_identifier plan.plan_file.attachment_identifier end if plan.plan_file -json.prices plan.prices do |price| - json.extract! price, :id, :group_id, :plan_id, :priceable_type, :priceable_id - json.amount price.amount / 100.0 - json.priceable_name price.priceable.name -end - json.partners plan.partners do |partner| json.first_name partner.first_name json.last_name partner.last_name json.email partner.email end if plan.respond_to?(:partners) - -json.training_credits plan.training_credits do |tc| - json.training_id tc.creditable_id -end if attribute_requested?(@attributes_requested, 'trainings_credits') - -json.machine_credits plan.machine_credits do |mc| - json.machine_id mc.creditable_id - json.hours mc.hours -end if attribute_requested?(@attributes_requested, 'machines_credits') diff --git a/app/views/api/plans/index.json.jbuilder b/app/views/api/plans/index.json.jbuilder index a639165001..46e7fb9bdb 100644 --- a/app/views/api/plans/index.json.jbuilder +++ b/app/views/api/plans/index.json.jbuilder @@ -1 +1,7 @@ -json.partial! 'api/plans/plan', collection: @plans, as: :plan +json.cache! @plans do + json.array!(@plans) do |plan| + json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :description, :type, :ui_weight + json.amount (plan.amount / 100.00) + json.plan_file_url plan.plan_file.attachment_url if plan.plan_file + end +end diff --git a/app/views/api/prices/_price.json.jbuilder b/app/views/api/prices/_price.json.jbuilder index 19a0ffc061..ca3aea1c43 100644 --- a/app/views/api/prices/_price.json.jbuilder +++ b/app/views/api/prices/_price.json.jbuilder @@ -1,2 +1,3 @@ json.extract! price, :id, :group_id, :plan_id, :priceable_type, :priceable_id json.amount price.amount / 100.0 +json.priceable_name price.priceable.name From 85a40db603c262671118668ee79d95a69093f914 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 21 Jun 2016 16:04:44 +0200 Subject: [PATCH 10/30] price cache --- app/assets/javascripts/controllers/admin/pricing.coffee.erb | 2 +- app/assets/javascripts/services/price.coffee | 2 +- app/controllers/api/trainings_pricings_controller.rb | 2 +- app/views/api/prices/_price.json.jbuilder | 1 - app/views/api/prices/index.json.jbuilder | 2 +- .../api/trainings_pricings/_trainings_pricing.json.jbuilder | 3 --- 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/controllers/admin/pricing.coffee.erb b/app/assets/javascripts/controllers/admin/pricing.coffee.erb index c6b959c4a6..99f6b94751 100644 --- a/app/assets/javascripts/controllers/admin/pricing.coffee.erb +++ b/app/assets/javascripts/controllers/admin/pricing.coffee.erb @@ -8,7 +8,7 @@ Application.Controllers.controller "EditPricingController", ["$scope", "$state", ### PUBLIC SCOPE ### ## List of machines prices (not considering any plan) - $scope.machinesPrices = machinesPricesPromise.prices + $scope.machinesPrices = machinesPricesPromise ## List of trainings pricing $scope.trainingsPricings = trainingsPricingsPromise diff --git a/app/assets/javascripts/services/price.coffee b/app/assets/javascripts/services/price.coffee index 66b8270fec..f836f943e8 100644 --- a/app/assets/javascripts/services/price.coffee +++ b/app/assets/javascripts/services/price.coffee @@ -4,7 +4,7 @@ Application.Services.factory 'Price', ["$resource", ($resource)-> $resource "/api/prices/:id", {}, query: - isArray: false + isArray: true update: method: 'PUT' compute: diff --git a/app/controllers/api/trainings_pricings_controller.rb b/app/controllers/api/trainings_pricings_controller.rb index 367b1f72dd..2db646e728 100644 --- a/app/controllers/api/trainings_pricings_controller.rb +++ b/app/controllers/api/trainings_pricings_controller.rb @@ -2,7 +2,7 @@ class API::TrainingsPricingsController < API::ApiController before_action :authenticate_user! def index - @trainings_pricings = TrainingsPricing.includes(:training) + @trainings_pricings = TrainingsPricing.all end def update diff --git a/app/views/api/prices/_price.json.jbuilder b/app/views/api/prices/_price.json.jbuilder index ca3aea1c43..19a0ffc061 100644 --- a/app/views/api/prices/_price.json.jbuilder +++ b/app/views/api/prices/_price.json.jbuilder @@ -1,3 +1,2 @@ json.extract! price, :id, :group_id, :plan_id, :priceable_type, :priceable_id json.amount price.amount / 100.0 -json.priceable_name price.priceable.name diff --git a/app/views/api/prices/index.json.jbuilder b/app/views/api/prices/index.json.jbuilder index 4f77c5ab73..8a03cbb34b 100644 --- a/app/views/api/prices/index.json.jbuilder +++ b/app/views/api/prices/index.json.jbuilder @@ -1,3 +1,3 @@ json.cache! @prices do - json.prices @prices, partial: 'api/prices/price', as: :price + json.partial! 'api/prices/price', collection: @prices, as: :price end diff --git a/app/views/api/trainings_pricings/_trainings_pricing.json.jbuilder b/app/views/api/trainings_pricings/_trainings_pricing.json.jbuilder index 221217ac22..1d215bd506 100644 --- a/app/views/api/trainings_pricings/_trainings_pricing.json.jbuilder +++ b/app/views/api/trainings_pricings/_trainings_pricing.json.jbuilder @@ -1,5 +1,2 @@ json.extract! trainings_pricing, :id, :group_id, :training_id json.amount trainings_pricing.amount / 100.0 -json.training do - json.name trainings_pricing.training.name -end From d65ecf437b4c214bf5d901959b0595d819c842e9 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 21 Jun 2016 17:02:44 +0200 Subject: [PATCH 11/30] cache training json --- app/views/api/trainings/index.json.jbuilder | 28 ++++++++------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/app/views/api/trainings/index.json.jbuilder b/app/views/api/trainings/index.json.jbuilder index 72ff1ff852..02cdd42b41 100644 --- a/app/views/api/trainings/index.json.jbuilder +++ b/app/views/api/trainings/index.json.jbuilder @@ -1,19 +1,13 @@ -json.array!(@trainings) do |training| - json.id training.id - json.name training.name - json.description training.description - json.machine_ids training.machine_ids - json.availabilities training.availabilities do |a| - json.id a.id - json.start_at a.start_at.iso8601 - json.end_at a.end_at.iso8601 - json.reservation_users a.slots.map do |slot| - json.id slot.reservation.user.id - json.full_name slot.reservation.user.profile.full_name - json.is_valid slot.reservation.user.trainings.include?(training) - end - end if attribute_requested?(@requested_attributes, 'availabilities') - json.nb_total_places training.nb_total_places +role = (current_user and current_user.is_admin?) ? 'admin' : 'user' - json.plan_ids training.plan_ids if current_user and current_user.is_admin? +json.cache! [@trainings, role] do + json.array!(@trainings) do |training| + json.id training.id + json.name training.name + json.description training.description + json.machine_ids training.machine_ids + json.nb_total_places training.nb_total_places + + json.plan_ids training.plan_ids if role === 'admin' + end end From e82372fb7b2919e20b60624698946d43f7a54a12 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Tue, 21 Jun 2016 19:07:47 +0200 Subject: [PATCH 12/30] optimise machine availabilities query --- app/controllers/api/availabilities_controller.rb | 12 ++++++------ app/views/api/machines/show.json.jbuilder | 10 ---------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/app/controllers/api/availabilities_controller.rb b/app/controllers/api/availabilities_controller.rb index 51f3c94ea7..665ef72634 100644 --- a/app/controllers/api/availabilities_controller.rb +++ b/app/controllers/api/availabilities_controller.rb @@ -49,9 +49,10 @@ def machine else @user = current_user end + current_user_role = current_user.is_admin? ? 'admin' : 'user' @machine = Machine.find(params[:machine_id]) @slots = [] - @reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).joins(:slots).where('slots.start_at > ?', Time.now) + @reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, :user).references(:slots, :user).where('slots.start_at > ?', Time.now) if @user.is_admin? @availabilities = @machine.availabilities.where("end_at > ? AND available_type = 'machines'", Time.now) else @@ -62,8 +63,8 @@ def machine @availabilities.each do |a| ((a.end_at - a.start_at)/SLOT_DURATION.minutes).to_i.times do |i| if (a.start_at + (i * SLOT_DURATION).minutes) > Time.now - slot = Slot.new(start_at: a.start_at + (i*SLOT_DURATION).minutes, end_at: a.start_at + (i*SLOT_DURATION).minutes + SLOT_DURATION.minutes, availability_id: a.id, machine: @machine, title: '') - slot = verify_machine_is_reserved(slot, @reservations) + slot = Slot.new(start_at: a.start_at + (i*SLOT_DURATION).minutes, end_at: a.start_at + (i*SLOT_DURATION).minutes + SLOT_DURATION.minutes, availability_id: a.id, availability: a, machine: @machine, title: '') + slot = verify_machine_is_reserved(slot, @reservations, current_user, current_user_role) @slots << slot end end @@ -115,15 +116,14 @@ def is_reserved(start_at, reservations) is_reserved end - def verify_machine_is_reserved(slot, reservations) - user = current_user + def verify_machine_is_reserved(slot, reservations, user, user_role) reservations.each do |r| r.slots.each do |s| if s.start_at == slot.start_at and s.canceled_at == nil slot.id = s.id slot.is_reserved = true slot.title = t('availabilities.not_available') - slot.can_modify = true if user.is_admin? + slot.can_modify = true if user_role === 'admin' slot.reservation = r end if s.start_at == slot.start_at and r.user == user and s.canceled_at == nil diff --git a/app/views/api/machines/show.json.jbuilder b/app/views/api/machines/show.json.jbuilder index 1e6d8760ac..bfb4ab07ad 100644 --- a/app/views/api/machines/show.json.jbuilder +++ b/app/views/api/machines/show.json.jbuilder @@ -11,16 +11,6 @@ json.current_user_training_reservation do json.partial! 'api/reservations/reservation', reservation: current_user.training_reservation_by_machine(@machine) end if current_user and !current_user.is_training_machine?(@machine) and current_user.training_reservation_by_machine(@machine) -json.amount_by_group Group.all do |g| - json.id g.id - json.name g.name - json.not_subscribe_amount @machine.not_subscribe_price(g.id).amount/100.0 - - json.amount_by_plan @machine.prices_by_group(g.id) do |price| - json.plan_id price.plan_id - json.amount price.amount/100.0 - end -end json.machine_projects @machine.projects.published.last(10) do |p| json.id p.id json.name p.name From 4dff74827bb0311c27d391af303e5d46ef5c6663 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Wed, 22 Jun 2016 12:54:12 +0200 Subject: [PATCH 13/30] optimise machine/training availabilities query --- app/controllers/api/availabilities_controller.rb | 16 ++++++++-------- app/models/availability.rb | 5 +++-- .../api/availabilities/machine.json.jbuilder | 2 +- .../api/availabilities/trainings.json.jbuilder | 16 +++++----------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/app/controllers/api/availabilities_controller.rb b/app/controllers/api/availabilities_controller.rb index 665ef72634..177e1f16fe 100644 --- a/app/controllers/api/availabilities_controller.rb +++ b/app/controllers/api/availabilities_controller.rb @@ -49,22 +49,22 @@ def machine else @user = current_user end - current_user_role = current_user.is_admin? ? 'admin' : 'user' + @current_user_role = current_user.is_admin? ? 'admin' : 'user' @machine = Machine.find(params[:machine_id]) @slots = [] - @reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, :user).references(:slots, :user).where('slots.start_at > ?', Time.now) + @reservations = Reservation.where('reservable_type = ? and reservable_id = ?', @machine.class.to_s, @machine.id).includes(:slots, user: [:profile]).references(:slots, :user).where('slots.start_at > ?', Time.now) if @user.is_admin? - @availabilities = @machine.availabilities.where("end_at > ? AND available_type = 'machines'", Time.now) + @availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND available_type = 'machines'", Time.now) else end_at = 1.month.since end_at = 3.months.since if is_subscription_year(@user) - @availabilities = @machine.availabilities.includes(:availability_tags).where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) + @availabilities = @machine.availabilities.includes(:tags).where("end_at > ? AND end_at < ? AND available_type = 'machines'", Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) end @availabilities.each do |a| ((a.end_at - a.start_at)/SLOT_DURATION.minutes).to_i.times do |i| if (a.start_at + (i * SLOT_DURATION).minutes) > Time.now slot = Slot.new(start_at: a.start_at + (i*SLOT_DURATION).minutes, end_at: a.start_at + (i*SLOT_DURATION).minutes + SLOT_DURATION.minutes, availability_id: a.id, availability: a, machine: @machine, title: '') - slot = verify_machine_is_reserved(slot, @reservations, current_user, current_user_role) + slot = verify_machine_is_reserved(slot, @reservations, current_user, @current_user_role) @slots << slot end end @@ -78,13 +78,13 @@ def trainings @user = current_user end @slots = [] - @reservations = @user.reservations.where("reservable_type = 'Training'").joins(:slots).where('slots.start_at > ?', Time.now) + @reservations = @user.reservations.includes(:slots).references(:slots).where("reservable_type = 'Training' AND slots.start_at > ?", Time.now) if @user.is_admin? - @availabilities = Availability.trainings.where('start_at > ?', Time.now) + @availabilities = Availability.includes(:tags, :slots, trainings: [:machines]).trainings.where('availabilities.start_at > ?', Time.now) else end_at = 1.month.since end_at = 3.months.since if can_show_slot_plus_three_months(@user) - @availabilities = Availability.trainings.includes(:availability_tags).where('start_at > ? AND start_at < ?', Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) + @availabilities = Availability.includes(:tags, :slots, trainings: [:machines]).trainings.where('start_at > ? AND start_at < ?', Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) end @availabilities.each do |a| a = verify_training_is_reserved(a, @reservations) diff --git a/app/models/availability.rb b/app/models/availability.rb index 0790fdc8fe..03b8f29845 100644 --- a/app/models/availability.rb +++ b/app/models/availability.rb @@ -16,7 +16,7 @@ class Availability < ActiveRecord::Base accepts_nested_attributes_for :tags, allow_destroy: true scope :machines, -> { where(available_type: 'machines') } - scope :trainings, -> { where(available_type: 'training') } + scope :trainings, -> { includes(:trainings).where(available_type: 'training') } attr_accessor :is_reserved, :slot_id, :can_modify @@ -51,9 +51,10 @@ def title # if haven't defined a nb_total_places, places are unlimited def is_completed return false if nb_total_places.blank? - nb_total_places <= slots.where(canceled_at: nil).size + nb_total_places <= slots.to_a.select {|s| s.canceled_at == nil }.size end + # TODO: refactoring this function for avoid N+1 query def nb_total_places if read_attribute(:nb_total_places).present? read_attribute(:nb_total_places) diff --git a/app/views/api/availabilities/machine.json.jbuilder b/app/views/api/availabilities/machine.json.jbuilder index c1811d812b..c068a649d9 100644 --- a/app/views/api/availabilities/machine.json.jbuilder +++ b/app/views/api/availabilities/machine.json.jbuilder @@ -17,7 +17,7 @@ json.array!(@slots) do |slot| json.user do json.id slot.reservation.user.id json.name slot.reservation.user.profile.full_name - end if slot.reservation # ... if the slot was reserved + end if @current_user_role == 'admin' and slot.reservation # ... if the slot was reserved json.tag_ids slot.availability.tag_ids json.tags slot.availability.tags do |t| json.id t.id diff --git a/app/views/api/availabilities/trainings.json.jbuilder b/app/views/api/availabilities/trainings.json.jbuilder index b180a62486..1cbee8169d 100644 --- a/app/views/api/availabilities/trainings.json.jbuilder +++ b/app/views/api/availabilities/trainings.json.jbuilder @@ -2,26 +2,21 @@ json.array!(@availabilities) do |a| json.id a.id json.slot_id a.slot_id if a.slot_id if a.is_reserved + json.is_reserved true json.title "#{a.trainings[0].name}' - #{t('trainings.i_ve_reserved')}" + json.borderColor '#b2e774' elsif a.is_completed + json.is_completed true json.title "#{a.trainings[0].name} - #{t('trainings.completed')}" + json.borderColor '#eeeeee' else json.title a.trainings[0].name + json.borderColor '#bd7ae9' end json.start a.start_at.iso8601 json.end a.end_at.iso8601 - json.is_reserved a.is_reserved json.backgroundColor 'white' - json.borderColor a.is_reserved ? '#b2e774' : '#bd7ae9' - if a.is_reserved - json.borderColor '#b2e774' - elsif a.is_completed - json.borderColor '#eeeeee' - else - json.borderColor '#bd7ae9' - end json.can_modify a.can_modify - json.is_completed a.is_completed json.nb_total_places a.nb_total_places json.training do @@ -32,7 +27,6 @@ json.array!(@availabilities) do |a| json.id m.id json.name m.name end - json.amount a.trainings.first.amount_by_group(@user.group_id).amount_by_plan(nil)/100.0 if @user end json.tag_ids a.tag_ids json.tags a.tags do |t| From 702e35650f8662d964d7137a66db740ded682988 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Fri, 24 Jun 2016 18:26:11 +0200 Subject: [PATCH 14/30] event cache --- app/models/event.rb | 2 ++ app/policies/event_policy.rb | 4 ++-- app/views/api/events/index.json.jbuilder | 13 +++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index 8007f6a4eb..412580b708 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -14,6 +14,8 @@ class Event < ActiveRecord::Base after_create :event_recurrence before_save :update_nb_free_places + # update event updated_at for index cache + after_save -> { self.touch } def name title diff --git a/app/policies/event_policy.rb b/app/policies/event_policy.rb index fc140bb42b..d317c1b962 100644 --- a/app/policies/event_policy.rb +++ b/app/policies/event_policy.rb @@ -2,12 +2,12 @@ class EventPolicy < ApplicationPolicy class Scope < Scope def resolve if user.nil? or (user and !user.is_admin?) - scope.includes(:event_image, :event_files, :availability) + scope.includes(:event_image, :event_files, :availability, :categories) .where('availabilities.start_at >= ?', Time.now) .order('availabilities.start_at ASC') .references(:availabilities) else - scope.includes(:event_image, :event_files, :availability) + scope.includes(:event_image, :event_files, :availability, :categories) .order('availabilities.start_at DESC') .references(:availabilities) end diff --git a/app/views/api/events/index.json.jbuilder b/app/views/api/events/index.json.jbuilder index 4a24caedc0..c95bf00a7a 100644 --- a/app/views/api/events/index.json.jbuilder +++ b/app/views/api/events/index.json.jbuilder @@ -1,7 +1,8 @@ -json.array!(@events) do |event| - json.partial! 'api/events/event', event: event - json.event_image_small event.event_image.attachment.small.url if event.event_image - json.url event_url(event, format: :json) - json.nb_total_events @total +json.cache! [@events, @page] do + json.array!(@events) do |event| + json.partial! 'api/events/event', event: event + json.event_image_small event.event_image.attachment.small.url if event.event_image + json.url event_url(event, format: :json) + json.nb_total_events @total + end end - From d64e8a291bb2c7bb373bdfea8a5d963b9e9785a5 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Fri, 24 Jun 2016 18:36:36 +0200 Subject: [PATCH 15/30] remove order for avoid N+1 --- app/views/api/trainings/show.json.jbuilder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/api/trainings/show.json.jbuilder b/app/views/api/trainings/show.json.jbuilder index 6afadde512..fbc0587b21 100644 --- a/app/views/api/trainings/show.json.jbuilder +++ b/app/views/api/trainings/show.json.jbuilder @@ -1,5 +1,5 @@ json.extract! @training, :id, :name, :machine_ids, :nb_total_places -json.availabilities @training.availabilities.order('start_at DESC') do |a| +json.availabilities @training.availabilities do |a| json.id a.id json.start_at a.start_at.iso8601 json.end_at a.end_at.iso8601 From 0bb4665b4a3e0fe7dab6e3b32da9faf40383f7be Mon Sep 17 00:00:00 2001 From: Peng DU Date: Fri, 24 Jun 2016 18:43:22 +0200 Subject: [PATCH 16/30] avoid N+1 for users list --- app/controllers/api/admins_controller.rb | 2 +- app/controllers/api/members_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/admins_controller.rb b/app/controllers/api/admins_controller.rb index 7f254bda74..175d6b4e54 100644 --- a/app/controllers/api/admins_controller.rb +++ b/app/controllers/api/admins_controller.rb @@ -3,7 +3,7 @@ class API::AdminsController < API::ApiController def index authorize :admin - @admins = User.admins + @admins = User.includes(profile: [:user_avatar]).admins end def create diff --git a/app/controllers/api/members_controller.rb b/app/controllers/api/members_controller.rb index 8950d019fa..d47dac1779 100644 --- a/app/controllers/api/members_controller.rb +++ b/app/controllers/api/members_controller.rb @@ -169,7 +169,7 @@ def list order_key = 'users.id' end - @members = User.includes(:profile, :group) + @members = User.includes(:profile, :group, :subscriptions) .joins(:profile, :group, :roles, 'LEFT JOIN "subscriptions" ON "subscriptions"."user_id" = "users"."id" LEFT JOIN "plans" ON "plans"."id" = "subscriptions"."plan_id"') .where("users.is_active = 'true' AND roles.name = 'member'") .order("#{order_key} #{direction}") From a12c26e06c8f39e1160a9d29ac740280ebaf3658 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 09:10:33 +0200 Subject: [PATCH 17/30] [bug] Unable to run if postgre unaccent was already active --- db/migrate/20160613093842_create_unaccent_function.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20160613093842_create_unaccent_function.rb b/db/migrate/20160613093842_create_unaccent_function.rb index 2c6ed05ddb..750f717343 100644 --- a/db/migrate/20160613093842_create_unaccent_function.rb +++ b/db/migrate/20160613093842_create_unaccent_function.rb @@ -2,8 +2,8 @@ class CreateUnaccentFunction < ActiveRecord::Migration # PostgreSQL only def up - execute 'CREATE EXTENSION unaccent;' - execute 'CREATE EXTENSION pg_trgm;' + execute 'CREATE EXTENSION IF NOT EXISTS unaccent;' + execute 'CREATE EXTENSION IF NOT EXISTS pg_trgm;' execute "CREATE OR REPLACE FUNCTION f_unaccent(text) RETURNS text AS $func$ From b08ab236dc9e0cf766026c83edb2994a14f5af0b Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 09:10:33 +0200 Subject: [PATCH 18/30] [bug] Unable to run migrations if postgre unaccent was already active --- db/migrate/20160613093842_create_unaccent_function.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20160613093842_create_unaccent_function.rb b/db/migrate/20160613093842_create_unaccent_function.rb index 2c6ed05ddb..750f717343 100644 --- a/db/migrate/20160613093842_create_unaccent_function.rb +++ b/db/migrate/20160613093842_create_unaccent_function.rb @@ -2,8 +2,8 @@ class CreateUnaccentFunction < ActiveRecord::Migration # PostgreSQL only def up - execute 'CREATE EXTENSION unaccent;' - execute 'CREATE EXTENSION pg_trgm;' + execute 'CREATE EXTENSION IF NOT EXISTS unaccent;' + execute 'CREATE EXTENSION IF NOT EXISTS pg_trgm;' execute "CREATE OR REPLACE FUNCTION f_unaccent(text) RETURNS text AS $func$ From 68e45c0460010095614e31ef159b0bba4fc89cbf Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 27 Jun 2016 10:55:51 +0200 Subject: [PATCH 19/30] cache last event and optimise last subscript api --- app/controllers/api/events_controller.rb | 5 +++-- app/controllers/api/members_controller.rb | 2 +- app/views/api/events/upcoming.json.jbuilder | 11 ++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/events_controller.rb b/app/controllers/api/events_controller.rb index d24573eee9..b17dd3b6f0 100644 --- a/app/controllers/api/events_controller.rb +++ b/app/controllers/api/events_controller.rb @@ -4,13 +4,14 @@ class API::EventsController < API::ApiController def index @events = policy_scope(Event) @total = @events.count - @events = @events.page(params[:page]).per(12) + @page = params[:page] + @events = @events.page(@page).per(12) end # GET /events/upcoming/:limit def upcoming limit = params[:limit] - @events = Event.includes(:event_image, :event_files, :availability) + @events = Event.includes(:event_image, :event_files, :availability, :categories) .where('availabilities.start_at >= ?', Time.now) .order('availabilities.start_at ASC').references(:availabilities).limit(limit) end diff --git a/app/controllers/api/members_controller.rb b/app/controllers/api/members_controller.rb index d47dac1779..103a83f5fb 100644 --- a/app/controllers/api/members_controller.rb +++ b/app/controllers/api/members_controller.rb @@ -13,7 +13,7 @@ def index end def last_subscribed - @members = User.active.with_role(:member).includes(:profile).where('is_allow_contact = true AND confirmed_at IS NOT NULL').order('created_at desc').limit(params[:last]) + @members = User.active.with_role(:member).includes(profile: [:user_avatar]).where('is_allow_contact = true AND confirmed_at IS NOT NULL').order('created_at desc').limit(params[:last]) @requested_attributes = ['profile'] render :index end diff --git a/app/views/api/events/upcoming.json.jbuilder b/app/views/api/events/upcoming.json.jbuilder index 97d32bc8c3..4d97b39785 100644 --- a/app/views/api/events/upcoming.json.jbuilder +++ b/app/views/api/events/upcoming.json.jbuilder @@ -1,6 +1,7 @@ -json.array!(@events) do |event| - json.partial! 'api/events/event', event: event - json.event_image_medium event.event_image.attachment.medium.url if event.event_image - json.url event_url(event, format: :json) +json.cache! @events do + json.array!(@events) do |event| + json.partial! 'api/events/event', event: event + json.event_image_medium event.event_image.attachment.medium.url if event.event_image + json.url event_url(event, format: :json) + end end - From b4773532866e345d112541a1e1674f5333ed4a2f Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 27 Jun 2016 12:37:44 +0200 Subject: [PATCH 20/30] cancel cache for plant and training_prcings --- app/views/api/plans/index.json.jbuilder | 10 ++++------ app/views/api/trainings_pricings/index.json.jbuilder | 4 +--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/views/api/plans/index.json.jbuilder b/app/views/api/plans/index.json.jbuilder index 46e7fb9bdb..fe6c2f366d 100644 --- a/app/views/api/plans/index.json.jbuilder +++ b/app/views/api/plans/index.json.jbuilder @@ -1,7 +1,5 @@ -json.cache! @plans do - json.array!(@plans) do |plan| - json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :description, :type, :ui_weight - json.amount (plan.amount / 100.00) - json.plan_file_url plan.plan_file.attachment_url if plan.plan_file - end +json.array!(@plans) do |plan| + json.extract! plan, :id, :base_name, :name, :interval, :interval_count, :group_id, :training_credit_nb, :description, :type, :ui_weight + json.amount (plan.amount / 100.00) + json.plan_file_url plan.plan_file.attachment_url if plan.plan_file end diff --git a/app/views/api/trainings_pricings/index.json.jbuilder b/app/views/api/trainings_pricings/index.json.jbuilder index 5ff1f0d04a..a091263a6f 100644 --- a/app/views/api/trainings_pricings/index.json.jbuilder +++ b/app/views/api/trainings_pricings/index.json.jbuilder @@ -1,3 +1 @@ -json.cache! @trainings_pricings do - json.partial! 'api/trainings_pricings/trainings_pricing', collection: @trainings_pricings, as: :trainings_pricing -end +json.partial! 'api/trainings_pricings/trainings_pricing', collection: @trainings_pricings, as: :trainings_pricing From 07f231cb1fc0ecc0349ca440d2f7b1e8ccc75cbd Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 27 Jun 2016 12:41:54 +0200 Subject: [PATCH 21/30] update changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbfebd902e..38e9ee69e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog Fab Manager +- add json cache for machines, events, trainings +- optimise sql query, avoid to N+1 + ## v2.2.0 2016 June 16 - Built-in support for extensions plug-ins - User profile form: social networks links, personal website link, job and change profile visibility (public / private) @@ -30,4 +33,4 @@ - Fix a bug: custom asset favicon-file favicon file is not set - Fix a security issue: stripe card token is now checked on server side on new/renew subscription - Translated notification e-mails into english language -- Subscription extension logic has been extracted into a microservice \ No newline at end of file +- Subscription extension logic has been extracted into a microservice From 4eea18a479c430fd38126622048b27074ab90a8e Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 27 Jun 2016 12:49:20 +0200 Subject: [PATCH 22/30] fix merge --- app/controllers/api/members_controller.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/controllers/api/members_controller.rb b/app/controllers/api/members_controller.rb index 5cc24f65b4..b234b0d5c7 100644 --- a/app/controllers/api/members_controller.rb +++ b/app/controllers/api/members_controller.rb @@ -17,16 +17,12 @@ def index end def last_subscribed -<<<<<<< HEAD - @members = User.active.with_role(:member).includes(profile: [:user_avatar]).where('is_allow_contact = true AND confirmed_at IS NOT NULL').order('created_at desc').limit(params[:last]) -======= - @query = User.active.with_role(:member).includes(:profile).where('is_allow_contact = true AND confirmed_at IS NOT NULL').order('created_at desc').limit(params[:last]) + @query = User.active.with_role(:member).includes(profile: [:user_avatar]).where('is_allow_contact = true AND confirmed_at IS NOT NULL').order('created_at desc').limit(params[:last]) # remove unmerged profiles from list @members = @query.to_a @members.delete_if { |u| u.need_completion? } ->>>>>>> 9ab28c1df60ab3b3a06b76025dba26d6151aa0ba @requested_attributes = ['profile'] render :index end From a4fcede6385a5275482fd7d82672d61eaf2651f8 Mon Sep 17 00:00:00 2001 From: Peng DU Date: Mon, 27 Jun 2016 13:38:50 +0200 Subject: [PATCH 23/30] fix bug: cant get member training availability --- app/controllers/api/availabilities_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/availabilities_controller.rb b/app/controllers/api/availabilities_controller.rb index 177e1f16fe..aa27c54207 100644 --- a/app/controllers/api/availabilities_controller.rb +++ b/app/controllers/api/availabilities_controller.rb @@ -84,7 +84,7 @@ def trainings else end_at = 1.month.since end_at = 3.months.since if can_show_slot_plus_three_months(@user) - @availabilities = Availability.includes(:tags, :slots, trainings: [:machines]).trainings.where('start_at > ? AND start_at < ?', Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) + @availabilities = Availability.includes(:tags, :slots, trainings: [:machines]).trainings.where('availabilities.start_at > ? AND availabilities.start_at < ?', Time.now, end_at).where('availability_tags.tag_id' => @user.tag_ids.concat([nil])) end @availabilities.each do |a| a = verify_training_is_reserved(a, @reservations) From 8e07460deb618fd64839c1eaace15906dedfcc3f Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 15:34:08 +0200 Subject: [PATCH 24/30] [feature] redirect to slugged-url on publish project --- app/assets/javascripts/controllers/projects.coffee.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/controllers/projects.coffee.erb b/app/assets/javascripts/controllers/projects.coffee.erb index f17b24051e..93a6dc5868 100644 --- a/app/assets/javascripts/controllers/projects.coffee.erb +++ b/app/assets/javascripts/controllers/projects.coffee.erb @@ -70,7 +70,7 @@ class ProjectsController $('section[ui-view=main]').scrollTop(0, 200) return else - $state.go('app.public.projects_show', {id: content.id}) + $state.go('app.public.projects_show', {id: content.slug}) From 8c90eab8ace0fe808a3fe0a196cd0d2339fe4d11 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 16:42:27 +0200 Subject: [PATCH 25/30] updated changelog with TODO DEPLOY --- .gitignore | 1 + CHANGELOG.md | 9 +++++---- db/schema.rb | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index bd7da4d2de..c3f1dd98e1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ /public/uploads /public/assets +/public/api # Ignore application configurations /config/application.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d8d2cbc2..1cb13089ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,11 @@ ## next release -- add json cache for machines, events, trainings -- optimise sql query, avoid to N+1 -- fix some reservations was referencing reservable not present in database -[TODO DEPLOY] run `bundle exec rake fablab:fix:reservations_not_existing_reservable` to fix it +- Add json cache for machines, events, trainings +- Optimise sql query, avoid to N+1 +- Fix a bug: some reservations was referencing reservable not present in database (#patch) +- [TODO DEPLOY] `bundle exec rake fablab:fix:reservations_not_existing_reservable` to apply #patch +- [TODO DEPLOY] `bundle install` and `rake db:migrate` ## v2.2.2 2016 June 23 - Fix some bugs: users with uncompleted account (sso imported) won't appear in statistics, in listings and in searches. Moreover, they won't block statistics generation diff --git a/db/schema.rb b/db/schema.rb index 470636d5bb..90d0dc0d78 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160526102307) do +ActiveRecord::Schema.define(version: 20160613093842) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" From fb73fc23dee05b9ac8d121599ac50831594a3324 Mon Sep 17 00:00:00 2001 From: cyril Date: Mon, 27 Jun 2016 17:08:18 +0200 Subject: [PATCH 26/30] fix ui:list openapi --- app/assets/templates/admin/open_api_clients/index.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/templates/admin/open_api_clients/index.html.erb b/app/assets/templates/admin/open_api_clients/index.html.erb index d1ce762db0..0d31395aa4 100644 --- a/app/assets/templates/admin/open_api_clients/index.html.erb +++ b/app/assets/templates/admin/open_api_clients/index.html.erb @@ -60,8 +60,8 @@ {{ 'reset_token' | translate }} - From f6f539d2fc28806f59ab08d8240810614c3931b1 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 17:23:05 +0200 Subject: [PATCH 27/30] link to openAPI documentation in front interface --- app/assets/stylesheets/app.utilities.scss | 5 +++++ .../templates/admin/open_api_clients/index.html.erb | 11 ++++++++++- config/locales/app.admin.en.yml | 1 + config/locales/app.admin.fr.yml | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/app.utilities.scss b/app/assets/stylesheets/app.utilities.scss index ddf73f9f59..b8c0e6aa53 100644 --- a/app/assets/stylesheets/app.utilities.scss +++ b/app/assets/stylesheets/app.utilities.scss @@ -335,6 +335,11 @@ p, .widget p { } } +.exponent { + font-size: 0.7em; + vertical-align: super +} + @media screen and (min-width: $screen-lg-min) { .b-r-lg {border-right: 1px solid $border-color; } } diff --git a/app/assets/templates/admin/open_api_clients/index.html.erb b/app/assets/templates/admin/open_api_clients/index.html.erb index d1ce762db0..7731ba0aec 100644 --- a/app/assets/templates/admin/open_api_clients/index.html.erb +++ b/app/assets/templates/admin/open_api_clients/index.html.erb @@ -5,12 +5,21 @@ -
+

{{ 'open_api_clients' }}

+
diff --git a/config/locales/app.admin.en.yml b/config/locales/app.admin.en.yml index aefc82caaf..5c668f4600 100644 --- a/config/locales/app.admin.en.yml +++ b/config/locales/app.admin.en.yml @@ -471,6 +471,7 @@ en: open_api_clients: add_new_client: "Create new API client" + api_documentation: "API documentation" open_api_clients: "OpenAPI clients" calls_count: "calls count" created_at: "Creation date" diff --git a/config/locales/app.admin.fr.yml b/config/locales/app.admin.fr.yml index d9013ad447..ce66544db0 100644 --- a/config/locales/app.admin.fr.yml +++ b/config/locales/app.admin.fr.yml @@ -471,6 +471,7 @@ fr: open_api_clients: add_new_client: "Créer un compte client" + api_documentation: "Documentation de l'API" open_api_clients: "Clients OpenAPI" calls_count: "Nombre d'appels" created_at: "Date de création" From 6df3c90da2c2e5bac60068a3068a87b12f2250f9 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Mon, 27 Jun 2016 18:08:19 +0200 Subject: [PATCH 28/30] fix api doc link --- app/assets/templates/admin/open_api_clients/index.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/templates/admin/open_api_clients/index.html.erb b/app/assets/templates/admin/open_api_clients/index.html.erb index 2a636651d8..d478f5c9b0 100644 --- a/app/assets/templates/admin/open_api_clients/index.html.erb +++ b/app/assets/templates/admin/open_api_clients/index.html.erb @@ -13,7 +13,7 @@
- +   {{ 'api_documentation' }}  From d5b06d08ad2326bfc4e9119815dd49eee183bc1e Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 28 Jun 2016 10:02:36 +0200 Subject: [PATCH 29/30] [feature] confirmation on project deletion --- .../javascripts/controllers/projects.coffee.erb | 14 ++++++++++---- config/locales/app.public.en.yml | 1 + config/locales/app.public.fr.yml | 1 + db/schema.rb | 6 +++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/controllers/projects.coffee.erb b/app/assets/javascripts/controllers/projects.coffee.erb index 93a6dc5868..07d3867023 100644 --- a/app/assets/javascripts/controllers/projects.coffee.erb +++ b/app/assets/javascripts/controllers/projects.coffee.erb @@ -338,8 +338,8 @@ Application.Controllers.controller "EditProjectController", ["$scope", "$state", ## # Controller used in the public project's details page ## -Application.Controllers.controller "ShowProjectController", ["$scope", "$state", "projectPromise", '$location', '$uibModal', '_t' -, ($scope, $state, projectPromise, $location, $uibModal, _t) -> +Application.Controllers.controller "ShowProjectController", ["$scope", "$state", "projectPromise", '$location', '$uibModal', 'dialogs', '_t' +, ($scope, $state, projectPromise, $location, $uibModal, dialogs, _t) -> ### PUBLIC SCOPE ### @@ -383,8 +383,14 @@ Application.Controllers.controller "ShowProjectController", ["$scope", "$state", # check the permissions if $scope.currentUser.role is 'admin' or $scope.projectDeletableBy($scope.currentUser) # delete the project then refresh the projects list - $scope.project.$delete -> - $state.go('app.public.projects_list', {}, {reload: true}) + dialogs.confirm + resolve: + object: -> + title: _t('confirmation_required') + msg: _t('do_you_really_want_to_delete_this_project') + , -> # cancel confirmed + $scope.project.$delete -> + $state.go('app.public.projects_list', {}, {reload: true}) else console.error _t('unauthorized_operation') diff --git a/config/locales/app.public.en.yml b/config/locales/app.public.en.yml index 96406d8aba..a68216ce71 100644 --- a/config/locales/app.public.en.yml +++ b/config/locales/app.public.en.yml @@ -156,6 +156,7 @@ en: tell_us_why_this_looks_abusive: "Tell us why this looks abusive" message_is_required: "Message is required." report: "Report" + do_you_really_want_to_delete_this_project: "Do you really want to delete this project?" machines_list: # list of machines diff --git a/config/locales/app.public.fr.yml b/config/locales/app.public.fr.yml index ca55c24cd6..7cfa703094 100644 --- a/config/locales/app.public.fr.yml +++ b/config/locales/app.public.fr.yml @@ -156,6 +156,7 @@ fr: tell_us_why_this_looks_abusive: "Dites nous en quoi cela vous semble abusif" message_is_required: "Le message est requis." report: "Signaler" + do_you_really_want_to_delete_this_project: "Êtes-vous sur de vouloir supprimer ce projet ?" machines_list: # liste des machines diff --git a/db/schema.rb b/db/schema.rb index 90d0dc0d78..09055dee93 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -227,7 +227,7 @@ t.boolean "is_read", default: false t.datetime "created_at" t.datetime "updated_at" - t.string "receiver_type", limit: 255 + t.string "receiver_type" t.boolean "is_send", default: false t.jsonb "meta_data", default: {} end @@ -456,8 +456,8 @@ t.datetime "updated_at" t.integer "availability_id" t.datetime "ex_start_at" - t.datetime "canceled_at" t.datetime "ex_end_at" + t.datetime "canceled_at" t.boolean "offered", default: false end @@ -615,7 +615,6 @@ add_index "user_trainings", ["user_id"], name: "index_user_trainings_on_user_id", using: :btree create_table "users", force: :cascade do |t| - t.string "username", limit: 255 t.string "email", limit: 255, default: "", null: false t.string "encrypted_password", limit: 255, default: "", null: false t.string "reset_password_token", limit: 255 @@ -638,6 +637,7 @@ t.boolean "is_allow_contact", default: true t.integer "group_id" t.string "stp_customer_id", limit: 255 + t.string "username", limit: 255 t.string "slug", limit: 255 t.boolean "is_active", default: true t.boolean "invoicing_disabled", default: false From 7126843edad2234e7df6a77c0e2a9c6a412e1d41 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 28 Jun 2016 10:23:39 +0200 Subject: [PATCH 30/30] updated changelog for 2.3.0 --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb13089ec..e493ecec10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ # Changelog Fab Manager -## next release +## v2.3.0 2016 June 28 +- Public API with access management and online documentation - Add json cache for machines, events, trainings - Optimise sql query, avoid to N+1 -- Fix a bug: some reservations was referencing reservable not present in database (#patch) +- Projects URL are always composed with slug instead of ID +- Confirmation on project deletion +- Fix a bug: unable to deploy 2.2.0+ when PostgreSQL 'unaccent' extension was already active +- Fix a bug: some reservations was referencing reservables not present in database (#patch) - [TODO DEPLOY] `bundle exec rake fablab:fix:reservations_not_existing_reservable` to apply #patch - [TODO DEPLOY] `bundle install` and `rake db:migrate`