Feature: Access tokens for API access (#604)
Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
parent
19ab0fe108
commit
a5b1e2b650
29 changed files with 517 additions and 270 deletions
|
@ -22,7 +22,7 @@ class Messages::Outgoing::NormalBuilder
|
|||
message_type: :outgoing,
|
||||
content: @content,
|
||||
private: @private,
|
||||
user_id: @user.id,
|
||||
user_id: @user&.id,
|
||||
source_id: @fb_id
|
||||
}
|
||||
end
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
class Api::BaseController < ApplicationController
|
||||
include AccessTokenAuthHelper
|
||||
respond_to :json
|
||||
before_action :authenticate_user!
|
||||
before_action :authenticate_access_token!, if: :authenticate_by_access_token?
|
||||
before_action :validate_bot_access_token!, if: :authenticate_by_access_token?
|
||||
before_action :authenticate_user!, unless: :authenticate_by_access_token?
|
||||
|
||||
private
|
||||
|
||||
def authenticate_by_access_token?
|
||||
request.headers[:api_access_token].present?
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation ||= current_account.conversations.find_by(display_id: params[:conversation_id])
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
class Api::V1::Widget::InboxesController < Api::BaseController
|
||||
class Api::V1::Accounts::Widget::InboxesController < Api::BaseController
|
||||
before_action :authorize_request
|
||||
before_action :set_web_widget_channel, only: [:update]
|
||||
before_action :set_inbox, only: [:update]
|
|
@ -14,7 +14,25 @@ class ApplicationController < ActionController::Base
|
|||
private
|
||||
|
||||
def current_account
|
||||
@_ ||= current_user.account
|
||||
@_ ||= find_current_account
|
||||
end
|
||||
|
||||
def find_current_account
|
||||
account = Account.find(params[:account_id])
|
||||
if current_user
|
||||
account_accessible_for_user?(account)
|
||||
elsif @resource&.is_a?(AgentBot)
|
||||
account_accessible_for_bot?(account)
|
||||
end
|
||||
account
|
||||
end
|
||||
|
||||
def account_accessible_for_user?(account)
|
||||
render_unauthorized('You are not authorized to access this account') unless account.account_users.find_by(user_id: current_user.id)
|
||||
end
|
||||
|
||||
def account_accessible_for_bot?(account)
|
||||
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
|
||||
end
|
||||
|
||||
def handle_with_exception
|
||||
|
|
24
app/controllers/concerns/access_token_auth_helper.rb
Normal file
24
app/controllers/concerns/access_token_auth_helper.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
module AccessTokenAuthHelper
|
||||
BOT_ACCESSIBLE_ENDPOINTS = {
|
||||
'api/v1/accounts/conversations' => ['toggle_status'],
|
||||
'api/v1/accounts/conversations/messages' => ['create']
|
||||
}.freeze
|
||||
|
||||
def authenticate_access_token!
|
||||
access_token = AccessToken.find_by(token: request.headers[:api_access_token])
|
||||
render_unauthorized('Invalid Access Token') && return unless access_token
|
||||
token_owner = access_token.owner
|
||||
@resource = token_owner
|
||||
end
|
||||
|
||||
def validate_bot_access_token!
|
||||
return if current_user.is_a?(User)
|
||||
return if agent_bot_accessible?
|
||||
|
||||
render_unauthorized('Access to this endpoint is not authorized for bots')
|
||||
end
|
||||
|
||||
def agent_bot_accessible?
|
||||
BOT_ACCESSIBLE_ENDPOINTS.fetch(params[:controller], []).include?(params[:action])
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@ import ApiClient from '../ApiClient';
|
|||
|
||||
class WebChannel extends ApiClient {
|
||||
constructor() {
|
||||
super('widget/inboxes');
|
||||
super('widget/inboxes', { accountScoped: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
onCopy() {
|
||||
onCopy(e) {
|
||||
e.preventDefault();
|
||||
copy(this.script);
|
||||
bus.$emit('newToastMessage', this.$t('COMPONENTS.CODE.COPY_SUCCESSFUL'));
|
||||
},
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
"TITLE": "Password",
|
||||
"NOTE": "Updating your password would reset your logins in multiple devices."
|
||||
},
|
||||
"ACCESS_TOKEN": {
|
||||
"TITLE": "Access Token",
|
||||
"NOTE": "This token can be used if you are building an API based integration"
|
||||
},
|
||||
"EMAIL_NOTIFICATIONS_SECTION" : {
|
||||
"TITLE": "Email Notifications",
|
||||
"NOTE": "Update your email notification preferences here",
|
||||
|
|
|
@ -83,6 +83,17 @@
|
|||
</div>
|
||||
</div>
|
||||
<email-notifications />
|
||||
<div class="profile--settings--row row">
|
||||
<div class="columns small-3 ">
|
||||
<h4 class="block-title">
|
||||
{{ $t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.TITLE') }}
|
||||
</h4>
|
||||
<p>{{ $t('PROFILE_SETTINGS.FORM.ACCESS_TOKEN.NOTE') }}</p>
|
||||
</div>
|
||||
<div class="columns small-9 medium-5">
|
||||
<woot-code :script="currentUser.access_token"></woot-code>
|
||||
</div>
|
||||
</div>
|
||||
<woot-submit-button
|
||||
class="button nice success button--fixed-right-top"
|
||||
:button-text="$t('PROFILE_SETTINGS.BTN_TEXT')"
|
||||
|
|
21
app/models/access_token.rb
Normal file
21
app/models/access_token.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: access_tokens
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# owner_type :string
|
||||
# token :string
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# owner_id :bigint
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_access_tokens_on_owner_type_and_owner_id (owner_type,owner_id)
|
||||
# index_access_tokens_on_token (token) UNIQUE
|
||||
#
|
||||
|
||||
class AccessToken < ApplicationRecord
|
||||
has_secure_token :token
|
||||
belongs_to :owner, polymorphic: true
|
||||
end
|
|
@ -14,6 +14,7 @@ class Account < ApplicationRecord
|
|||
validates :name, presence: true
|
||||
|
||||
has_many :account_users, dependent: :destroy
|
||||
has_many :agent_bot_inboxes, dependent: :destroy
|
||||
has_many :users, through: :account_users
|
||||
has_many :inboxes, dependent: :destroy
|
||||
has_many :conversations, dependent: :destroy
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# Table name: agent_bots
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# auth_token :string
|
||||
# description :string
|
||||
# name :string
|
||||
# outgoing_url :string
|
||||
|
@ -12,8 +11,9 @@
|
|||
#
|
||||
|
||||
class AgentBot < ApplicationRecord
|
||||
include AccessTokenable
|
||||
include Avatarable
|
||||
|
||||
has_many :agent_bot_inboxes, dependent: :destroy
|
||||
has_many :inboxes, through: :agent_bot_inboxes
|
||||
has_secure_token :auth_token
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# status :integer default("active")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :integer
|
||||
# agent_bot_id :integer
|
||||
# inbox_id :integer
|
||||
#
|
||||
|
@ -13,8 +14,16 @@
|
|||
class AgentBotInbox < ApplicationRecord
|
||||
validates :inbox_id, presence: true
|
||||
validates :agent_bot_id, presence: true
|
||||
before_validation :ensure_account_id
|
||||
|
||||
belongs_to :inbox
|
||||
belongs_to :agent_bot
|
||||
belongs_to :account
|
||||
enum status: { active: 0, inactive: 1 }
|
||||
|
||||
private
|
||||
|
||||
def ensure_account_id
|
||||
self.account_id = inbox&.account_id
|
||||
end
|
||||
end
|
||||
|
|
11
app/models/concerns/access_tokenable.rb
Normal file
11
app/models/concerns/access_tokenable.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
module AccessTokenable
|
||||
extend ActiveSupport::Concern
|
||||
included do
|
||||
has_one :access_token, as: :owner, dependent: :destroy
|
||||
after_create :create_access_token
|
||||
end
|
||||
|
||||
def create_access_token
|
||||
AccessToken.create!(owner: self)
|
||||
end
|
||||
end
|
|
@ -67,7 +67,9 @@ class Conversation < ApplicationRecord
|
|||
end
|
||||
|
||||
def toggle_status
|
||||
# FIXME: implement state machine with aasm
|
||||
self.status = open? ? :resolved : :open
|
||||
self.status = :open if bot?
|
||||
save
|
||||
end
|
||||
|
||||
|
|
|
@ -35,12 +35,13 @@
|
|||
#
|
||||
|
||||
class User < ApplicationRecord
|
||||
include AccessTokenable
|
||||
include AvailabilityStatusable
|
||||
include Avatarable
|
||||
# Include default devise modules.
|
||||
include DeviseTokenAuth::Concerns::User
|
||||
include Events::Types
|
||||
include Pubsubable
|
||||
include Avatarable
|
||||
include AvailabilityStatusable
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
devise :database_authenticatable,
|
||||
|
@ -69,7 +70,7 @@ class User < ApplicationRecord
|
|||
|
||||
before_validation :set_password_and_uid, on: :create
|
||||
|
||||
after_create :notify_creation
|
||||
after_create :notify_creation, :create_access_token
|
||||
|
||||
after_destroy :notify_deletion
|
||||
|
||||
|
|
|
@ -13,5 +13,6 @@ json.payload do
|
|||
json.inviter_id @resource.account_user.inviter_id
|
||||
json.confirmed @resource.confirmed?
|
||||
json.avatar_url @resource.avatar_url
|
||||
json.access_token @resource.access_token&.token
|
||||
end
|
||||
end
|
||||
|
|
|
@ -92,6 +92,9 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
resources :webhooks, except: [:show]
|
||||
namespace :widget do
|
||||
resources :inboxes, only: [:create, :update]
|
||||
end
|
||||
end
|
||||
|
||||
# end of account scoped api routes
|
||||
|
@ -101,7 +104,6 @@ Rails.application.routes.draw do
|
|||
|
||||
namespace :widget do
|
||||
resources :messages, only: [:index, :create, :update]
|
||||
resources :inboxes, only: [:create, :update]
|
||||
resources :inbox_members, only: [:index]
|
||||
end
|
||||
|
||||
|
|
23
db/migrate/20200309170810_create_access_tokens.rb
Normal file
23
db/migrate/20200309170810_create_access_tokens.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
class CreateAccessTokens < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :access_tokens do |t|
|
||||
t.references :owner, polymorphic: true, index: true
|
||||
t.string :token, index: { unique: true }
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
remove_column :agent_bots, :auth_token, :string
|
||||
|
||||
[::User, ::AgentBot].each do |access_tokenable|
|
||||
generate_access_tokens(access_tokenable)
|
||||
end
|
||||
end
|
||||
|
||||
def generate_access_tokens(access_tokenable)
|
||||
access_tokenable.find_in_batches do |record_batch|
|
||||
record_batch.each do |record|
|
||||
record.create_access_token if record.access_token.blank?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
class AddAccountIdToAgentBotInboxes < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :agent_bot_inboxes, :account_id, :integer, index: true
|
||||
|
||||
AgentBotInbox.all.each do |agent_bot_inbox|
|
||||
agent_bot_inbox.account_id = agent_bot_inbox.inbox.account_id
|
||||
agent_bot_inbox.save!
|
||||
end
|
||||
end
|
||||
end
|
503
db/schema.rb
503
db/schema.rb
|
@ -10,300 +10,311 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20_200_226_194_012) do
|
||||
ActiveRecord::Schema.define(version: 2020_03_09_213132) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension 'plpgsql'
|
||||
enable_extension "plpgsql"
|
||||
|
||||
create_table 'account_users', force: :cascade do |t|
|
||||
t.bigint 'account_id'
|
||||
t.bigint 'user_id'
|
||||
t.integer 'role', default: 0
|
||||
t.bigint 'inviter_id'
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
t.index %w[account_id user_id], name: 'uniq_user_id_per_account_id', unique: true
|
||||
t.index ['account_id'], name: 'index_account_users_on_account_id'
|
||||
t.index ['user_id'], name: 'index_account_users_on_user_id'
|
||||
create_table "access_tokens", force: :cascade do |t|
|
||||
t.string "owner_type"
|
||||
t.bigint "owner_id"
|
||||
t.string "token"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["owner_type", "owner_id"], name: "index_access_tokens_on_owner_type_and_owner_id"
|
||||
t.index ["token"], name: "index_access_tokens_on_token", unique: true
|
||||
end
|
||||
|
||||
create_table 'accounts', id: :serial, force: :cascade do |t|
|
||||
t.string 'name', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
create_table "account_users", force: :cascade do |t|
|
||||
t.bigint "account_id"
|
||||
t.bigint "user_id"
|
||||
t.integer "role", default: 0
|
||||
t.bigint "inviter_id"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["account_id", "user_id"], name: "uniq_user_id_per_account_id", unique: true
|
||||
t.index ["account_id"], name: "index_account_users_on_account_id"
|
||||
t.index ["user_id"], name: "index_account_users_on_user_id"
|
||||
end
|
||||
|
||||
create_table 'active_storage_attachments', force: :cascade do |t|
|
||||
t.string 'name', null: false
|
||||
t.string 'record_type', null: false
|
||||
t.bigint 'record_id', null: false
|
||||
t.bigint 'blob_id', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.index ['blob_id'], name: 'index_active_storage_attachments_on_blob_id'
|
||||
t.index %w[record_type record_id name blob_id], name: 'index_active_storage_attachments_uniqueness', unique: true
|
||||
create_table "accounts", id: :serial, force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table 'active_storage_blobs', force: :cascade do |t|
|
||||
t.string 'key', null: false
|
||||
t.string 'filename', null: false
|
||||
t.string 'content_type'
|
||||
t.text 'metadata'
|
||||
t.bigint 'byte_size', null: false
|
||||
t.string 'checksum', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.index ['key'], name: 'index_active_storage_blobs_on_key', unique: true
|
||||
create_table "active_storage_attachments", force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "record_type", null: false
|
||||
t.bigint "record_id", null: false
|
||||
t.bigint "blob_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
|
||||
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
|
||||
end
|
||||
|
||||
create_table 'agent_bot_inboxes', force: :cascade do |t|
|
||||
t.integer 'inbox_id'
|
||||
t.integer 'agent_bot_id'
|
||||
t.integer 'status', default: 0
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
create_table "active_storage_blobs", force: :cascade do |t|
|
||||
t.string "key", null: false
|
||||
t.string "filename", null: false
|
||||
t.string "content_type"
|
||||
t.text "metadata"
|
||||
t.bigint "byte_size", null: false
|
||||
t.string "checksum", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
|
||||
end
|
||||
|
||||
create_table 'agent_bots', force: :cascade do |t|
|
||||
t.string 'name'
|
||||
t.string 'description'
|
||||
t.string 'outgoing_url'
|
||||
t.string 'auth_token'
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
create_table "agent_bot_inboxes", force: :cascade do |t|
|
||||
t.integer "inbox_id"
|
||||
t.integer "agent_bot_id"
|
||||
t.integer "status", default: 0
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.integer "account_id"
|
||||
end
|
||||
|
||||
create_table 'attachments', id: :serial, force: :cascade do |t|
|
||||
t.integer 'file_type', default: 0
|
||||
t.string 'external_url'
|
||||
t.float 'coordinates_lat', default: 0.0
|
||||
t.float 'coordinates_long', default: 0.0
|
||||
t.integer 'message_id', null: false
|
||||
t.integer 'account_id', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.string 'fallback_title'
|
||||
t.string 'extension'
|
||||
create_table "agent_bots", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "description"
|
||||
t.string "outgoing_url"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table 'canned_responses', id: :serial, force: :cascade do |t|
|
||||
t.integer 'account_id', null: false
|
||||
t.string 'short_code'
|
||||
t.text 'content'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
create_table "attachments", id: :serial, force: :cascade do |t|
|
||||
t.integer "file_type", default: 0
|
||||
t.string "external_url"
|
||||
t.float "coordinates_lat", default: 0.0
|
||||
t.float "coordinates_long", default: 0.0
|
||||
t.integer "message_id", null: false
|
||||
t.integer "account_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "fallback_title"
|
||||
t.string "extension"
|
||||
end
|
||||
|
||||
create_table 'channel_facebook_pages', id: :serial, force: :cascade do |t|
|
||||
t.string 'name', null: false
|
||||
t.string 'page_id', null: false
|
||||
t.string 'user_access_token', null: false
|
||||
t.string 'page_access_token', null: false
|
||||
t.integer 'account_id', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.index %w[page_id account_id], name: 'index_channel_facebook_pages_on_page_id_and_account_id', unique: true
|
||||
t.index ['page_id'], name: 'index_channel_facebook_pages_on_page_id'
|
||||
create_table "canned_responses", id: :serial, force: :cascade do |t|
|
||||
t.integer "account_id", null: false
|
||||
t.string "short_code"
|
||||
t.text "content"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table 'channel_twitter_profiles', force: :cascade do |t|
|
||||
t.string 'name'
|
||||
t.string 'profile_id', null: false
|
||||
t.string 'twitter_access_token', null: false
|
||||
t.string 'twitter_access_token_secret', null: false
|
||||
t.integer 'account_id', null: false
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
create_table "channel_facebook_pages", id: :serial, force: :cascade do |t|
|
||||
t.string "name", null: false
|
||||
t.string "page_id", null: false
|
||||
t.string "user_access_token", null: false
|
||||
t.string "page_access_token", null: false
|
||||
t.integer "account_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["page_id", "account_id"], name: "index_channel_facebook_pages_on_page_id_and_account_id", unique: true
|
||||
t.index ["page_id"], name: "index_channel_facebook_pages_on_page_id"
|
||||
end
|
||||
|
||||
create_table 'channel_web_widgets', id: :serial, force: :cascade do |t|
|
||||
t.string 'website_name'
|
||||
t.string 'website_url'
|
||||
t.integer 'account_id'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.string 'website_token'
|
||||
t.string 'widget_color', default: '#1f93ff'
|
||||
t.index ['website_token'], name: 'index_channel_web_widgets_on_website_token', unique: true
|
||||
create_table "channel_twitter_profiles", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "profile_id", null: false
|
||||
t.string "twitter_access_token", null: false
|
||||
t.string "twitter_access_token_secret", null: false
|
||||
t.integer "account_id", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table 'contact_inboxes', force: :cascade do |t|
|
||||
t.bigint 'contact_id'
|
||||
t.bigint 'inbox_id'
|
||||
t.string 'source_id', null: false
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
t.index ['contact_id'], name: 'index_contact_inboxes_on_contact_id'
|
||||
t.index %w[inbox_id source_id], name: 'index_contact_inboxes_on_inbox_id_and_source_id', unique: true
|
||||
t.index ['inbox_id'], name: 'index_contact_inboxes_on_inbox_id'
|
||||
t.index ['source_id'], name: 'index_contact_inboxes_on_source_id'
|
||||
create_table "channel_web_widgets", id: :serial, force: :cascade do |t|
|
||||
t.string "website_name"
|
||||
t.string "website_url"
|
||||
t.integer "account_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "website_token"
|
||||
t.string "widget_color", default: "#1f93ff"
|
||||
t.index ["website_token"], name: "index_channel_web_widgets_on_website_token", unique: true
|
||||
end
|
||||
|
||||
create_table 'contacts', id: :serial, force: :cascade do |t|
|
||||
t.string 'name'
|
||||
t.string 'email'
|
||||
t.string 'phone_number'
|
||||
t.integer 'account_id', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.string 'pubsub_token'
|
||||
t.jsonb 'additional_attributes'
|
||||
t.index ['account_id'], name: 'index_contacts_on_account_id'
|
||||
t.index ['pubsub_token'], name: 'index_contacts_on_pubsub_token', unique: true
|
||||
create_table "contact_inboxes", force: :cascade do |t|
|
||||
t.bigint "contact_id"
|
||||
t.bigint "inbox_id"
|
||||
t.string "source_id", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["contact_id"], name: "index_contact_inboxes_on_contact_id"
|
||||
t.index ["inbox_id", "source_id"], name: "index_contact_inboxes_on_inbox_id_and_source_id", unique: true
|
||||
t.index ["inbox_id"], name: "index_contact_inboxes_on_inbox_id"
|
||||
t.index ["source_id"], name: "index_contact_inboxes_on_source_id"
|
||||
end
|
||||
|
||||
create_table 'conversations', id: :serial, force: :cascade do |t|
|
||||
t.integer 'account_id', null: false
|
||||
t.integer 'inbox_id', null: false
|
||||
t.integer 'status', default: 0, null: false
|
||||
t.integer 'assignee_id'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.bigint 'contact_id'
|
||||
t.integer 'display_id', null: false
|
||||
t.datetime 'user_last_seen_at'
|
||||
t.datetime 'agent_last_seen_at'
|
||||
t.boolean 'locked', default: false
|
||||
t.jsonb 'additional_attributes'
|
||||
t.bigint 'contact_inbox_id'
|
||||
t.index %w[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 ['contact_inbox_id'], name: 'index_conversations_on_contact_inbox_id'
|
||||
create_table "contacts", id: :serial, force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "email"
|
||||
t.string "phone_number"
|
||||
t.integer "account_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "pubsub_token"
|
||||
t.jsonb "additional_attributes"
|
||||
t.index ["account_id"], name: "index_contacts_on_account_id"
|
||||
t.index ["pubsub_token"], name: "index_contacts_on_pubsub_token", unique: true
|
||||
end
|
||||
|
||||
create_table 'inbox_members', id: :serial, force: :cascade do |t|
|
||||
t.integer 'user_id', null: false
|
||||
t.integer 'inbox_id', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.index ['inbox_id'], name: 'index_inbox_members_on_inbox_id'
|
||||
create_table "conversations", id: :serial, force: :cascade do |t|
|
||||
t.integer "account_id", null: false
|
||||
t.integer "inbox_id", null: false
|
||||
t.integer "status", default: 0, null: false
|
||||
t.integer "assignee_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "contact_id"
|
||||
t.integer "display_id", null: false
|
||||
t.datetime "user_last_seen_at"
|
||||
t.datetime "agent_last_seen_at"
|
||||
t.boolean "locked", default: false
|
||||
t.jsonb "additional_attributes"
|
||||
t.bigint "contact_inbox_id"
|
||||
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 ["contact_inbox_id"], name: "index_conversations_on_contact_inbox_id"
|
||||
end
|
||||
|
||||
create_table 'inboxes', id: :serial, force: :cascade do |t|
|
||||
t.integer 'channel_id', null: false
|
||||
t.integer 'account_id', null: false
|
||||
t.string 'name', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.string 'channel_type'
|
||||
t.boolean 'enable_auto_assignment', default: true
|
||||
t.index ['account_id'], name: 'index_inboxes_on_account_id'
|
||||
create_table "inbox_members", id: :serial, force: :cascade do |t|
|
||||
t.integer "user_id", null: false
|
||||
t.integer "inbox_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["inbox_id"], name: "index_inbox_members_on_inbox_id"
|
||||
end
|
||||
|
||||
create_table 'messages', id: :serial, force: :cascade do |t|
|
||||
t.text 'content'
|
||||
t.integer 'account_id', null: false
|
||||
t.integer 'inbox_id', null: false
|
||||
t.integer 'conversation_id', null: false
|
||||
t.integer 'message_type', null: false
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.boolean 'private', default: false
|
||||
t.integer 'user_id'
|
||||
t.integer 'status', default: 0
|
||||
t.string 'source_id'
|
||||
t.integer 'content_type', default: 0
|
||||
t.json 'content_attributes', default: {}
|
||||
t.bigint 'contact_id'
|
||||
t.index ['contact_id'], name: 'index_messages_on_contact_id'
|
||||
t.index ['conversation_id'], name: 'index_messages_on_conversation_id'
|
||||
t.index ['source_id'], name: 'index_messages_on_source_id'
|
||||
create_table "inboxes", id: :serial, force: :cascade do |t|
|
||||
t.integer "channel_id", null: false
|
||||
t.integer "account_id", null: false
|
||||
t.string "name", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "channel_type"
|
||||
t.boolean "enable_auto_assignment", default: true
|
||||
t.index ["account_id"], name: "index_inboxes_on_account_id"
|
||||
end
|
||||
|
||||
create_table 'notification_settings', force: :cascade do |t|
|
||||
t.integer 'account_id'
|
||||
t.integer 'user_id'
|
||||
t.integer 'email_flags', default: 0, null: false
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
t.index %w[account_id user_id], name: 'by_account_user', unique: true
|
||||
create_table "messages", id: :serial, force: :cascade do |t|
|
||||
t.text "content"
|
||||
t.integer "account_id", null: false
|
||||
t.integer "inbox_id", null: false
|
||||
t.integer "conversation_id", null: false
|
||||
t.integer "message_type", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.boolean "private", default: false
|
||||
t.integer "user_id"
|
||||
t.integer "status", default: 0
|
||||
t.string "source_id"
|
||||
t.integer "content_type", default: 0
|
||||
t.json "content_attributes", default: {}
|
||||
t.bigint "contact_id"
|
||||
t.index ["contact_id"], name: "index_messages_on_contact_id"
|
||||
t.index ["conversation_id"], name: "index_messages_on_conversation_id"
|
||||
t.index ["source_id"], name: "index_messages_on_source_id"
|
||||
end
|
||||
|
||||
create_table 'subscriptions', id: :serial, force: :cascade do |t|
|
||||
t.string 'pricing_version'
|
||||
t.integer 'account_id'
|
||||
t.datetime 'expiry'
|
||||
t.string 'billing_plan', default: 'trial'
|
||||
t.string 'stripe_customer_id'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.integer 'state', default: 0
|
||||
t.boolean 'payment_source_added', default: false
|
||||
create_table "notification_settings", force: :cascade do |t|
|
||||
t.integer "account_id"
|
||||
t.integer "user_id"
|
||||
t.integer "email_flags", default: 0, null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["account_id", "user_id"], name: "by_account_user", unique: true
|
||||
end
|
||||
|
||||
create_table 'taggings', id: :serial, force: :cascade do |t|
|
||||
t.integer 'tag_id'
|
||||
t.string 'taggable_type'
|
||||
t.integer 'taggable_id'
|
||||
t.string 'tagger_type'
|
||||
t.integer 'tagger_id'
|
||||
t.string 'context', limit: 128
|
||||
t.datetime 'created_at'
|
||||
t.index ['context'], name: 'index_taggings_on_context'
|
||||
t.index %w[tag_id taggable_id taggable_type context tagger_id tagger_type], name: 'taggings_idx', unique: true
|
||||
t.index ['tag_id'], name: 'index_taggings_on_tag_id'
|
||||
t.index %w[taggable_id taggable_type context], name: 'index_taggings_on_taggable_id_and_taggable_type_and_context'
|
||||
t.index %w[taggable_id taggable_type tagger_id context], name: 'taggings_idy'
|
||||
t.index ['taggable_id'], name: 'index_taggings_on_taggable_id'
|
||||
t.index ['taggable_type'], name: 'index_taggings_on_taggable_type'
|
||||
t.index %w[tagger_id tagger_type], name: 'index_taggings_on_tagger_id_and_tagger_type'
|
||||
t.index ['tagger_id'], name: 'index_taggings_on_tagger_id'
|
||||
create_table "subscriptions", id: :serial, force: :cascade do |t|
|
||||
t.string "pricing_version"
|
||||
t.integer "account_id"
|
||||
t.datetime "expiry"
|
||||
t.string "billing_plan", default: "trial"
|
||||
t.string "stripe_customer_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.integer "state", default: 0
|
||||
t.boolean "payment_source_added", default: false
|
||||
end
|
||||
|
||||
create_table 'tags', id: :serial, force: :cascade do |t|
|
||||
t.string 'name'
|
||||
t.integer 'taggings_count', default: 0
|
||||
t.index ['name'], name: 'index_tags_on_name', unique: true
|
||||
create_table "taggings", id: :serial, force: :cascade do |t|
|
||||
t.integer "tag_id"
|
||||
t.string "taggable_type"
|
||||
t.integer "taggable_id"
|
||||
t.string "tagger_type"
|
||||
t.integer "tagger_id"
|
||||
t.string "context", limit: 128
|
||||
t.datetime "created_at"
|
||||
t.index ["context"], name: "index_taggings_on_context"
|
||||
t.index ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
|
||||
t.index ["tag_id"], name: "index_taggings_on_tag_id"
|
||||
t.index ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
|
||||
t.index ["taggable_id", "taggable_type", "tagger_id", "context"], name: "taggings_idy"
|
||||
t.index ["taggable_id"], name: "index_taggings_on_taggable_id"
|
||||
t.index ["taggable_type"], name: "index_taggings_on_taggable_type"
|
||||
t.index ["tagger_id", "tagger_type"], name: "index_taggings_on_tagger_id_and_tagger_type"
|
||||
t.index ["tagger_id"], name: "index_taggings_on_tagger_id"
|
||||
end
|
||||
|
||||
create_table 'telegram_bots', id: :serial, force: :cascade do |t|
|
||||
t.string 'name'
|
||||
t.string 'auth_key'
|
||||
t.integer 'account_id'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
create_table "tags", id: :serial, force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.integer "taggings_count", default: 0
|
||||
t.index ["name"], name: "index_tags_on_name", unique: true
|
||||
end
|
||||
|
||||
create_table 'users', id: :serial, force: :cascade do |t|
|
||||
t.string 'provider', default: 'email', null: false
|
||||
t.string 'uid', default: '', null: false
|
||||
t.string 'encrypted_password', default: '', null: false
|
||||
t.string 'reset_password_token'
|
||||
t.datetime 'reset_password_sent_at'
|
||||
t.datetime 'remember_created_at'
|
||||
t.integer 'sign_in_count', default: 0, null: false
|
||||
t.datetime 'current_sign_in_at'
|
||||
t.datetime 'last_sign_in_at'
|
||||
t.string 'current_sign_in_ip'
|
||||
t.string 'last_sign_in_ip'
|
||||
t.string 'confirmation_token'
|
||||
t.datetime 'confirmed_at'
|
||||
t.datetime 'confirmation_sent_at'
|
||||
t.string 'unconfirmed_email'
|
||||
t.string 'name', null: false
|
||||
t.string 'nickname'
|
||||
t.string 'email'
|
||||
t.json 'tokens'
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
t.string 'pubsub_token'
|
||||
t.index ['email'], name: 'index_users_on_email'
|
||||
t.index ['pubsub_token'], name: 'index_users_on_pubsub_token', unique: true
|
||||
t.index ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true
|
||||
t.index %w[uid provider], name: 'index_users_on_uid_and_provider', unique: true
|
||||
create_table "telegram_bots", id: :serial, force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "auth_key"
|
||||
t.integer "account_id"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table 'webhooks', force: :cascade do |t|
|
||||
t.integer 'account_id'
|
||||
t.integer 'inbox_id'
|
||||
t.string 'url'
|
||||
t.datetime 'created_at', precision: 6, null: false
|
||||
t.datetime 'updated_at', precision: 6, null: false
|
||||
t.integer 'webhook_type', default: 0
|
||||
create_table "users", id: :serial, force: :cascade do |t|
|
||||
t.string "provider", default: "email", null: false
|
||||
t.string "uid", default: "", null: false
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
t.string "reset_password_token"
|
||||
t.datetime "reset_password_sent_at"
|
||||
t.datetime "remember_created_at"
|
||||
t.integer "sign_in_count", default: 0, null: false
|
||||
t.datetime "current_sign_in_at"
|
||||
t.datetime "last_sign_in_at"
|
||||
t.string "current_sign_in_ip"
|
||||
t.string "last_sign_in_ip"
|
||||
t.string "confirmation_token"
|
||||
t.datetime "confirmed_at"
|
||||
t.datetime "confirmation_sent_at"
|
||||
t.string "unconfirmed_email"
|
||||
t.string "name", null: false
|
||||
t.string "nickname"
|
||||
t.string "email"
|
||||
t.json "tokens"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "pubsub_token"
|
||||
t.index ["email"], name: "index_users_on_email"
|
||||
t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true
|
||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
t.index ["uid", "provider"], name: "index_users_on_uid_and_provider", unique: true
|
||||
end
|
||||
|
||||
add_foreign_key 'account_users', 'accounts'
|
||||
add_foreign_key 'account_users', 'users'
|
||||
add_foreign_key 'active_storage_attachments', 'active_storage_blobs', column: 'blob_id'
|
||||
add_foreign_key 'contact_inboxes', 'contacts'
|
||||
add_foreign_key 'contact_inboxes', 'inboxes'
|
||||
add_foreign_key 'conversations', 'contact_inboxes'
|
||||
add_foreign_key 'messages', 'contacts'
|
||||
create_table "webhooks", force: :cascade do |t|
|
||||
t.integer "account_id"
|
||||
t.integer "inbox_id"
|
||||
t.string "url"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.integer "webhook_type", default: 0
|
||||
end
|
||||
|
||||
add_foreign_key "account_users", "accounts"
|
||||
add_foreign_key "account_users", "users"
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "contact_inboxes", "contacts"
|
||||
add_foreign_key "contact_inboxes", "inboxes"
|
||||
add_foreign_key "conversations", "contact_inboxes"
|
||||
add_foreign_key "messages", "contacts"
|
||||
end
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
"eslint --fix",
|
||||
"git add"
|
||||
],
|
||||
"*.rb": [
|
||||
"!(*schema).rb": [
|
||||
"rubocop -a",
|
||||
"git add"
|
||||
],
|
||||
|
|
60
spec/controllers/api/base_controller_spec.rb
Normal file
60
spec/controllers/api/base_controller_spec.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'API Base', type: :request do
|
||||
let!(:account) { create(:account) }
|
||||
let!(:user) { create(:user, account: account) }
|
||||
|
||||
describe 'request with api_access_token for user' do
|
||||
context 'when it is an invalid api_access_token' do
|
||||
it 'returns unauthorized' do
|
||||
get '/api/v1/profile',
|
||||
headers: { api_access_token: 'invalid' },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is a valid api_access_token' do
|
||||
it 'returns current user information' do
|
||||
get '/api/v1/profile',
|
||||
headers: { api_access_token: user.access_token.token },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
json_response = JSON.parse(response.body)
|
||||
expect(json_response['id']).to eq(user.id)
|
||||
expect(json_response['email']).to eq(user.email)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'request with api_access_token for bot' do
|
||||
let!(:agent_bot) { create(:agent_bot) }
|
||||
let!(:inbox) { create(:inbox, account: account) }
|
||||
let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user, status: 'bot') }
|
||||
|
||||
context 'when it is an unauthorized url' do
|
||||
it 'returns unauthorized' do
|
||||
get '/api/v1/profile',
|
||||
headers: { api_access_token: agent_bot.access_token.token },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is a accessible url' do
|
||||
it 'returns success' do
|
||||
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status",
|
||||
headers: { api_access_token: agent_bot.access_token.token },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(conversation.reload.status).to eq('open')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +1,11 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Conversation Messages API', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
let!(:account) { create(:account) }
|
||||
|
||||
describe 'POST /api/v1/accounts/{account.id}/conversations/<id>/messages' do
|
||||
let(:conversation) { create(:conversation, account: account) }
|
||||
let!(:inbox) { create(:inbox, account: account) }
|
||||
let!(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
|
@ -30,6 +31,24 @@ RSpec.describe 'Conversation Messages API', type: :request do
|
|||
expect(conversation.messages.first.content).to eq(params[:message])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated agent bot' do
|
||||
let!(:agent_bot) { create(:agent_bot) }
|
||||
|
||||
it 'creates a new outgoing message' do
|
||||
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
|
||||
params = { message: 'test-message' }
|
||||
|
||||
post api_v1_account_conversation_messages_url(account_id: account.id, conversation_id: conversation.display_id),
|
||||
params: params,
|
||||
headers: { api_access_token: agent_bot.access_token.token },
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(conversation.messages.count).to eq(1)
|
||||
expect(conversation.messages.first.content).to eq(params[:message])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/{account.id}/conversations/:id/messages' do
|
||||
|
|
|
@ -80,6 +80,17 @@ RSpec.describe 'Conversations API', type: :request do
|
|||
expect(response).to have_http_status(:success)
|
||||
expect(conversation.reload.status).to eq('resolved')
|
||||
end
|
||||
|
||||
it 'toggles the conversation status to open from bot' do
|
||||
conversation.update!(status: 'bot')
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/toggle_status",
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(conversation.reload.status).to eq('open')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
||||
RSpec.describe '/api/v1/accounts/{account.id}/widget/inboxes', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:admin) { create(:user, account: account, role: :administrator) }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
|
||||
describe 'POST /api/v1/widget/inboxes' do
|
||||
describe 'POST /api/v1/accounts/{account.id}/widget/inboxes' do
|
||||
let(:params) { { website: { website_name: 'test', website_url: 'test.com', widget_color: '#eaeaea' } } }
|
||||
|
||||
context 'when unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
post '/api/v1/widget/inboxes', params: params
|
||||
post "/api/v1/accounts/#{account.id}/widget/inboxes", params: params
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,7 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
context 'when user is logged in' do
|
||||
context 'with user as administrator' do
|
||||
it 'creates inbox and returns website_token' do
|
||||
post '/api/v1/widget/inboxes', params: params, headers: admin.create_new_auth_token
|
||||
post "/api/v1/accounts/#{account.id}/widget/inboxes", params: params, headers: admin.create_new_auth_token
|
||||
|
||||
expect(response).to have_http_status(:success)
|
||||
json_response = JSON.parse(response.body)
|
||||
|
@ -31,7 +31,7 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
|
||||
context 'with user as agent' do
|
||||
it 'returns unauthorized' do
|
||||
post '/api/v1/widget/inboxes',
|
||||
post "/api/v1/accounts/#{account.id}/widget/inboxes",
|
||||
params: params,
|
||||
headers: agent.create_new_auth_token
|
||||
|
||||
|
@ -41,12 +41,12 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v1/widget/inboxes/:id' do
|
||||
describe 'PATCH /api/v1/accounts/{account.id}/widget/inboxes/:id' do
|
||||
let(:update_params) { { website: { widget_color: '#eaeaea' } } }
|
||||
|
||||
context 'when unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
patch "/api/v1/widget/inboxes/#{inbox.channel_id}", params: update_params
|
||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}", params: update_params
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
@ -54,7 +54,7 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
context 'when user is logged in' do
|
||||
context 'with user as administrator' do
|
||||
it 'updates website channel' do
|
||||
patch "/api/v1/widget/inboxes/#{inbox.channel_id}",
|
||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}",
|
||||
params: update_params,
|
||||
headers: admin.create_new_auth_token
|
||||
|
||||
|
@ -67,7 +67,7 @@ RSpec.describe '/api/v1/widget/inboxes', type: :request do
|
|||
|
||||
context 'with user as agent' do
|
||||
it 'returns unauthorized' do
|
||||
patch "/api/v1/widget/inboxes/#{inbox.channel_id}",
|
||||
patch "/api/v1/accounts/#{account.id}/widget/inboxes/#{inbox.channel_id}",
|
||||
params: update_params,
|
||||
headers: agent.create_new_auth_token
|
||||
|
Loading…
Reference in a new issue