diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index 232eecd32..0037bc405 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -1,5 +1,6 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController before_action :portal + before_action :check_authorization before_action :fetch_article, except: [:index, :create] def index @@ -9,6 +10,8 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController def create @article = @portal.articles.create!(article_params) + @article.associate_root_article(article_params[:associated_article_id]) + render json: { error: @article.errors.messages }, status: :unprocessable_entity and return unless @article.valid? end def edit; end @@ -36,7 +39,7 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController def article_params params.require(:article).permit( - :title, :content, :description, :position, :category_id, :author_id + :title, :content, :description, :position, :category_id, :author_id, :associated_article_id ) end diff --git a/app/controllers/api/v1/accounts/categories_controller.rb b/app/controllers/api/v1/accounts/categories_controller.rb index 13917701e..f242d3385 100644 --- a/app/controllers/api/v1/accounts/categories_controller.rb +++ b/app/controllers/api/v1/accounts/categories_controller.rb @@ -1,5 +1,6 @@ class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseController before_action :portal + before_action :check_authorization before_action :fetch_category, except: [:index, :create] def index diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index 75ffc35e9..ab4014123 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -1,10 +1,16 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController before_action :fetch_portal, except: [:index, :create] + before_action :check_authorization def index @portals = Current.account.portals end + def add_members + agents = Current.account.agents.where(id: portal_member_params[:member_ids]) + @portal.members << agents + end + def show; end def create @@ -35,4 +41,8 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController :account_id, :color, :custom_domain, :header_text, :homepage_link, :name, :page_title, :slug, :archived ) end + + def portal_member_params + params.require(:portal).permit(:account_id, member_ids: []) + end end diff --git a/app/models/article.rb b/app/models/article.rb index 72bf3b2c4..a8d356dd1 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -2,31 +2,45 @@ # # Table name: articles # -# id :bigint not null, primary key -# content :text -# description :text -# status :integer -# title :string -# views :integer -# created_at :datetime not null -# updated_at :datetime not null -# account_id :integer not null -# author_id :bigint -# category_id :integer -# folder_id :integer -# portal_id :integer not null +# id :bigint not null, primary key +# content :text +# description :text +# status :integer +# title :string +# views :integer +# created_at :datetime not null +# updated_at :datetime not null +# account_id :integer not null +# associated_article_id :bigint +# author_id :bigint +# category_id :integer +# folder_id :integer +# portal_id :integer not null # # Indexes # -# index_articles_on_author_id (author_id) +# index_articles_on_associated_article_id (associated_article_id) +# index_articles_on_author_id (author_id) # # Foreign Keys # +# fk_rails_... (associated_article_id => articles.id) # fk_rails_... (author_id => users.id) # class Article < ApplicationRecord include PgSearch::Model + has_many :associated_articles, + class_name: :Article, + foreign_key: :associated_article_id, + dependent: :nullify, + inverse_of: 'root_article' + + belongs_to :root_article, + class_name: :Article, + foreign_key: :associated_article_id, + inverse_of: :associated_articles, + optional: true belongs_to :account belongs_to :category belongs_to :portal @@ -71,6 +85,21 @@ class Article < ApplicationRecord params[:page] || 1 end + def associate_root_article(associated_article_id) + article = portal.articles.find(associated_article_id) if associated_article_id.present? + + return if article.nil? + + root_article_id = self.class.find_root_article_id(article) + + update(associated_article_id: root_article_id) if root_article_id.present? + end + + # Make sure we always associate the parent's associated id to avoid the deeper associations od articles. + def self.find_root_article_id(article) + article.associated_article_id || article.id + end + private def ensure_account_id diff --git a/app/models/portal.rb b/app/models/portal.rb index 8774820be..eb823d211 100644 --- a/app/models/portal.rb +++ b/app/models/portal.rb @@ -25,9 +25,18 @@ class Portal < ApplicationRecord has_many :categories, dependent: :destroy_async has_many :folders, through: :categories has_many :articles, dependent: :destroy_async - has_many :users, through: :portals_members + has_many :portal_members, + class_name: :PortalMember, + dependent: :destroy_async + has_many :members, + through: :portal_members, + class_name: :User, + dependent: :nullify, + source: :user validates :account_id, presence: true validates :name, presence: true validates :slug, presence: true, uniqueness: true + + accepts_nested_attributes_for :members end diff --git a/app/models/portal_member.rb b/app/models/portal_member.rb new file mode 100644 index 000000000..e0e687ae1 --- /dev/null +++ b/app/models/portal_member.rb @@ -0,0 +1,20 @@ +# == Schema Information +# +# Table name: portal_members +# +# id :bigint not null, primary key +# created_at :datetime not null +# updated_at :datetime not null +# portal_id :bigint +# user_id :bigint +# +# Indexes +# +# index_portal_members_on_portal_id_and_user_id (portal_id,user_id) UNIQUE +# index_portal_members_on_user_id_and_portal_id (user_id,portal_id) UNIQUE +# +class PortalMember < ApplicationRecord + belongs_to :portal, class_name: 'Portal' + belongs_to :user, class_name: 'User' + validates :user_id, uniqueness: { scope: :portal_id } +end diff --git a/app/models/related_category.rb b/app/models/related_category.rb index 018a13418..a7978055f 100644 --- a/app/models/related_category.rb +++ b/app/models/related_category.rb @@ -2,10 +2,10 @@ # # Table name: related_categories # -# id :bigint not null, primary key -# created_at :datetime not null -# updated_at :datetime not null -# category_id :bigint +# id :bigint not null, primary key +# created_at :datetime not null +# updated_at :datetime not null +# category_id :bigint # related_category_id :bigint # # Indexes diff --git a/app/models/user.rb b/app/models/user.rb index 2125f88d2..1da1fb3b7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -89,10 +89,17 @@ class User < ApplicationRecord has_many :notification_settings, dependent: :destroy_async has_many :notification_subscriptions, dependent: :destroy_async has_many :notifications, dependent: :destroy_async - has_many :portals, through: :portals_members has_many :team_members, dependent: :destroy_async has_many :teams, through: :team_members has_many :articles, foreign_key: 'author_id', dependent: :nullify + has_many :portal_members, + class_name: :PortalMember, + dependent: :destroy_async + has_many :portals, + through: :portals_members, + class_name: :Portal, + dependent: :nullify, + source: :portal before_validation :set_password_and_uid, on: :create diff --git a/app/policies/article_policy.rb b/app/policies/article_policy.rb new file mode 100644 index 000000000..1fb6c54da --- /dev/null +++ b/app/policies/article_policy.rb @@ -0,0 +1,31 @@ +class ArticlePolicy < ApplicationPolicy + def index? + @account_user.administrator? || @account.users.include?(@user) + end + + def update? + @account_user.administrator? || portal_member? + end + + def show? + @account_user.administrator? || portal_member? + end + + def edit? + @account_user.administrator? || portal_member? + end + + def create? + @account_user.administrator? || portal_member? + end + + def destroy? + @account_user.administrator? || portal_member? + end + + private + + def portal_member? + @record.first.portal.members.include?(@user) + end +end diff --git a/app/policies/category_policy.rb b/app/policies/category_policy.rb new file mode 100644 index 000000000..017906b55 --- /dev/null +++ b/app/policies/category_policy.rb @@ -0,0 +1,31 @@ +class CategoryPolicy < ApplicationPolicy + def index? + @account_user.administrator? || @account.users.include?(@user) + end + + def update? + @account_user.administrator? || portal_member? + end + + def show? + @account_user.administrator? || portal_member? + end + + def edit? + @account_user.administrator? || portal_member? + end + + def create? + @account_user.administrator? || portal_member? + end + + def destroy? + @account_user.administrator? || portal_member? + end + + private + + def portal_member? + @record.first.portal.members.include?(@user) + end +end diff --git a/app/policies/portal_policy.rb b/app/policies/portal_policy.rb new file mode 100644 index 000000000..a27f0f92f --- /dev/null +++ b/app/policies/portal_policy.rb @@ -0,0 +1,35 @@ +class PortalPolicy < ApplicationPolicy + def index? + @account_user.administrator? || @account.users.include?(@user) + end + + def update? + @account_user.administrator? + end + + def show? + @account_user.administrator? || portal_member? + end + + def edit? + @account_user.administrator? + end + + def create? + @account_user.administrator? + end + + def destroy? + @account_user.administrator? + end + + def add_members? + @account_user.administrator? + end + + private + + def portal_member? + @record.first.members.include?(@user) + end +end diff --git a/app/views/api/v1/accounts/articles/_article.json.jbuilder b/app/views/api/v1/accounts/articles/_article.json.jbuilder index 85ff7c1a0..e3b1af52b 100644 --- a/app/views/api/v1/accounts/articles/_article.json.jbuilder +++ b/app/views/api/v1/accounts/articles/_article.json.jbuilder @@ -11,6 +11,7 @@ if article.portal.present? json.partial! 'api/v1/accounts/portals/portal.json.jbuilder', portal: article.portal end end + json.views article.views if article.author.present? @@ -18,3 +19,11 @@ if article.author.present? json.partial! 'api/v1/models/agent.json.jbuilder', resource: article.author end end + +json.associated_articles do + if article.associated_articles.any? + json.array! article.associated_articles.each do |associated_article| + json.partial! 'api/v1/accounts/articles/associated_article.json.jbuilder', article: associated_article + end + end +end diff --git a/app/views/api/v1/accounts/articles/_associated_article.json.jbuilder b/app/views/api/v1/accounts/articles/_associated_article.json.jbuilder new file mode 100644 index 000000000..4c5439d1a --- /dev/null +++ b/app/views/api/v1/accounts/articles/_associated_article.json.jbuilder @@ -0,0 +1,21 @@ +json.id article.id +json.category_id article.category_id +json.title article.title +json.content article.content +json.description article.description +json.status article.status +json.account_id article.account_id + +if article.portal.present? + json.portal do + json.partial! 'api/v1/accounts/portals/portal.json.jbuilder', portal: article.portal + end +end + +json.views article.views + +if article.author.present? + json.author do + json.partial! 'api/v1/models/agent.json.jbuilder', resource: article.author + 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 1f828d4ad..79a1e4806 100644 --- a/app/views/api/v1/accounts/portals/_portal.json.jbuilder +++ b/app/views/api/v1/accounts/portals/_portal.json.jbuilder @@ -8,3 +8,11 @@ json.page_title portal.page_title json.slug portal.slug json.archived portal.archived json.config portal.config + +json.portal_members do + if portal.members.any? + json.array! portal.members.each do |member| + json.partial! 'api/v1/models/agent.json.jbuilder', resource: member + end + end +end diff --git a/app/views/api/v1/accounts/portals/add_members.json.jbuilder b/app/views/api/v1/accounts/portals/add_members.json.jbuilder new file mode 100644 index 000000000..f4bc72924 --- /dev/null +++ b/app/views/api/v1/accounts/portals/add_members.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'portal', portal: @portal diff --git a/app/views/api/v1/accounts/portals/index.json.jbuilder b/app/views/api/v1/accounts/portals/index.json.jbuilder index 4a7949da1..3e4553382 100644 --- a/app/views/api/v1/accounts/portals/index.json.jbuilder +++ b/app/views/api/v1/accounts/portals/index.json.jbuilder @@ -1 +1,3 @@ -json.array! @portals, partial: 'portal', as: :portal +json.payload do + json.array! @portals, partial: 'portal', as: :portal +end diff --git a/config/routes.rb b/config/routes.rb index 809f6a76b..8f34700fc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -158,6 +158,7 @@ Rails.application.routes.draw do resources :portals do member do post :archive + put :add_members end resources :categories resources :articles diff --git a/db/migrate/20220608084622_add_associated_article_id.rb b/db/migrate/20220608084622_add_associated_article_id.rb new file mode 100644 index 000000000..f6cefb74c --- /dev/null +++ b/db/migrate/20220608084622_add_associated_article_id.rb @@ -0,0 +1,5 @@ +class AddAssociatedArticleId < ActiveRecord::Migration[6.1] + def change + add_reference :articles, :associated_article, foreign_key: { to_table: :articles } + end +end diff --git a/db/migrate/20220628124837_create_portal_members.rb b/db/migrate/20220628124837_create_portal_members.rb new file mode 100644 index 000000000..2bf062b4b --- /dev/null +++ b/db/migrate/20220628124837_create_portal_members.rb @@ -0,0 +1,12 @@ +class CreatePortalMembers < ActiveRecord::Migration[6.1] + def change + create_table :portal_members do |t| + t.bigint :portal_id + t.bigint :user_id + t.timestamps + end + + add_index :portal_members, [:portal_id, :user_id], unique: true + add_index :portal_members, [:user_id, :portal_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index fefbe449a..811c9d460 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_06_27_135753) do +ActiveRecord::Schema.define(version: 2022_06_28_124837) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -126,6 +126,8 @@ ActiveRecord::Schema.define(version: 2022_06_27_135753) do t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.bigint "author_id" + t.bigint "associated_article_id" + t.index ["associated_article_id"], name: "index_articles_on_associated_article_id" t.index ["author_id"], name: "index_articles_on_author_id" end @@ -663,6 +665,15 @@ ActiveRecord::Schema.define(version: 2022_06_27_135753) do t.datetime "updated_at", precision: 6, null: false end + create_table "portal_members", force: :cascade do |t| + t.bigint "portal_id" + t.bigint "user_id" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.index ["portal_id", "user_id"], name: "index_portal_members_on_portal_id_and_user_id", unique: true + t.index ["user_id", "portal_id"], name: "index_portal_members_on_user_id_and_portal_id", unique: true + end + create_table "portals", force: :cascade do |t| t.integer "account_id", null: false t.string "name", null: false @@ -836,6 +847,7 @@ ActiveRecord::Schema.define(version: 2022_06_27_135753) do add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "agent_bots", "accounts", on_delete: :cascade + add_foreign_key "articles", "articles", column: "associated_article_id" add_foreign_key "articles", "users", column: "author_id" add_foreign_key "campaigns", "accounts", on_delete: :cascade add_foreign_key "campaigns", "inboxes", on_delete: :cascade diff --git a/spec/controllers/api/v1/accounts/articles_controller_spec.rb b/spec/controllers/api/v1/accounts/articles_controller_spec.rb index 69c1ee4ca..13abba627 100644 --- a/spec/controllers/api/v1/accounts/articles_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/articles_controller_spec.rb @@ -7,6 +7,8 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do let!(:category) { create(:category, name: 'category', portal: portal, account_id: account.id, locale: 'en', slug: 'category_slug') } let!(:article) { create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id) } + before { create(:portal_member, user: agent, portal: portal) } + describe 'POST /api/v1/accounts/{account.id}/portals/{portal.slug}/articles' do context 'when it is an unauthenticated user' do it 'returns unauthorized' do @@ -34,6 +36,58 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do json_response = JSON.parse(response.body) expect(json_response['payload']['title']).to eql('MyTitle') end + + it 'associate to the root article' do + root_article = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: nil) + parent_article = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, + associated_article_id: root_article.id) + + article_params = { + article: { + category_id: category.id, + description: 'test description', + title: 'MyTitle', + content: 'This is my content.', + status: :published, + author_id: agent.id, + associated_article_id: parent_article.id + } + } + post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles", + params: article_params, + headers: agent.create_new_auth_token + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['payload']['title']).to eql('MyTitle') + + category = Article.find(json_response['payload']['id']) + expect(category.associated_article_id).to eql(root_article.id) + end + + it 'associate to the current parent article' do + parent_article = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: nil) + + article_params = { + article: { + category_id: category.id, + description: 'test description', + title: 'MyTitle', + content: 'This is my content.', + status: :published, + author_id: agent.id, + associated_article_id: parent_article.id + } + } + post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles", + params: article_params, + headers: agent.create_new_auth_token + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['payload']['title']).to eql('MyTitle') + + category = Article.find(json_response['payload']['id']) + expect(category.associated_article_id).to eql(parent_article.id) + end end end @@ -132,6 +186,24 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do expect(json_response['payload']['title']).to eq(article2.title) expect(json_response['payload']['id']).to eq(article2.id) end + + it 'get associated articles' do + root_article = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: nil) + child_article_1 = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, + associated_article_id: root_article.id) + child_article_2 = create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, + associated_article_id: root_article.id) + + get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles/#{root_article.id}", + headers: agent.create_new_auth_token + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + + expect(json_response['payload']['associated_articles'].length).to eq(2) + expect(json_response['payload']['associated_articles'][0]['id']).to eq(child_article_1.id) + expect(json_response['payload']['associated_articles'][1]['id']).to eq(child_article_2.id) + expect(json_response['payload']['id']).to eq(root_article.id) + end end end end diff --git a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb index 06620f7a5..7701e044c 100644 --- a/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/automation_rules_controller_spec.rb @@ -115,10 +115,10 @@ RSpec.describe 'Api::V1::Accounts::AutomationRulesController', type: :request do it 'Saves for automation_rules for account with status conditions' do params[:conditions] = [ { - 'attribute_key': 'status', - 'filter_operator': 'equal_to', - 'values': ['resolved'], - 'query_operator': nil + attribute_key: 'status', + filter_operator: 'equal_to', + values: ['resolved'], + query_operator: nil } ] expect(account.automation_rules.count).to eq(0) diff --git a/spec/controllers/api/v1/accounts/categories_controller_spec.rb b/spec/controllers/api/v1/accounts/categories_controller_spec.rb index 4ebdb54d5..b5e2e5434 100644 --- a/spec/controllers/api/v1/accounts/categories_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/categories_controller_spec.rb @@ -9,6 +9,8 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do let!(:related_category_1) { create(:category, name: 'related category 1', portal: portal, account_id: account.id, slug: 'category_slug_1') } let!(:related_category_2) { create(:category, name: 'related category 2', portal: portal, account_id: account.id, slug: 'category_slug_2') } + before { create(:portal_member, user: agent, portal: portal) } + describe 'POST /api/v1/accounts/{account.id}/portals/{portal.slug}/categories' do context 'when it is an unauthenticated user' do it 'returns unauthorized' do @@ -118,6 +120,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do headers: agent.create_new_auth_token expect(response).to have_http_status(:unprocessable_entity) json_response = JSON.parse(response.body) + expect(json_response['message']).to eql("Slug can't be blank") end end @@ -241,6 +244,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do category_count = Category.all.count category2 = create(:category, name: 'test_category_2', portal: portal, locale: 'es', slug: 'category_slug_2') + expect(category2.id).not_to be nil get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories", diff --git a/spec/controllers/api/v1/accounts/portals_controller_spec.rb b/spec/controllers/api/v1/accounts/portals_controller_spec.rb index d4c923d58..7bde7e3a1 100644 --- a/spec/controllers/api/v1/accounts/portals_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/portals_controller_spec.rb @@ -3,8 +3,13 @@ require 'rails_helper' RSpec.describe 'Api::V1::Accounts::Portals', type: :request do let(:account) { create(:account) } let(:agent) { create(:user, account: account, role: :agent) } + let(:admin) { create(:user, account: account, role: :administrator) } + let(:agent_1) { create(:user, account: account, role: :agent) } + let(:agent_2) { create(:user, account: account, role: :agent) } let!(:portal) { create(:portal, slug: 'portal-1', name: 'test_portal', account_id: account.id) } + before { create(:portal_member, user: agent, portal: portal) } + describe 'GET /api/v1/accounts/{account.id}/portals' do context 'when it is an unauthenticated user' do it 'returns unauthorized' do @@ -19,9 +24,11 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do expect(portal2.id).not_to be nil get "/api/v1/accounts/#{account.id}/portals", headers: agent.create_new_auth_token + expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) - expect(json_response.count).to be 2 + expect(json_response['payload'].length).to be 2 + expect(json_response['payload'][0]['id']).to be portal.id end end end @@ -30,6 +37,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do context 'when it is an unauthenticated user' do it 'returns unauthorized' do get "/api/v1/accounts/#{account.id}/portals" + expect(response).to have_http_status(:unauthorized) end end @@ -38,6 +46,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do it 'get one portals' do get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", headers: agent.create_new_auth_token + expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['name']).to eq portal.name @@ -48,7 +57,10 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do describe 'POST /api/v1/accounts/{account.id}/portals' do context 'when it is an unauthenticated user' do it 'returns unauthorized' do - post "/api/v1/accounts/#{account.id}/portals", params: {} + post "/api/v1/accounts/#{account.id}/portals", + params: {}, + headers: agent.create_new_auth_token + expect(response).to have_http_status(:unauthorized) end end @@ -63,7 +75,8 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do } post "/api/v1/accounts/#{account.id}/portals", params: portal_params, - headers: agent.create_new_auth_token + headers: admin.create_new_auth_token + expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['name']).to eql('test_portal') @@ -75,6 +88,7 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do context 'when it is an unauthenticated user' do it 'returns unauthorized' do put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", params: {} + expect(response).to have_http_status(:unauthorized) end end @@ -91,7 +105,8 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", params: portal_params, - headers: agent.create_new_auth_token + headers: admin.create_new_auth_token + expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['name']).to eql(portal_params[:portal][:name]) @@ -108,7 +123,8 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", params: portal_params, - headers: agent.create_new_auth_token + headers: admin.create_new_auth_token + expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['archived']).to eql(portal_params[:portal][:archived]) @@ -130,11 +146,44 @@ RSpec.describe 'Api::V1::Accounts::Portals', type: :request do context 'when it is an authenticated user' do it 'deletes portal' do delete "/api/v1/accounts/#{account.id}/portals/#{portal.slug}", - headers: agent.create_new_auth_token + headers: admin.create_new_auth_token expect(response).to have_http_status(:success) deleted_portal = Portal.find_by(id: portal.slug) expect(deleted_portal).to be nil end end end + + describe 'PUT /api/v1/accounts/{account.id}/portals/{portal.slug}/add_members' do + let(:new_account) { create(:account) } + let(:new_agent) { create(:user, account: new_account, role: :agent) } + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/add_members", params: {} + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user' do + it 'add members to the portal' do + portal_params = { + portal: { + member_ids: [agent_1.id, agent_2.id] + } + } + expect(portal.members.count).to be(1) + + put "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/add_members", + params: portal_params, + headers: admin.create_new_auth_token + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(portal.reload.member_ids).to include(agent_1.id) + expect(json_response['portal_members'].length).to be(3) + end + end + end end diff --git a/spec/factories/portal_members.rb b/spec/factories/portal_members.rb new file mode 100644 index 000000000..9cab7b054 --- /dev/null +++ b/spec/factories/portal_members.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :portal_member do + portal + user + end +end diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 1945cca62..90e69b384 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -80,6 +80,7 @@ RSpec.describe Article, type: :model do params = { query: 'title' } records = portal_1.articles.search(params) + expect(records.count).to eq(4) params = { query: 'the content' } diff --git a/spec/models/portal_member_spec.rb b/spec/models/portal_member_spec.rb new file mode 100644 index 000000000..67215a3d6 --- /dev/null +++ b/spec/models/portal_member_spec.rb @@ -0,0 +1,8 @@ +require 'rails_helper' + +RSpec.describe PortalMember, type: :model do + describe 'associations' do + it { is_expected.to belong_to(:portal) } + it { is_expected.to belong_to(:user) } + end +end diff --git a/spec/models/portal_spec.rb b/spec/models/portal_spec.rb index e7fe633bc..e702646ca 100644 --- a/spec/models/portal_spec.rb +++ b/spec/models/portal_spec.rb @@ -12,5 +12,7 @@ RSpec.describe Portal, type: :model do it { is_expected.to have_many(:categories) } it { is_expected.to have_many(:folders) } it { is_expected.to have_many(:articles) } + it { is_expected.to have_many(:portal_members) } + it { is_expected.to have_many(:members) } end end