From 6ea073747ee13a2c795110b0339eda7a3c8319c1 Mon Sep 17 00:00:00 2001 From: Khemissi Amir <amir.khemissi@insat.ucar.tn> Date: Tue, 4 Apr 2023 17:04:37 +0100 Subject: [PATCH] Fixes to complete the relative URL root path support. (#5106) * Action cable: Added support for relative URL root path. * Meeting start: Added support for relative URL root path. * Application: Minor improvements to relative URL root path parsing. * Email branding: Added support for relative URL root path. + Refactored and improved Setting getter service. * Manage users: Fixed edit user on custom relative URL root path. --- app/controllers/api/v1/meetings_controller.rb | 4 +- .../api/v1/site_settings_controller.rb | 10 +--- app/javascript/channels/consumer.jsx | 2 +- .../hooks/queries/users/useUser.jsx | 4 +- app/mailers/user_mailer.rb | 3 +- app/services/meeting_starter.rb | 2 +- app/services/setting_getter.rb | 53 +++++++++++-------- config/application.rb | 4 +- esbuild.dev.mjs | 3 +- esbuild.mjs | 3 +- spec/controllers/meetings_controller_spec.rb | 4 +- .../site_settings_controller_spec.rb | 10 ++-- 12 files changed, 53 insertions(+), 49 deletions(-) diff --git a/app/controllers/api/v1/meetings_controller.rb b/app/controllers/api/v1/meetings_controller.rb index ef823e07..3b5126fa 100644 --- a/app/controllers/api/v1/meetings_controller.rb +++ b/app/controllers/api/v1/meetings_controller.rb @@ -29,7 +29,7 @@ module Api # Starts a BigBlueButton meetings and joins in the meeting starter def start begin - MeetingStarter.new(room: @room, base_url: root_url, current_user:, provider: current_provider).call + MeetingStarter.new(room: @room, base_url: request.base_url, current_user:, provider: current_provider).call rescue BigBlueButton::BigBlueButtonException => e return render_error status: :bad_request unless e.key == 'idNotUnique' end @@ -68,7 +68,7 @@ module Api if !data[:status] && settings['glAnyoneCanStart'] == 'true' # Meeting isnt running and anyoneCanStart setting is enabled begin - MeetingStarter.new(room: @room, base_url: root_url, current_user:, provider: current_provider).call + MeetingStarter.new(room: @room, base_url: request.base_url, current_user:, provider: current_provider).call rescue BigBlueButton::BigBlueButtonException => e return render_error status: :bad_request unless e.key == 'idNotUnique' end diff --git a/app/controllers/api/v1/site_settings_controller.rb b/app/controllers/api/v1/site_settings_controller.rb index 8bf06d0d..ecbd9472 100644 --- a/app/controllers/api/v1/site_settings_controller.rb +++ b/app/controllers/api/v1/site_settings_controller.rb @@ -25,17 +25,9 @@ module Api # GET /api/v1/site_settings # Returns the values of 1 or multiple site_settings that are not forbidden to access def index - settings = {} return render_error status: :forbidden if forbidden_settings(params[:names]) - if params[:names].is_a?(Array) - params[:names].each do |name| - settings[name] = SettingGetter.new(setting_name: name, provider: current_provider).call - end - else - # return the value directly - settings = SettingGetter.new(setting_name: params[:names], provider: current_provider).call - end + settings = SettingGetter.new(setting_name: params[:names], provider: current_provider).call render_data data: settings, status: :ok end diff --git a/app/javascript/channels/consumer.jsx b/app/javascript/channels/consumer.jsx index 99123c20..e1d523c5 100644 --- a/app/javascript/channels/consumer.jsx +++ b/app/javascript/channels/consumer.jsx @@ -19,4 +19,4 @@ import { createConsumer } from '@rails/actioncable'; -export default createConsumer(); +export default createConsumer(`${process.env.RELATIVE_URL_ROOT}/cable`); diff --git a/app/javascript/hooks/queries/users/useUser.jsx b/app/javascript/hooks/queries/users/useUser.jsx index 64f22960..54c460bb 100644 --- a/app/javascript/hooks/queries/users/useUser.jsx +++ b/app/javascript/hooks/queries/users/useUser.jsx @@ -15,11 +15,11 @@ // with Greenlight; if not, see <http://www.gnu.org/licenses/>. import { useQuery } from 'react-query'; -import axios from 'axios'; +import axios from '../../../helpers/Axios'; export default function useUser(userId) { return useQuery( ['getUser', userId], - () => axios.get(`/api/v1/users/${userId}.json`).then((resp) => resp.data.data), + () => axios.get(`/users/${userId}.json`).then((resp) => resp.data.data), ); } diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 2df77808..3e00b6fc 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -55,8 +55,7 @@ class UserMailer < ApplicationMailer end def branding - branding_hash = SiteSetting.includes(:setting).where(provider: @provider, settings: { name: %w[PrimaryColor BrandingImage] }) - .pluck(:name, :value).to_h + branding_hash = SettingGetter.new(setting_name: %w[PrimaryColor BrandingImage], provider: @provider).call @brand_image = ActionController::Base.helpers.image_url(branding_hash['BrandingImage'], host: @base_url) @brand_color = branding_hash['PrimaryColor'] end diff --git a/app/services/meeting_starter.rb b/app/services/meeting_starter.rb index e576082c..4575aef4 100644 --- a/app/services/meeting_starter.rb +++ b/app/services/meeting_starter.rb @@ -56,7 +56,7 @@ class MeetingStarter private def computed_options(access_code:) - room_url = File.join(@base_url, '/rooms/', @room.friendly_id, '/join') + room_url = "#{root_url(host: @base_url)}rooms/#{@room.friendly_id}/join" moderator_message = "#{I18n.t('meeting.moderator_message')}<br>#{room_url}" moderator_message += "<br>#{I18n.t('meeting.access_code', code: access_code)}" if access_code.present? { diff --git a/app/services/setting_getter.rb b/app/services/setting_getter.rb index 4c5f4352..fda7f838 100644 --- a/app/services/setting_getter.rb +++ b/app/services/setting_getter.rb @@ -20,40 +20,51 @@ class SettingGetter include Rails.application.routes.url_helpers def initialize(setting_name:, provider:) - @setting_name = setting_name + @setting_name = Array(setting_name) @provider = provider end def call - setting = SiteSetting.joins(:setting) - .find_by( - provider: @provider, - setting: { name: @setting_name } - ) - - value = if @setting_name == 'BrandingImage' - if setting.image.attached? - rails_blob_path setting.image, only_path: true - else - ActionController::Base.helpers.image_path('bbb_logo.png') - end - else - setting&.value - end - - transform_value(value) + # Fetch the site settings records while eager loading their respective settings ↓ + site_settings = SiteSetting.includes(:setting) + .where( + provider: @provider, + setting: { name: @setting_name } + ) + + # Pessimist check: Pass only if all provided names were found ↓ + return nil unless @setting_name.size == site_settings.size + + site_settings_hash = {} + + # In memory prepare the result hash ↓ + site_settings.map do |site_setting| + site_settings_hash[site_setting.setting.name] = transform_value(site_setting) + end + + # If there's only one setting is being fetched no need for a hash ↓ + return site_settings_hash.values.first if site_settings_hash.size == 1 + + # A Hash<setting_name => parsed_value> is returned otherwise ↓ + site_settings_hash end private - def transform_value(value) - case value + def transform_value(site_setting) + if site_setting.setting.name == 'BrandingImage' + return rails_blob_path site_setting.image, only_path: true if site_setting.image.attached? + + return ActionController::Base.helpers.image_path('bbb_logo.png') + end + + case site_setting.value when 'true' true when 'false' false else - value + site_setting.value end end end diff --git a/config/application.rb b/config/application.rb index 3c50cf78..71810992 100644 --- a/config/application.rb +++ b/config/application.rb @@ -66,6 +66,8 @@ module Greenlight config.bigbluebutton_secret = ENV.fetch('BIGBLUEBUTTON_SECRET', '8cd8ef52e8e101574e400365b55e11a6') - config.relative_url_root = ENV.fetch('RELATIVE_URL_ROOT', '/') + # Fetch 'RELATIVE_URL_ROOT' ENV variable value while removing any trailing slashes. + config.relative_url_root = ENV.fetch('RELATIVE_URL_ROOT', nil)&.sub(%r{/*\z}, '') + config.relative_url_root = '/' if config.relative_url_root.blank? end end diff --git a/esbuild.dev.mjs b/esbuild.dev.mjs index fb48838b..83a11e9c 100644 --- a/esbuild.dev.mjs +++ b/esbuild.dev.mjs @@ -1,6 +1,7 @@ import * as esbuild from 'esbuild'; -const relativeUrlRoot = (process.env.RELATIVE_URL_ROOT || '').replace(/\/$/, ''); +// Fetch 'RELATIVE_URL_ROOT' ENV variable value while removing any trailing slashes. +const relativeUrlRoot = (process.env.RELATIVE_URL_ROOT || '').replace(/\/*$/, ''); await esbuild.build({ entryPoints: ['app/javascript/main.jsx'], diff --git a/esbuild.mjs b/esbuild.mjs index 7b76ffbf..8a658959 100644 --- a/esbuild.mjs +++ b/esbuild.mjs @@ -1,6 +1,7 @@ import * as esbuild from 'esbuild'; -const relativeUrlRoot = (process.env.RELATIVE_URL_ROOT || '').replace(/\/$/, ''); +// Fetch 'RELATIVE_URL_ROOT' ENV variable value while removing any trailing slashes. +const relativeUrlRoot = (process.env.RELATIVE_URL_ROOT || '').replace(/\/*$/, ''); await esbuild.build({ entryPoints: ['app/javascript/main.jsx'], diff --git a/spec/controllers/meetings_controller_spec.rb b/spec/controllers/meetings_controller_spec.rb index 66827d38..21230bb1 100644 --- a/spec/controllers/meetings_controller_spec.rb +++ b/spec/controllers/meetings_controller_spec.rb @@ -41,7 +41,7 @@ RSpec.describe Api::V1::MeetingsController, type: :controller do describe '#start' do it 'makes a call to the MeetingStarter service with the right values and returns the join url' do - expect(MeetingStarter).to receive(:new).with(room:, base_url: root_url, current_user: user, provider: 'greenlight').and_call_original + expect(MeetingStarter).to receive(:new).with(room:, base_url: request.base_url, current_user: user, provider: 'greenlight').and_call_original expect_any_instance_of(MeetingStarter).to receive(:call) expect_any_instance_of(BigBlueButtonApi).to receive(:join_meeting).with(room:, name: user.name, avatar_url: nil, role: 'Moderator') @@ -343,7 +343,7 @@ RSpec.describe Api::V1::MeetingsController, type: :controller do allow_any_instance_of(BigBlueButtonApi).to receive(:meeting_running?).and_return(false) - expect(MeetingStarter).to receive(:new).with(room:, base_url: root_url, current_user: user, provider: 'greenlight').and_call_original + expect(MeetingStarter).to receive(:new).with(room:, base_url: request.base_url, current_user: user, provider: 'greenlight').and_call_original expect_any_instance_of(MeetingStarter).to receive(:call) post :status, params: { friendly_id: room.friendly_id, name: user.name } diff --git a/spec/controllers/site_settings_controller_spec.rb b/spec/controllers/site_settings_controller_spec.rb index f049573d..8b2faf14 100644 --- a/spec/controllers/site_settings_controller_spec.rb +++ b/spec/controllers/site_settings_controller_spec.rb @@ -38,15 +38,13 @@ RSpec.describe Api::V1::SiteSettingsController, type: :controller do end it 'calls SettingGetter and returns multiple values' do - expect(SettingGetter).to receive(:new).with(setting_name: 'SettingName', provider: 'greenlight').and_call_original - expect(SettingGetter).to receive(:new).with(setting_name: 'SettingName2', provider: 'greenlight').and_call_original - allow_any_instance_of(SettingGetter).to receive(:call).and_return('false') + expect(SettingGetter).to receive(:new).with(setting_name: %w[Uno Dos Tres], provider: 'greenlight').and_call_original + allow_any_instance_of(SettingGetter).to receive(:call).and_return({ 'Uno' => 1, 'Dos' => 2, 'Tres' => 3 }) - get :index, params: { names: %w[SettingName SettingName2] } + get :index, params: { names: %w[Uno Dos Tres] } expect(response).to have_http_status(:ok) - expect(JSON.parse(response.body)['data']['SettingName']).to eq('false') - expect(JSON.parse(response.body)['data']['SettingName2']).to eq('false') + expect(JSON.parse(response.body)['data']).to eq({ 'Uno' => 1, 'Dos' => 2, 'Tres' => 3 }) end it 'returns forbidden if trying to access a forbidden setting' do -- GitLab