From fdf449dc873d2dbb68c8119acbce9a6887c3c365 Mon Sep 17 00:00:00 2001 From: Tejaswini Chile Date: Fri, 8 Jul 2022 17:24:38 +0530 Subject: [PATCH] Feat: Article public apis (#4955) --- .../api/v1/accounts/articles_controller.rb | 2 +- .../api/v1/accounts/categories_controller.rb | 2 +- .../api/v1/portals/articles_controller.rb | 25 +++++++++ app/models/article.rb | 2 +- app/models/category.rb | 40 +++++++------ .../categories/_category.json.jbuilder | 6 +- .../api/v1/models/_article.json.jbuilder | 30 ++++++++++ .../models/_associated_article.json.jbuilder | 15 +++++ .../api/v1/models/_category.json.jbuilder | 6 +- .../v1/portals/articles/index.json.jbuilder | 3 + .../v1/portals/articles/show.json.jbuilder | 1 + config/routes.rb | 5 +- ...5458_rename_linked_category_column_name.rb | 5 ++ db/schema.rb | 8 +-- .../v1/accounts/articles_controller_spec.rb | 17 ++++++ .../v1/accounts/categories_controller_spec.rb | 16 +++--- .../v1/portals/articles_controller_spec.rb | 56 +++++++++++++++++++ spec/models/article_spec.rb | 10 ++-- spec/models/category_spec.rb | 2 +- 19 files changed, 205 insertions(+), 46 deletions(-) create mode 100644 app/controllers/public/api/v1/portals/articles_controller.rb create mode 100644 app/views/public/api/v1/models/_article.json.jbuilder create mode 100644 app/views/public/api/v1/models/_associated_article.json.jbuilder create mode 100644 app/views/public/api/v1/portals/articles/index.json.jbuilder create mode 100644 app/views/public/api/v1/portals/articles/show.json.jbuilder create mode 100644 db/migrate/20220706085458_rename_linked_category_column_name.rb create mode 100644 spec/controllers/public/api/v1/portals/articles_controller_spec.rb diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index 249352e18..77a1fdf65 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -5,7 +5,7 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController def index @articles = @portal.articles - @articles.search(list_params) if params[:payload].present? + @articles = @articles.search(list_params) if params[:payload].present? end def create diff --git a/app/controllers/api/v1/accounts/categories_controller.rb b/app/controllers/api/v1/accounts/categories_controller.rb index f242d3385..c4491b2e2 100644 --- a/app/controllers/api/v1/accounts/categories_controller.rb +++ b/app/controllers/api/v1/accounts/categories_controller.rb @@ -46,7 +46,7 @@ class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseControlle def category_params params.require(:category).permit( - :name, :description, :position, :slug, :locale, :parent_category_id, :linked_category_id + :name, :description, :position, :slug, :locale, :parent_category_id, :associated_category_id ) end end diff --git a/app/controllers/public/api/v1/portals/articles_controller.rb b/app/controllers/public/api/v1/portals/articles_controller.rb new file mode 100644 index 000000000..2a961b7a9 --- /dev/null +++ b/app/controllers/public/api/v1/portals/articles_controller.rb @@ -0,0 +1,25 @@ +class Public::Api::V1::Portals::ArticlesController < ApplicationController + before_action :set_portal + before_action :set_article, only: [:show] + + def index + @articles = @portal.articles + @articles = @articles.search(list_params) if params[:payload].present? + end + + def show; end + + private + + def set_article + @article = @portal.articles.find(params[:id]) + end + + def set_portal + @portal = ::Portal.find_by!(slug: params[:portal_slug], archived: false) + end + + def list_params + params.require(:payload).permit(:query) + end +end diff --git a/app/models/article.rb b/app/models/article.rb index 65482957e..7cf4ebd41 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -77,7 +77,7 @@ class Article < ApplicationRecord records = joins( :category ).search_by_category_slug(params[:category_slug]).search_by_category_locale(params[:locale]) - records.text_search(params[:query]) if params[:query].present? + records = records.text_search(params[:query]) if params[:query].present? records.page(current_page(params)) end diff --git a/app/models/category.rb b/app/models/category.rb index 6e9b30ae0..a3955ae91 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -2,22 +2,22 @@ # # Table name: categories # -# id :bigint not null, primary key -# description :text -# locale :string default("en") -# name :string -# position :integer -# slug :string not null -# created_at :datetime not null -# updated_at :datetime not null -# account_id :integer not null -# linked_category_id :bigint -# parent_category_id :bigint -# portal_id :integer not null +# id :bigint not null, primary key +# description :text +# locale :string default("en") +# name :string +# position :integer +# slug :string not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :integer not null +# associated_category_id :bigint +# parent_category_id :bigint +# portal_id :integer not null # # Indexes # -# index_categories_on_linked_category_id (linked_category_id) +# index_categories_on_associated_category_id (associated_category_id) # index_categories_on_locale (locale) # index_categories_on_locale_and_account_id (locale,account_id) # index_categories_on_parent_category_id (parent_category_id) @@ -25,7 +25,7 @@ # # Foreign Keys # -# fk_rails_... (linked_category_id => categories.id) +# fk_rails_... (associated_category_id => categories.id) # fk_rails_... (parent_category_id => categories.id) # class Category < ApplicationRecord @@ -45,13 +45,17 @@ class Category < ApplicationRecord foreign_key: :parent_category_id, dependent: :nullify, inverse_of: 'parent_category' - has_many :linked_categories, + has_many :associated_categories, class_name: :Category, - foreign_key: :linked_category_id, + foreign_key: :associated_category_id, dependent: :nullify, - inverse_of: 'linked_category' + inverse_of: 'root_category' belongs_to :parent_category, class_name: :Category, optional: true - belongs_to :linked_category, class_name: :Category, optional: true + belongs_to :root_category, + class_name: :Category, + foreign_key: :associated_category_id, + inverse_of: :associated_categories, + optional: true before_validation :ensure_account_id validates :account_id, presence: true diff --git a/app/views/api/v1/accounts/categories/_category.json.jbuilder b/app/views/api/v1/accounts/categories/_category.json.jbuilder index d299a50d0..2e4263722 100644 --- a/app/views/api/v1/accounts/categories/_category.json.jbuilder +++ b/app/views/api/v1/accounts/categories/_category.json.jbuilder @@ -20,8 +20,8 @@ if category.parent_category.present? end end -if category.linked_category.present? - json.linked_category do - json.partial! 'api/v1/accounts/categories/associated_category.json.jbuilder', category: category.linked_category +if category.root_category.present? + json.root_category do + json.partial! 'api/v1/accounts/categories/associated_category.json.jbuilder', category: category.root_category end end diff --git a/app/views/public/api/v1/models/_article.json.jbuilder b/app/views/public/api/v1/models/_article.json.jbuilder new file mode 100644 index 000000000..80b254a8a --- /dev/null +++ b/app/views/public/api/v1/models/_article.json.jbuilder @@ -0,0 +1,30 @@ +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 +json.last_updated_at article.updated_at + +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 + +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/public/api/v1/models/_associated_article.json.jbuilder b/app/views/public/api/v1/models/_associated_article.json.jbuilder new file mode 100644 index 000000000..02b4dd4db --- /dev/null +++ b/app/views/public/api/v1/models/_associated_article.json.jbuilder @@ -0,0 +1,15 @@ +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 +json.last_updated_at article.updated_at +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/public/api/v1/models/_category.json.jbuilder b/app/views/public/api/v1/models/_category.json.jbuilder index c79febc63..ee3057ae3 100644 --- a/app/views/public/api/v1/models/_category.json.jbuilder +++ b/app/views/public/api/v1/models/_category.json.jbuilder @@ -18,8 +18,8 @@ if category.parent_category.present? end end -if category.linked_category.present? - json.linked_category do - json.partial! partial: 'associated_category', category: category.linked_category +if category.root_category.present? + json.root_category do + json.partial! partial: 'associated_category', category: category.root_category end end diff --git a/app/views/public/api/v1/portals/articles/index.json.jbuilder b/app/views/public/api/v1/portals/articles/index.json.jbuilder new file mode 100644 index 000000000..477662a36 --- /dev/null +++ b/app/views/public/api/v1/portals/articles/index.json.jbuilder @@ -0,0 +1,3 @@ +json.payload do + json.array! @articles, partial: 'public/api/v1/models/article.json.jbuilder', as: :article +end diff --git a/app/views/public/api/v1/portals/articles/show.json.jbuilder b/app/views/public/api/v1/portals/articles/show.json.jbuilder new file mode 100644 index 000000000..16444b425 --- /dev/null +++ b/app/views/public/api/v1/portals/articles/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! 'public/api/v1/models/article.json.jbuilder', article: @article diff --git a/config/routes.rb b/config/routes.rb index 37e435adb..4ff36aba8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -260,9 +260,8 @@ Rails.application.routes.draw do end resources :portals, only: [:show], param: :slug do scope module: :portals do - resources :categories, only: [:index, :show], param: :slug do - resources :articles, only: [:index, :show], param: :slug - end + resources :categories, only: [:index, :show], param: :slug + resources :articles, only: [:index, :show] end end resources :csat_survey, only: [:show, :update] diff --git a/db/migrate/20220706085458_rename_linked_category_column_name.rb b/db/migrate/20220706085458_rename_linked_category_column_name.rb new file mode 100644 index 000000000..295b70c31 --- /dev/null +++ b/db/migrate/20220706085458_rename_linked_category_column_name.rb @@ -0,0 +1,5 @@ +class RenameLinkedCategoryColumnName < ActiveRecord::Migration[6.1] + def change + rename_column :categories, :linked_category_id, :associated_category_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 811c9d460..a68d81b96 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_28_124837) do +ActiveRecord::Schema.define(version: 2022_07_06_085458) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -200,8 +200,8 @@ ActiveRecord::Schema.define(version: 2022_06_28_124837) do t.string "locale", default: "en" t.string "slug", null: false t.bigint "parent_category_id" - t.bigint "linked_category_id" - t.index ["linked_category_id"], name: "index_categories_on_linked_category_id" + t.bigint "associated_category_id" + t.index ["associated_category_id"], name: "index_categories_on_associated_category_id" t.index ["locale", "account_id"], name: "index_categories_on_locale_and_account_id" t.index ["locale"], name: "index_categories_on_locale" t.index ["parent_category_id"], name: "index_categories_on_parent_category_id" @@ -851,7 +851,7 @@ ActiveRecord::Schema.define(version: 2022_06_28_124837) do add_foreign_key "articles", "users", column: "author_id" add_foreign_key "campaigns", "accounts", on_delete: :cascade add_foreign_key "campaigns", "inboxes", on_delete: :cascade - add_foreign_key "categories", "categories", column: "linked_category_id" + add_foreign_key "categories", "categories", column: "associated_category_id" add_foreign_key "categories", "categories", column: "parent_category_id" add_foreign_key "contact_inboxes", "contacts", on_delete: :cascade add_foreign_key "contact_inboxes", "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 807def183..976cebc0d 100644 --- a/spec/controllers/api/v1/accounts/articles_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/articles_controller_spec.rb @@ -174,6 +174,23 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do json_response = JSON.parse(response.body) expect(json_response['payload'].count).to be 2 end + + it 'get all articles with searched text query' do + article2 = create(:article, + account_id: account.id, + portal: portal, + category: category, + author_id: agent.id, + content: 'this is some test and funny content') + expect(article2.id).not_to be nil + + get "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles", + headers: agent.create_new_auth_token, + params: { payload: { query: 'funny' } } + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['payload'].count).to be 1 + end end describe 'GET /api/v1/accounts/{account.id}/portals/{portal.slug}/articles/{article.id}' do diff --git a/spec/controllers/api/v1/accounts/categories_controller_spec.rb b/spec/controllers/api/v1/accounts/categories_controller_spec.rb index b5e2e5434..91992c7c8 100644 --- a/spec/controllers/api/v1/accounts/categories_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/categories_controller_spec.rb @@ -5,7 +5,9 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do let(:agent) { create(:user, account: account, role: :agent) } let!(:portal) { create(:portal, name: 'test_portal', account_id: account.id) } let!(:category) { create(:category, name: 'category', portal: portal, account_id: account.id, slug: 'category_slug') } - let!(:category_to_link) { create(:category, name: 'linked category', portal: portal, account_id: account.id, slug: 'linked_category_slug') } + let!(:category_to_associate) do + create(:category, name: 'associated category', portal: portal, account_id: account.id, slug: 'associated_category_slug') + end 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') } @@ -29,7 +31,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do locale: 'es', slug: 'test_category_1', parent_category_id: category.id, - linked_category_id: category_to_link.id, + associated_category_id: category_to_associate.id, related_category_ids: [related_category_1.id, related_category_2.id] } } @@ -44,7 +46,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do locale: 'es', slug: 'test_category_2', parent_category_id: category.id, - linked_category_id: category_to_link.id, + associated_category_id: category_to_associate.id, related_category_ids: [related_category_1.id, related_category_2.id] } } @@ -61,9 +63,9 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do expect(json_response['payload']['related_categories'][0]['id']).to eql(related_category_1.id) expect(json_response['payload']['related_categories'][1]['id']).to eql(related_category_2.id) expect(json_response['payload']['parent_category']['id']).to eql(category.id) - expect(json_response['payload']['linked_category']['id']).to eql(category_to_link.id) + expect(json_response['payload']['root_category']['id']).to eql(category_to_associate.id) expect(category.reload.sub_category_ids).to eql([Category.last.id]) - expect(category_to_link.reload.linked_category_ids).to eql([Category.last.id]) + expect(category_to_associate.reload.associated_category_ids).to eql([Category.last.id]) end it 'creates multiple sub_categories under one parent_category' do @@ -79,7 +81,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do expect(category.reload.sub_category_ids).to eql(Category.last(2).pluck(:id)) end - it 'creates multiple linked_categories with one category' do + it 'creates multiple associated_categories with one category' do post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/categories", params: category_params, headers: agent.create_new_auth_token @@ -89,7 +91,7 @@ RSpec.describe 'Api::V1::Accounts::Categories', type: :request do headers: agent.create_new_auth_token expect(response).to have_http_status(:success) - expect(category_to_link.reload.linked_category_ids).to eql(Category.last(2).pluck(:id)) + expect(category_to_associate.reload.associated_category_ids).to eql(Category.last(2).pluck(:id)) end it 'will throw an error on locale, category_id uniqueness' 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 new file mode 100644 index 000000000..894a5e995 --- /dev/null +++ b/spec/controllers/public/api/v1/portals/articles_controller_spec.rb @@ -0,0 +1,56 @@ +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!(: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) } + + before do + create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id) + create(:article, category: category, portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: article.id) + create(:article, category: category_2, portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: article.id) + create(:article, category: category_2, portal: portal, account_id: account.id, author_id: agent.id) + end + + describe 'GET /public/api/v1/portals/:portal_slug/articles' do + it 'Fetch all articles in the portal' do + get "/public/api/v1/portals/#{portal.slug}/articles" + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + + expect(json_response['payload'].length).to eql portal.articles.count + end + + it 'get all articles with searched text query' do + article2 = create(:article, + account_id: account.id, + portal: portal, + category: category, + author_id: agent.id, + content: 'this is some test and funny content') + expect(article2.id).not_to be nil + + get "/public/api/v1/portals/#{portal.slug}/articles", + headers: agent.create_new_auth_token, + params: { payload: { query: 'funny' } } + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['payload'].count).to be 1 + end + end + + describe 'GET /public/api/v1/portals/:portal_slug/articles/:id' do + it 'Fetch article with the id' do + get "/public/api/v1/portals/#{portal.slug}/articles/#{article.id}" + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + + expect(json_response['title']).to eql article.title + end + end +end diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 90e69b384..f018e277d 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -27,7 +27,7 @@ RSpec.describe Article, type: :model do before do create(:article, category_id: category_1.id, content: 'This is the content', description: 'this is the description', title: 'this is title', portal_id: portal_1.id, author_id: user.id) - create(:article, category_id: category_1.id, title: 'title 1', portal_id: portal_1.id, author_id: user.id) + create(:article, category_id: category_1.id, title: 'title 1', content: 'This is the content', portal_id: portal_1.id, author_id: user.id) create(:article, category_id: category_2.id, title: 'title 2', portal_id: portal_2.id, author_id: user.id) create(:article, category_id: category_2.id, title: 'title 3', portal_id: portal_1.id, author_id: user.id) create(:article, category_id: category_3.id, title: 'title 6', portal_id: portal_2.id, author_id: user.id) @@ -76,6 +76,7 @@ RSpec.describe Article, type: :model do it 'returns data with text_search query' do params = { query: 'title' } records = portal_2.articles.search(params) + expect(records.count).to eq(2) params = { query: 'title' } @@ -84,12 +85,13 @@ RSpec.describe Article, type: :model do expect(records.count).to eq(4) params = { query: 'the content' } - records = portal_2.articles.search(params) + records = portal_1.articles.search(params) + expect(records.count).to eq(2) end it 'returns data with text_search query and locale' do - params = { query: 'the title', locale: 'es' } + params = { query: 'title', locale: 'es' } records = portal_2.articles.search(params) expect(records.count).to eq(2) end @@ -101,7 +103,7 @@ RSpec.describe Article, type: :model do end it 'return records with category_slug and text_search query' do - params = { category_slug: 'category_2', query: 'the title' } + params = { category_slug: 'category_2', query: 'title' } records = portal_1.articles.search(params) expect(records.count).to eq(2) end diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 565abba8c..fd3da0778 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Category, type: :model do it { is_expected.to belong_to(:portal) } it { is_expected.to have_many(:articles) } it { is_expected.to have_many(:sub_categories) } - it { is_expected.to have_many(:linked_categories) } + it { is_expected.to have_many(:associated_categories) } it { is_expected.to have_many(:related_categories) } end