diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index d5b1281bf..b081978b9 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -1,4 +1,6 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController + include ::FileTypeHelper + before_action :fetch_portal, except: [:index, :create] before_action :check_authorization @@ -14,11 +16,21 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController def show; end def create - @portal = Current.account.portals.create!(portal_params) + @portal = Current.account.portals.build(portal_params) + render json: { error: @portal.errors.messages }, status: :unprocessable_entity and return unless @portal.valid? + + @portal.save! + process_attached_logo end def update - @portal.update!(portal_params) + ActiveRecord::Base.transaction do + @portal.update!(portal_params) if params[:portal].present? + process_attached_logo + rescue StandardError => e + Rails.logger.error e + render json: { error: @portal.errors.messages }.to_json, status: :unprocessable_entity + end end def destroy @@ -31,6 +43,10 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController head :ok end + def process_attached_logo + @portal.logo.attach(params[:logo]) + end + private def fetch_portal @@ -43,7 +59,7 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController def portal_params params.require(:portal).permit( - :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived + :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived, config: { allowed_locales: [] } ) end diff --git a/app/models/category.rb b/app/models/category.rb index a3955ae91..beecebd71 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -61,6 +61,7 @@ class Category < ApplicationRecord validates :account_id, presence: true validates :slug, presence: true validates :name, presence: true + validate :allowed_locales validates :locale, uniqueness: { scope: %i[slug portal_id], message: 'should be unique in the category and portal' } accepts_nested_attributes_for :related_categories @@ -80,4 +81,14 @@ class Category < ApplicationRecord def ensure_account_id self.account_id = portal&.account_id end + + def allowed_locales + return if portal.blank? + + allowed_locales = portal.config['allowed_locales'] + + return true if allowed_locales.include?(locale) + + errors.add(:locale, "#{locale} of category is not part of portal's #{allowed_locales}.") + end end diff --git a/app/models/portal.rb b/app/models/portal.rb index 88fcb957d..19b6cfa31 100644 --- a/app/models/portal.rb +++ b/app/models/portal.rb @@ -21,6 +21,8 @@ # index_portals_on_slug (slug) UNIQUE # class Portal < ApplicationRecord + include Rails.application.routes.url_helpers + belongs_to :account has_many :categories, dependent: :destroy_async has_many :folders, through: :categories @@ -33,12 +35,35 @@ class Portal < ApplicationRecord class_name: :User, dependent: :nullify, source: :user + has_one_attached :logo validates :account_id, presence: true validates :name, presence: true validates :slug, presence: true, uniqueness: true + validate :config_json_format accepts_nested_attributes_for :members scope :active, -> { where(archived: false) } + + CONFIG_JSON_KEYS = %w[allowed_locales].freeze + + def file_base_data + { + id: logo.id, + portal_id: id, + file_type: logo.content_type, + account_id: account_id, + file_url: url_for(logo), + blob_id: logo.blob_id, + filename: logo.filename.to_s + } + end + + private + + def config_json_format + denied_keys = config.keys - CONFIG_JSON_KEYS + errors.add(:cofig, "in portal on #{denied_keys.join(',')} is not supported.") if denied_keys.any? + end end diff --git a/app/views/api/v1/accounts/portals/_portal.json.jbuilder b/app/views/api/v1/accounts/portals/_portal.json.jbuilder index 79a1e4806..12fcde593 100644 --- a/app/views/api/v1/accounts/portals/_portal.json.jbuilder +++ b/app/views/api/v1/accounts/portals/_portal.json.jbuilder @@ -8,6 +8,7 @@ json.page_title portal.page_title json.slug portal.slug json.archived portal.archived json.config portal.config +json.logo portal.file_base_data if portal.logo.present? json.portal_members do if portal.members.any? diff --git a/spec/controllers/api/v1/accounts/categories_controller_spec.rb b/spec/controllers/api/v1/accounts/categories_controller_spec.rb index 91992c7c8..05b980674 100644 --- a/spec/controllers/api/v1/accounts/categories_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/categories_controller_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Api::V1::Accounts::Categories', type: :request do let(:account) { create(:account) } let(:agent) { create(:user, account: account, role: :agent) } - let!(:portal) { create(:portal, name: 'test_portal', account_id: account.id) } + let!(:portal) { create(:portal, name: 'test_portal', account_id: account.id, config: { allowed_locales: %w[en es] }) } let!(:category) { create(:category, name: 'category', portal: portal, account_id: account.id, slug: 'category_slug') } let!(:category_to_associate) do create(:category, name: 'associated category', portal: portal, account_id: account.id, slug: 'associated_category_slug') diff --git a/spec/controllers/api/v1/accounts/portals_controller_spec.rb b/spec/controllers/api/v1/accounts/portals_controller_spec.rb index 7bde7e3a1..bdca10a68 100644 --- a/spec/controllers/api/v1/accounts/portals_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/portals_controller_spec.rb @@ -67,11 +67,14 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do context 'when it is an authenticated user' do it 'creates portal' do + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + portal_params = { portal: { name: 'test_portal', slug: 'test_kbase' - } + }, + logo: file } post "/api/v1/accounts/#{account.id}/portals", params: portal_params, @@ -80,6 +83,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['name']).to eql('test_portal') + expect(json_response['logo']['filename']).to eql('avatar.png') end end end @@ -97,7 +101,8 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do it 'updates portal' do portal_params = { portal: { - name: 'updated_test_portal' + name: 'updated_test_portal', + config: { 'allowed_locales' => %w[en es] } } } @@ -110,6 +115,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['name']).to eql(portal_params[:portal][:name]) + expect(json_response['config']).to eql({ 'allowed_locales' => %w[en es] }) end it 'archive portal' do diff --git a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb index 894a5e995..f5509cf28 100644 --- a/spec/controllers/public/api/v1/portals/articles_controller_spec.rb +++ b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' RSpec.describe 'Public Articles API', type: :request do let!(:account) { create(:account) } let(:agent) { create(:user, account: account, role: :agent) } - let!(:portal) { create(:portal, slug: 'test-portal') } + let!(:portal) { create(:portal, slug: 'test-portal', config: { allowed_locales: %w[en es] }) } let!(:category) { create(:category, name: 'category', portal: portal, account_id: account.id, locale: 'en', slug: 'category_slug') } let!(:category_2) { create(:category, name: 'category', portal: portal, account_id: account.id, locale: 'es', slug: 'category_2_slug') } let!(:article) { create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id) } diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index f018e277d..bcc928214 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -18,8 +18,8 @@ RSpec.describe Article, type: :model do describe 'search' do let!(:account) { create(:account) } let(:user) { create(:user, account_ids: [account.id], role: :agent) } - let!(:portal_1) { create(:portal, account_id: account.id) } - let!(:portal_2) { create(:portal, account_id: account.id) } + let!(:portal_1) { create(:portal, account_id: account.id, config: { allowed_locales: %w[en es] }) } + let!(:portal_2) { create(:portal, account_id: account.id, config: { allowed_locales: %w[en es] }) } let!(:category_1) { create(:category, slug: 'category_1', locale: 'en', portal_id: portal_1.id) } let!(:category_2) { create(:category, slug: 'category_2', locale: 'es', portal_id: portal_1.id) } let!(:category_3) { create(:category, slug: 'category_3', locale: 'es', portal_id: portal_2.id) } diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index fd3da0778..df4392398 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -15,11 +15,24 @@ RSpec.describe Category, type: :model do it { is_expected.to have_many(:related_categories) } end + describe 'validations' do + let!(:account) { create(:account) } + let(:user) { create(:user, account_ids: [account.id], role: :agent) } + let!(:portal) { create(:portal, account_id: account.id, config: { allowed_locales: ['en'] }) } + + it 'returns erros when locale is not allowed in the portal' do + category = create(:category, slug: 'category_1', locale: 'en', portal_id: portal.id) + expect(category).to be_valid + category.update(locale: 'es') + expect(category.errors.full_messages[0]).to eq("Locale es of category is not part of portal's [\"en\"].") + end + end + describe 'search' do let!(:account) { create(:account) } let(:user) { create(:user, account_ids: [account.id], role: :agent) } - let!(:portal_1) { create(:portal, account_id: account.id) } - let!(:portal_2) { create(:portal, account_id: account.id) } + let!(:portal_1) { create(:portal, account_id: account.id, config: { allowed_locales: %w[en es] }) } + let!(:portal_2) { create(:portal, account_id: account.id, config: { allowed_locales: %w[en es] }) } before do create(:category, slug: 'category_1', locale: 'en', portal_id: portal_1.id) diff --git a/spec/models/portal_spec.rb b/spec/models/portal_spec.rb index e702646ca..20fc2baba 100644 --- a/spec/models/portal_spec.rb +++ b/spec/models/portal_spec.rb @@ -15,4 +15,22 @@ RSpec.describe Portal, type: :model do it { is_expected.to have_many(:portal_members) } it { is_expected.to have_many(:members) } end + + describe 'validations' do + let!(:account) { create(:account) } + let!(:portal) { create(:portal, account_id: account.id) } + + context 'when set portal config' do + it 'Adds default allowed_locales en' do + expect(portal.config).to be_present + expect(portal.config['allowed_locales']).to eq(['en']) + end + + it 'Does not allow any other config than allowed_locales' do + portal.update(config: { 'some_other_key': 'test_value' }) + expect(portal).not_to be_valid + expect(portal.errors.full_messages[0]).to eq('Cofig in portal on some_other_key is not supported.') + end + end + end end