feat: Add APIs for Campaigns (#2175)
This commit is contained in:
parent
3afc9b5f5b
commit
b89cc9cf57
23 changed files with 452 additions and 1 deletions
28
app/controllers/api/v1/accounts/campaigns_controller.rb
Normal file
28
app/controllers/api/v1/accounts/campaigns_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Api::V1::Accounts::CampaignsController < Api::V1::Accounts::BaseController
|
||||||
|
before_action :campaign, except: [:index, :create]
|
||||||
|
before_action :check_authorization
|
||||||
|
|
||||||
|
def index
|
||||||
|
@campaigns = Current.account.campaigns
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@campaign = Current.account.campaigns.create!(campaign_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@campaign.update(campaign_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def campaign
|
||||||
|
@campaign ||= Current.account.campaigns.find_by(display_id: params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def campaign_params
|
||||||
|
params.require(:campaign).permit(:title, :description, :content, :enabled, :inbox_id, :sender_id, trigger_rules: {})
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,6 +11,10 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
||||||
@assignable_agents = (Current.account.users.where(id: @inbox.members.select(:user_id)) + Current.account.administrators).uniq
|
@assignable_agents = (Current.account.users.where(id: @inbox.members.select(:user_id)) + Current.account.administrators).uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def campaigns
|
||||||
|
@campaigns = @inbox.campaigns
|
||||||
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
channel = create_channel
|
channel = create_channel
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Account < ApplicationRecord
|
||||||
has_many :data_imports, dependent: :destroy
|
has_many :data_imports, dependent: :destroy
|
||||||
has_many :users, through: :account_users
|
has_many :users, through: :account_users
|
||||||
has_many :inboxes, dependent: :destroy
|
has_many :inboxes, dependent: :destroy
|
||||||
|
has_many :campaigns, dependent: :destroy
|
||||||
has_many :conversations, dependent: :destroy
|
has_many :conversations, dependent: :destroy
|
||||||
has_many :messages, dependent: :destroy
|
has_many :messages, dependent: :destroy
|
||||||
has_many :contacts, dependent: :destroy
|
has_many :contacts, dependent: :destroy
|
||||||
|
@ -104,4 +105,8 @@ class Account < ApplicationRecord
|
||||||
trigger.after(:insert).for_each(:row) do
|
trigger.after(:insert).for_each(:row) do
|
||||||
"execute format('create sequence IF NOT EXISTS conv_dpid_seq_%s', NEW.id);"
|
"execute format('create sequence IF NOT EXISTS conv_dpid_seq_%s', NEW.id);"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trigger.name('camp_dpid_before_insert').after(:insert).for_each(:row) do
|
||||||
|
"execute format('create sequence IF NOT EXISTS camp_dpid_seq_%s', NEW.id);"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
49
app/models/campaign.rb
Normal file
49
app/models/campaign.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: campaigns
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# content :text not null
|
||||||
|
# description :text
|
||||||
|
# enabled :boolean default(TRUE)
|
||||||
|
# title :string not null
|
||||||
|
# trigger_rules :jsonb
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :bigint not null
|
||||||
|
# display_id :integer not null
|
||||||
|
# inbox_id :bigint not null
|
||||||
|
# sender_id :integer
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_campaigns_on_account_id (account_id)
|
||||||
|
# index_campaigns_on_inbox_id (inbox_id)
|
||||||
|
#
|
||||||
|
# Foreign Keys
|
||||||
|
#
|
||||||
|
# fk_rails_... (account_id => accounts.id)
|
||||||
|
# fk_rails_... (inbox_id => inboxes.id)
|
||||||
|
#
|
||||||
|
class Campaign < ApplicationRecord
|
||||||
|
validates :account_id, presence: true
|
||||||
|
validates :inbox_id, presence: true
|
||||||
|
belongs_to :account
|
||||||
|
belongs_to :inbox
|
||||||
|
belongs_to :sender, class_name: 'User', optional: true
|
||||||
|
|
||||||
|
has_many :conversations, dependent: :nullify, autosave: true
|
||||||
|
|
||||||
|
after_commit :set_display_id, unless: :display_id?
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_display_id
|
||||||
|
reload
|
||||||
|
end
|
||||||
|
|
||||||
|
# creating db triggers
|
||||||
|
trigger.before(:insert).for_each(:row) do
|
||||||
|
"NEW.display_id := nextval('camp_dpid_seq_' || NEW.account_id);"
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,6 +14,7 @@
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# account_id :integer not null
|
# account_id :integer not null
|
||||||
# assignee_id :integer
|
# assignee_id :integer
|
||||||
|
# campaign_id :bigint
|
||||||
# contact_id :bigint
|
# contact_id :bigint
|
||||||
# contact_inbox_id :bigint
|
# contact_inbox_id :bigint
|
||||||
# display_id :integer not null
|
# display_id :integer not null
|
||||||
|
@ -24,11 +25,13 @@
|
||||||
#
|
#
|
||||||
# index_conversations_on_account_id (account_id)
|
# index_conversations_on_account_id (account_id)
|
||||||
# index_conversations_on_account_id_and_display_id (account_id,display_id) UNIQUE
|
# index_conversations_on_account_id_and_display_id (account_id,display_id) UNIQUE
|
||||||
|
# index_conversations_on_campaign_id (campaign_id)
|
||||||
# index_conversations_on_contact_inbox_id (contact_inbox_id)
|
# index_conversations_on_contact_inbox_id (contact_inbox_id)
|
||||||
# index_conversations_on_team_id (team_id)
|
# index_conversations_on_team_id (team_id)
|
||||||
#
|
#
|
||||||
# Foreign Keys
|
# Foreign Keys
|
||||||
#
|
#
|
||||||
|
# fk_rails_... (campaign_id => campaigns.id)
|
||||||
# fk_rails_... (contact_inbox_id => contact_inboxes.id)
|
# fk_rails_... (contact_inbox_id => contact_inboxes.id)
|
||||||
# fk_rails_... (team_id => teams.id)
|
# fk_rails_... (team_id => teams.id)
|
||||||
#
|
#
|
||||||
|
@ -54,6 +57,7 @@ class Conversation < ApplicationRecord
|
||||||
belongs_to :contact
|
belongs_to :contact
|
||||||
belongs_to :contact_inbox
|
belongs_to :contact_inbox
|
||||||
belongs_to :team, optional: true
|
belongs_to :team, optional: true
|
||||||
|
belongs_to :campaign, optional: true
|
||||||
|
|
||||||
has_many :messages, dependent: :destroy, autosave: true
|
has_many :messages, dependent: :destroy, autosave: true
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ class Inbox < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :channel, polymorphic: true, dependent: :destroy
|
belongs_to :channel, polymorphic: true, dependent: :destroy
|
||||||
|
|
||||||
|
has_many :campaigns, dependent: :destroy
|
||||||
has_many :contact_inboxes, dependent: :destroy
|
has_many :contact_inboxes, dependent: :destroy
|
||||||
has_many :contacts, through: :contact_inboxes
|
has_many :contacts, through: :contact_inboxes
|
||||||
|
|
||||||
|
|
21
app/policies/campaign_policy.rb
Normal file
21
app/policies/campaign_policy.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
class CampaignPolicy < ApplicationPolicy
|
||||||
|
def index?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def update?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def show?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def create?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
end
|
|
@ -27,6 +27,10 @@ class InboxPolicy < ApplicationPolicy
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def campaigns?
|
||||||
|
@account_user.administrator?
|
||||||
|
end
|
||||||
|
|
||||||
def create?
|
def create?
|
||||||
@account_user.administrator?
|
@account_user.administrator?
|
||||||
end
|
end
|
||||||
|
|
1
app/views/api/v1/accounts/campaigns/create.json.jbuilder
Normal file
1
app/views/api/v1/accounts/campaigns/create.json.jbuilder
Normal file
|
@ -0,0 +1 @@
|
||||||
|
json.partial! 'api/v1/models/campaign.json.jbuilder', resource: @campaign
|
3
app/views/api/v1/accounts/campaigns/index.json.jbuilder
Normal file
3
app/views/api/v1/accounts/campaigns/index.json.jbuilder
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
json.array! @campaigns do |campaign|
|
||||||
|
json.partial! 'api/v1/models/campaign.json.jbuilder', resource: campaign
|
||||||
|
end
|
1
app/views/api/v1/accounts/campaigns/show.json.jbuilder
Normal file
1
app/views/api/v1/accounts/campaigns/show.json.jbuilder
Normal file
|
@ -0,0 +1 @@
|
||||||
|
json.partial! 'api/v1/models/campaign.json.jbuilder', resource: @campaign
|
1
app/views/api/v1/accounts/campaigns/update.json.jbuilder
Normal file
1
app/views/api/v1/accounts/campaigns/update.json.jbuilder
Normal file
|
@ -0,0 +1 @@
|
||||||
|
json.partial! 'api/v1/models/campaign.json.jbuilder', resource: @campaign
|
|
@ -0,0 +1,3 @@
|
||||||
|
json.array! @campaigns do |campaign|
|
||||||
|
json.partial! 'api/v1/models/campaign.json.jbuilder', resource: campaign
|
||||||
|
end
|
15
app/views/api/v1/models/_campaign.json.jbuilder
Normal file
15
app/views/api/v1/models/_campaign.json.jbuilder
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
json.id resource.display_id
|
||||||
|
json.content resource.content
|
||||||
|
json.description resource.description
|
||||||
|
json.enabled resource.enabled
|
||||||
|
json.title resource.title
|
||||||
|
json.trigger_rules resource.trigger_rules
|
||||||
|
json.account_id resource.account_id
|
||||||
|
json.inbox do
|
||||||
|
json.partial! 'api/v1/models/inbox.json.jbuilder', resource: resource.inbox
|
||||||
|
end
|
||||||
|
json.sender do
|
||||||
|
json.partial! 'api/v1/models/agent.json.jbuilder', resource: resource.sender if resource.sender.present?
|
||||||
|
end
|
||||||
|
json.created_at resource.created_at
|
||||||
|
json.updated_at resource.updated_at
|
|
@ -42,6 +42,8 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resources :canned_responses, except: [:show, :edit, :new]
|
resources :canned_responses, except: [:show, :edit, :new]
|
||||||
|
resources :campaigns, only: [:index, :create, :show, :update]
|
||||||
|
|
||||||
namespace :channels do
|
namespace :channels do
|
||||||
resource :twilio_channel, only: [:create]
|
resource :twilio_channel, only: [:create]
|
||||||
end
|
end
|
||||||
|
@ -89,6 +91,7 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
resources :inboxes, only: [:index, :create, :update, :destroy] do
|
resources :inboxes, only: [:index, :create, :update, :destroy] do
|
||||||
get :assignable_agents, on: :member
|
get :assignable_agents, on: :member
|
||||||
|
get :campaigns, on: :member
|
||||||
post :set_agent_bot, on: :member
|
post :set_agent_bot, on: :member
|
||||||
end
|
end
|
||||||
resources :inbox_members, only: [:create, :show], param: :inbox_id
|
resources :inbox_members, only: [:create, :show], param: :inbox_id
|
||||||
|
|
18
db/migrate/20210428135041_campaigns.rb
Normal file
18
db/migrate/20210428135041_campaigns.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
class Campaigns < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
create_table :campaigns do |t|
|
||||||
|
t.integer :display_id, null: false
|
||||||
|
t.string :title, null: false
|
||||||
|
t.text :description
|
||||||
|
t.text :content, null: false
|
||||||
|
t.integer :sender_id
|
||||||
|
t.boolean :enabled, default: true
|
||||||
|
t.references :account, null: false, foreign_key: true
|
||||||
|
t.references :inbox, null: false, foreign_key: true
|
||||||
|
t.column :trigger_rules, :jsonb, default: {}
|
||||||
|
t.timestamps
|
||||||
|
end
|
||||||
|
|
||||||
|
add_reference :conversations, :campaign, foreign_key: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
# This migration was auto-generated via `rake db:generate_trigger_migration'.
|
||||||
|
# While you can edit this file, any changes you make to the definitions here
|
||||||
|
# will be undone by the next auto-generated trigger migration.
|
||||||
|
|
||||||
|
class CreateTriggersAccountsInsertOrCampaignsInsert < ActiveRecord::Migration[6.0]
|
||||||
|
def up
|
||||||
|
create_trigger('camp_dpid_before_insert', generated: true, compatibility: 1)
|
||||||
|
.on('accounts')
|
||||||
|
.name('camp_dpid_before_insert')
|
||||||
|
.after(:insert)
|
||||||
|
.for_each(:row) do
|
||||||
|
"execute format('create sequence IF NOT EXISTS camp_dpid_seq_%s', NEW.id);"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_trigger('campaigns_before_insert_row_tr', generated: true, compatibility: 1)
|
||||||
|
.on('campaigns')
|
||||||
|
.before(:insert)
|
||||||
|
.for_each(:row) do
|
||||||
|
"NEW.display_id := nextval('camp_dpid_seq_' || NEW.account_id);"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
drop_trigger('camp_dpid_before_insert', 'accounts', generated: true)
|
||||||
|
|
||||||
|
drop_trigger('campaigns_before_insert_row_tr', 'campaigns', generated: true)
|
||||||
|
end
|
||||||
|
end
|
38
db/schema.rb
38
db/schema.rb
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2021_04_26_191914) do
|
ActiveRecord::Schema.define(version: 2021_04_28_151147) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
|
@ -113,6 +113,22 @@ ActiveRecord::Schema.define(version: 2021_04_26_191914) do
|
||||||
t.string "extension"
|
t.string "extension"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_table "campaigns", force: :cascade do |t|
|
||||||
|
t.integer "display_id", null: false
|
||||||
|
t.string "title", null: false
|
||||||
|
t.text "description"
|
||||||
|
t.text "content", null: false
|
||||||
|
t.integer "sender_id"
|
||||||
|
t.boolean "enabled", default: true
|
||||||
|
t.bigint "account_id", null: false
|
||||||
|
t.bigint "inbox_id", null: false
|
||||||
|
t.jsonb "trigger_rules", default: {}
|
||||||
|
t.datetime "created_at", precision: 6, null: false
|
||||||
|
t.datetime "updated_at", precision: 6, null: false
|
||||||
|
t.index ["account_id"], name: "index_campaigns_on_account_id"
|
||||||
|
t.index ["inbox_id"], name: "index_campaigns_on_inbox_id"
|
||||||
|
end
|
||||||
|
|
||||||
create_table "canned_responses", id: :serial, force: :cascade do |t|
|
create_table "canned_responses", id: :serial, force: :cascade do |t|
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
t.string "short_code"
|
t.string "short_code"
|
||||||
|
@ -235,8 +251,10 @@ ActiveRecord::Schema.define(version: 2021_04_26_191914) do
|
||||||
t.string "identifier"
|
t.string "identifier"
|
||||||
t.datetime "last_activity_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
|
t.datetime "last_activity_at", default: -> { "CURRENT_TIMESTAMP" }, null: false
|
||||||
t.bigint "team_id"
|
t.bigint "team_id"
|
||||||
|
t.bigint "campaign_id"
|
||||||
t.index ["account_id", "display_id"], name: "index_conversations_on_account_id_and_display_id", unique: true
|
t.index ["account_id", "display_id"], name: "index_conversations_on_account_id_and_display_id", unique: true
|
||||||
t.index ["account_id"], name: "index_conversations_on_account_id"
|
t.index ["account_id"], name: "index_conversations_on_account_id"
|
||||||
|
t.index ["campaign_id"], name: "index_conversations_on_campaign_id"
|
||||||
t.index ["contact_inbox_id"], name: "index_conversations_on_contact_inbox_id"
|
t.index ["contact_inbox_id"], name: "index_conversations_on_contact_inbox_id"
|
||||||
t.index ["team_id"], name: "index_conversations_on_team_id"
|
t.index ["team_id"], name: "index_conversations_on_team_id"
|
||||||
end
|
end
|
||||||
|
@ -592,8 +610,11 @@ ActiveRecord::Schema.define(version: 2021_04_26_191914) do
|
||||||
add_foreign_key "account_users", "accounts"
|
add_foreign_key "account_users", "accounts"
|
||||||
add_foreign_key "account_users", "users"
|
add_foreign_key "account_users", "users"
|
||||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||||
|
add_foreign_key "campaigns", "accounts"
|
||||||
|
add_foreign_key "campaigns", "inboxes"
|
||||||
add_foreign_key "contact_inboxes", "contacts"
|
add_foreign_key "contact_inboxes", "contacts"
|
||||||
add_foreign_key "contact_inboxes", "inboxes"
|
add_foreign_key "contact_inboxes", "inboxes"
|
||||||
|
add_foreign_key "conversations", "campaigns"
|
||||||
add_foreign_key "conversations", "contact_inboxes"
|
add_foreign_key "conversations", "contact_inboxes"
|
||||||
add_foreign_key "conversations", "teams"
|
add_foreign_key "conversations", "teams"
|
||||||
add_foreign_key "data_imports", "accounts"
|
add_foreign_key "data_imports", "accounts"
|
||||||
|
@ -614,4 +635,19 @@ ActiveRecord::Schema.define(version: 2021_04_26_191914) do
|
||||||
"NEW.display_id := nextval('conv_dpid_seq_' || NEW.account_id);"
|
"NEW.display_id := nextval('conv_dpid_seq_' || NEW.account_id);"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
create_trigger("camp_dpid_before_insert", :generated => true, :compatibility => 1).
|
||||||
|
on("accounts").
|
||||||
|
name("camp_dpid_before_insert").
|
||||||
|
after(:insert).
|
||||||
|
for_each(:row) do
|
||||||
|
"execute format('create sequence IF NOT EXISTS camp_dpid_seq_%s', NEW.id);"
|
||||||
|
end
|
||||||
|
|
||||||
|
create_trigger("campaigns_before_insert_row_tr", :generated => true, :compatibility => 1).
|
||||||
|
on("campaigns").
|
||||||
|
before(:insert).
|
||||||
|
for_each(:row) do
|
||||||
|
"NEW.display_id := nextval('camp_dpid_seq_' || NEW.account_id);"
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
148
spec/controllers/api/v1/accounts/campaigns_controller_spec.rb
Normal file
148
spec/controllers/api/v1/accounts/campaigns_controller_spec.rb
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe 'Campaigns API', type: :request do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/campaigns' do
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
let!(:campaign) { create(:campaign, account: account) }
|
||||||
|
|
||||||
|
it 'returns unauthorized for agents' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns all campaigns to administrators' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
expect(body.first[:id]).to eq(campaign.display_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/campaigns/:id' do
|
||||||
|
let(:campaign) { create(:campaign, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
|
||||||
|
it 'returns unauthorized for agents' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}",
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows the campaign for administrators' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(JSON.parse(response.body, symbolize_names: true)[:id]).to eq(campaign.display_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/{account.id}/campaigns' do
|
||||||
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/campaigns",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
|
||||||
|
it 'returns unauthorized for agents' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/campaigns",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new campaign' do
|
||||||
|
post "/api/v1/accounts/#{account.id}/campaigns",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(JSON.parse(response.body, symbolize_names: true)[:title]).to eq('test')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PATCH /api/v1/accounts/{account.id}/campaigns/:id' do
|
||||||
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
let!(:campaign) { create(:campaign, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
|
||||||
|
it 'returns unauthorized for agents' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
headers: agent.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the campaign' do
|
||||||
|
patch "/api/v1/accounts/#{account.id}/campaigns/#{campaign.display_id}",
|
||||||
|
params: { inbox_id: inbox.id, title: 'test', content: 'test message' },
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(JSON.parse(response.body, symbolize_names: true)[:title]).to eq('test')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -74,6 +74,44 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET /api/v1/accounts/{account.id}/inboxes/{inbox.id}/campaigns' do
|
||||||
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
|
context 'when it is an unauthenticated user' do
|
||||||
|
it 'returns unauthorized' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/campaigns"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is an authenticated user' do
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:administrator) { create(:user, account: account, role: :administrator) }
|
||||||
|
|
||||||
|
let!(:campaign) { create(:campaign, account: account, inbox: inbox) }
|
||||||
|
|
||||||
|
it 'returns unauthorized for agents' do
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/campaigns"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns all campaigns belonging to the inbox to administrators' do
|
||||||
|
# create a random campaign
|
||||||
|
create(:campaign, account: account)
|
||||||
|
get "/api/v1/accounts/#{account.id}/inboxes/#{inbox.id}/campaigns",
|
||||||
|
headers: administrator.create_new_auth_token,
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
body = JSON.parse(response.body, symbolize_names: true)
|
||||||
|
expect(body.first[:id]).to eq(campaign.display_id)
|
||||||
|
expect(body.length).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'DELETE /api/v1/accounts/{account.id}/inboxes/:id' do
|
describe 'DELETE /api/v1/accounts/{account.id}/inboxes/:id' do
|
||||||
let(:inbox) { create(:inbox, account: account) }
|
let(:inbox) { create(:inbox, account: account) }
|
||||||
|
|
||||||
|
|
16
spec/factories/campaigns.rb
Normal file
16
spec/factories/campaigns.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :campaign do
|
||||||
|
sequence(:title) { |n| "Campaign #{n}" }
|
||||||
|
sequence(:content) { |n| "Campaign content #{n}" }
|
||||||
|
after(:build) do |campaign|
|
||||||
|
campaign.account ||= create(:account)
|
||||||
|
campaign.inbox ||= create(
|
||||||
|
:inbox,
|
||||||
|
account: campaign.account,
|
||||||
|
channel: create(:channel_widget, account: campaign.account)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
spec/models/campaign_spec.rb
Normal file
23
spec/models/campaign_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe Campaign, type: :model do
|
||||||
|
describe 'associations' do
|
||||||
|
it { is_expected.to belong_to(:account) }
|
||||||
|
it { is_expected.to belong_to(:inbox) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.before_create' do
|
||||||
|
let(:campaign) { build(:campaign, display_id: nil) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
campaign.save
|
||||||
|
campaign.reload
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'runs before_create callbacks' do
|
||||||
|
expect(campaign.display_id).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,7 @@ require Rails.root.join 'spec/models/concerns/round_robin_handler_spec.rb'
|
||||||
RSpec.describe Conversation, type: :model do
|
RSpec.describe Conversation, type: :model do
|
||||||
describe 'associations' do
|
describe 'associations' do
|
||||||
it { is_expected.to belong_to(:account) }
|
it { is_expected.to belong_to(:account) }
|
||||||
|
it { is_expected.to belong_to(:inbox) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'concerns' do
|
describe 'concerns' do
|
||||||
|
|
Loading…
Reference in a new issue