chore: General fixes and clean up (#1169)
This commit is contained in:
parent
124e43b477
commit
2193de9853
23 changed files with 111 additions and 121 deletions
|
@ -50,6 +50,8 @@ class Messages::Facebook::MessageBuilder
|
||||||
def attach_file(attachment, file_url)
|
def attach_file(attachment, file_url)
|
||||||
file_resource = LocalResource.new(file_url)
|
file_resource = LocalResource.new(file_url)
|
||||||
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
|
attachment.file.attach(io: file_resource.file, filename: file_resource.tmp_filename, content_type: file_resource.encoding)
|
||||||
|
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "invalid url #{file_url} : #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
|
|
|
@ -81,6 +81,8 @@ class Api::V1::Accounts::CallbacksController < Api::V1::Accounts::BaseController
|
||||||
|
|
||||||
avatar_resource = LocalResource.new(uri)
|
avatar_resource = LocalResource.new(uri)
|
||||||
facebook_inbox.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
facebook_inbox.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
||||||
|
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "invalid url #{file_url} : #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_avatar_url(page_id)
|
def get_avatar_url(page_id)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseController
|
||||||
include Events::Types
|
include Events::Types
|
||||||
|
|
||||||
before_action :conversation, except: [:index]
|
before_action :conversation, except: [:index]
|
||||||
before_action :contact_inbox, only: [:create]
|
before_action :contact_inbox, only: [:create]
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,11 @@ class AsyncDispatcher < BaseDispatcher
|
||||||
end
|
end
|
||||||
|
|
||||||
def listeners
|
def listeners
|
||||||
listeners = [EventListener.instance, WebhookListener.instance, HookListener.instance]
|
listeners = [
|
||||||
|
EventListener.instance,
|
||||||
|
WebhookListener.instance,
|
||||||
|
InstallationWebhookListener.instance, HookListener.instance
|
||||||
|
]
|
||||||
listeners
|
listeners
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,5 +4,7 @@ class ContactAvatarJob < ApplicationJob
|
||||||
def perform(contact, avatar_url)
|
def perform(contact, avatar_url)
|
||||||
avatar_resource = LocalResource.new(avatar_url)
|
avatar_resource = LocalResource.new(avatar_url)
|
||||||
contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
||||||
|
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "invalid url #{file_url} : #{e.message}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
19
app/listeners/installation_webhook_listener.rb
Normal file
19
app/listeners/installation_webhook_listener.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
class InstallationWebhookListener < BaseListener
|
||||||
|
def account_created(event)
|
||||||
|
payload = event.data[:account].webhook_data.merge(event: __method__.to_s)
|
||||||
|
deliver_webhook_payloads(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_destroyed(event)
|
||||||
|
payload = event.data[:account].webhook_data.merge(event: __method__.to_s)
|
||||||
|
deliver_webhook_payloads(payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def deliver_webhook_payloads(payload)
|
||||||
|
# Deliver the installation event
|
||||||
|
webhook_url = InstallationConfig.find_by(name: 'INSTALLATION_EVENTS_WEBHOOK_URL')&.value
|
||||||
|
WebhookJob.perform_later(webhook_url, payload) if webhook_url
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,8 +16,6 @@
|
||||||
class Account < ApplicationRecord
|
class Account < ApplicationRecord
|
||||||
# used for single column multi flags
|
# used for single column multi flags
|
||||||
include FlagShihTzu
|
include FlagShihTzu
|
||||||
|
|
||||||
include Events::Types
|
|
||||||
include Reportable
|
include Reportable
|
||||||
include Featurable
|
include Featurable
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ class Account < ApplicationRecord
|
||||||
|
|
||||||
enum locale: LANGUAGES_CONFIG.map { |key, val| [val[:iso_639_1_code], key] }.to_h
|
enum locale: LANGUAGES_CONFIG.map { |key, val| [val[:iso_639_1_code], key] }.to_h
|
||||||
|
|
||||||
after_create :notify_creation
|
after_create_commit :notify_creation
|
||||||
after_destroy :notify_deletion
|
after_destroy :notify_deletion
|
||||||
|
|
||||||
def agents
|
def agents
|
||||||
|
|
|
@ -24,8 +24,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class AccountUser < ApplicationRecord
|
class AccountUser < ApplicationRecord
|
||||||
include Events::Types
|
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :inviter, class_name: 'User', optional: true
|
belongs_to :inviter, class_name: 'User', optional: true
|
||||||
|
@ -33,7 +31,7 @@ class AccountUser < ApplicationRecord
|
||||||
enum role: { agent: 0, administrator: 1 }
|
enum role: { agent: 0, administrator: 1 }
|
||||||
accepts_nested_attributes_for :account
|
accepts_nested_attributes_for :account
|
||||||
|
|
||||||
after_create :notify_creation, :create_notification_setting
|
after_create_commit :notify_creation, :create_notification_setting
|
||||||
after_destroy :notify_deletion, :destroy_notification_setting
|
after_destroy :notify_deletion, :destroy_notification_setting
|
||||||
|
|
||||||
validates :user_id, uniqueness: { scope: :account_id }
|
validates :user_id, uniqueness: { scope: :account_id }
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
class ApplicationRecord < ActiveRecord::Base
|
class ApplicationRecord < ActiveRecord::Base
|
||||||
|
include Events::Types
|
||||||
self.abstract_class = true
|
self.abstract_class = true
|
||||||
|
|
||||||
|
# the models that exposed in email templates through liquid
|
||||||
DROPPABLES = %w[Account Channel Conversation Inbox User].freeze
|
DROPPABLES = %w[Account Channel Conversation Inbox User].freeze
|
||||||
|
|
||||||
|
# ModelDrop class should exist in app/drops
|
||||||
def to_drop
|
def to_drop
|
||||||
return unless DROPPABLES.include?(self.class.name)
|
return unless DROPPABLES.include?(self.class.name)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ class Contact < ApplicationRecord
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
include Avatarable
|
include Avatarable
|
||||||
include AvailabilityStatusable
|
include AvailabilityStatusable
|
||||||
include Events::Types
|
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :email, allow_blank: true, uniqueness: { scope: [:account_id], case_sensitive: false }
|
validates :email, allow_blank: true, uniqueness: { scope: [:account_id], case_sensitive: false }
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class Conversation < ApplicationRecord
|
class Conversation < ApplicationRecord
|
||||||
include Events::Types
|
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :inbox_id, presence: true
|
validates :inbox_id, presence: true
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class Message < ApplicationRecord
|
class Message < ApplicationRecord
|
||||||
include Events::Types
|
|
||||||
|
|
||||||
NUMBER_OF_PERMITTED_ATTACHMENTS = 15
|
NUMBER_OF_PERMITTED_ATTACHMENTS = 15
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
|
@ -105,6 +103,7 @@ class Message < ApplicationRecord
|
||||||
created_at: created_at,
|
created_at: created_at,
|
||||||
message_type: message_type,
|
message_type: message_type,
|
||||||
content_type: content_type,
|
content_type: content_type,
|
||||||
|
private: private,
|
||||||
content_attributes: content_attributes,
|
content_attributes: content_attributes,
|
||||||
source_id: source_id,
|
source_id: source_id,
|
||||||
sender: sender.try(:webhook_data),
|
sender: sender.try(:webhook_data),
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
class Plan
|
|
||||||
attr_accessor :key, :attributes
|
|
||||||
|
|
||||||
def initialize(key, attributes = {})
|
|
||||||
@key = key.to_sym
|
|
||||||
@attributes = attributes
|
|
||||||
end
|
|
||||||
|
|
||||||
def name
|
|
||||||
attributes[:name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def id
|
|
||||||
attributes[:id]
|
|
||||||
end
|
|
||||||
|
|
||||||
def price
|
|
||||||
attributes[:price]
|
|
||||||
end
|
|
||||||
|
|
||||||
def active
|
|
||||||
attributes[:active]
|
|
||||||
end
|
|
||||||
|
|
||||||
def version
|
|
||||||
attributes[:version]
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def config
|
|
||||||
Hashie::Mash.new(PLAN_CONFIG)
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_trial_period
|
|
||||||
(config['trial_period'] || 14).days
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_pricing_version
|
|
||||||
config['default_pricing_version']
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_plans
|
|
||||||
load_active_plans + load_inactive_plans
|
|
||||||
end
|
|
||||||
|
|
||||||
def all_plans
|
|
||||||
default_plans
|
|
||||||
end
|
|
||||||
|
|
||||||
def active_plans
|
|
||||||
all_plans.select(&:active)
|
|
||||||
end
|
|
||||||
|
|
||||||
def paid_plan
|
|
||||||
active_plans.first
|
|
||||||
end
|
|
||||||
|
|
||||||
def inactive_plans
|
|
||||||
all_plans.reject(&:active)
|
|
||||||
end
|
|
||||||
|
|
||||||
def trial_plan
|
|
||||||
all_plans.detect { |plan| plan.key == :trial }
|
|
||||||
end
|
|
||||||
|
|
||||||
def plans_of_version(version)
|
|
||||||
all_plans.select { |plan| plan.version == version }
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_by_key(key)
|
|
||||||
key = key.to_sym
|
|
||||||
all_plans.detect { |plan| plan.key == key }.dup
|
|
||||||
end
|
|
||||||
|
|
||||||
# #helpers
|
|
||||||
|
|
||||||
def load_active_plans
|
|
||||||
result = []
|
|
||||||
Plan.config.active.each_pair do |version, plans|
|
|
||||||
plans.each_pair do |key, attributes|
|
|
||||||
result << Plan.new(key, attributes.merge(active: true, version: version))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_inactive_plans
|
|
||||||
result = []
|
|
||||||
Plan.config.inactive.each_pair do |version, plans|
|
|
||||||
plans.each_pair do |key, attributes|
|
|
||||||
result << Plan.new(key, attributes.merge(active: false, version: version))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -41,7 +41,6 @@ class User < ApplicationRecord
|
||||||
include Avatarable
|
include Avatarable
|
||||||
# Include default devise modules.
|
# Include default devise modules.
|
||||||
include DeviseTokenAuth::Concerns::User
|
include DeviseTokenAuth::Concerns::User
|
||||||
include Events::Types
|
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
include Rails.application.routes.url_helpers
|
include Rails.application.routes.url_helpers
|
||||||
include Reportable
|
include Reportable
|
||||||
|
@ -78,7 +77,7 @@ class User < ApplicationRecord
|
||||||
|
|
||||||
before_validation :set_password_and_uid, on: :create
|
before_validation :set_password_and_uid, on: :create
|
||||||
|
|
||||||
after_create :create_access_token
|
after_create_commit :create_access_token
|
||||||
after_save :update_presence_in_redis, if: :saved_change_to_availability?
|
after_save :update_presence_in_redis, if: :saved_change_to_availability?
|
||||||
|
|
||||||
scope :order_by_full_name, -> { order('lower(name) ASC') }
|
scope :order_by_full_name, -> { order('lower(name) ASC') }
|
||||||
|
|
|
@ -107,5 +107,7 @@ class Twilio::IncomingMessageService
|
||||||
)
|
)
|
||||||
|
|
||||||
@message.save!
|
@message.save!
|
||||||
|
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED => e
|
||||||
|
Rails.logger.info "invalid url #{file_url} : #{e.message}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,3 +22,5 @@
|
||||||
value: false
|
value: false
|
||||||
- name: BRAND_NAME
|
- name: BRAND_NAME
|
||||||
value: 'Chatwoot'
|
value: 'Chatwoot'
|
||||||
|
- name: 'INSTALLATION_EVENTS_WEBHOOK_URL'
|
||||||
|
value:
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Events::Types
|
module Events::Types
|
||||||
|
### Installation Events ###
|
||||||
# account events
|
# account events
|
||||||
ACCOUNT_CREATED = 'account.created'
|
ACCOUNT_CREATED = 'account.created'
|
||||||
ACCOUNT_DESTROYED = 'account.destroyed'
|
ACCOUNT_DESTROYED = 'account.destroyed'
|
||||||
|
|
||||||
|
#### Account Events ###
|
||||||
# channel events
|
# channel events
|
||||||
WEBWIDGET_TRIGGERED = 'webwidget.triggered'
|
WEBWIDGET_TRIGGERED = 'webwidget.triggered'
|
||||||
|
|
||||||
|
@ -28,10 +30,7 @@ module Events::Types
|
||||||
CONTACT_CREATED = 'contact.created'
|
CONTACT_CREATED = 'contact.created'
|
||||||
CONTACT_UPDATED = 'contact.updated'
|
CONTACT_UPDATED = 'contact.updated'
|
||||||
|
|
||||||
# subscription events
|
# agent events
|
||||||
AGENT_ADDED = 'agent.added'
|
AGENT_ADDED = 'agent.added'
|
||||||
AGENT_REMOVED = 'agent.removed'
|
AGENT_REMOVED = 'agent.removed'
|
||||||
SUBSCRIPTION_CREATED = 'subscription.created'
|
|
||||||
SUBSCRIPTION_REACTIVATED = 'subscription.reactivated'
|
|
||||||
SUBSCRIPTION_DEACTIVATED = 'subscription.deactivated'
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
class Webhooks::Trigger
|
class Webhooks::Trigger
|
||||||
def self.execute(url, payload)
|
def self.execute(url, payload)
|
||||||
RestClient.post(url, payload.to_json, { content_type: :json, accept: :json })
|
RestClient.post(url, payload.to_json, { content_type: :json, accept: :json })
|
||||||
|
rescue RestClient::NotFound => e
|
||||||
|
Rails.logger.info "invalid url #{url} : #{e.message}"
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
Raven.capture_exception(e)
|
Raven.capture_exception(e)
|
||||||
end
|
end
|
||||||
|
|
6
spec/factories/installation_config.rb
Normal file
6
spec/factories/installation_config.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :installation_config do
|
||||||
|
name { 'xyc' }
|
||||||
|
value { 1.5 }
|
||||||
|
end
|
||||||
|
end
|
44
spec/listeners/installation_webhook_listener_spec.rb
Normal file
44
spec/listeners/installation_webhook_listener_spec.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
describe InstallationWebhookListener do
|
||||||
|
let(:listener) { described_class.instance }
|
||||||
|
let!(:account) { create(:account) }
|
||||||
|
let!(:event) { Events::Base.new(event_name, Time.zone.now, account: account) }
|
||||||
|
|
||||||
|
describe '#account_created' do
|
||||||
|
let(:event_name) { :'account.created' }
|
||||||
|
|
||||||
|
context 'when installation config is not configured' do
|
||||||
|
it 'does not trigger webhook' do
|
||||||
|
expect(WebhookJob).to receive(:perform_later).exactly(0).times
|
||||||
|
listener.account_created(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when installation config is configured' do
|
||||||
|
it 'triggers webhook' do
|
||||||
|
create(:installation_config, name: 'INSTALLATION_EVENTS_WEBHOOK_URL', value: 'https://test.com')
|
||||||
|
expect(WebhookJob).to receive(:perform_later).with('https://test.com', account.webhook_data.merge(event: 'account_created')).once
|
||||||
|
listener.account_created(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#account_destroyed' do
|
||||||
|
let(:event_name) { :'account.destroyed' }
|
||||||
|
|
||||||
|
context 'when installation config is not configured' do
|
||||||
|
it 'does not trigger webhook' do
|
||||||
|
expect(WebhookJob).to receive(:perform_later).exactly(0).times
|
||||||
|
listener.account_destroyed(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when installation config is configured' do
|
||||||
|
it 'triggers webhook' do
|
||||||
|
create(:installation_config, name: 'INSTALLATION_EVENTS_WEBHOOK_URL', value: 'https://test.com')
|
||||||
|
expect(WebhookJob).to receive(:perform_later).with('https://test.com', account.webhook_data.merge(event: 'account_destroyed')).once
|
||||||
|
listener.account_destroyed(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,7 +17,7 @@ describe WebhookListener do
|
||||||
|
|
||||||
context 'when webhook is not configured' do
|
context 'when webhook is not configured' do
|
||||||
it 'does not trigger webhook' do
|
it 'does not trigger webhook' do
|
||||||
expect(RestClient).to receive(:post).exactly(0).times
|
expect(WebhookJob).to receive(:perform_later).exactly(0).times
|
||||||
listener.message_created(event)
|
listener.message_created(event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,10 @@ properties:
|
||||||
description: Flag to identify if it is a private note
|
description: Flag to identify if it is a private note
|
||||||
content_type:
|
content_type:
|
||||||
type: string
|
type: string
|
||||||
enum: ['input_select', 'form', 'cards']
|
enum: ['input_email', 'cards', 'input_select', 'form' , 'article']
|
||||||
|
example: 'cards'
|
||||||
|
description: 'if you want to create custom message types'
|
||||||
content_attributes:
|
content_attributes:
|
||||||
type: object
|
type: object
|
||||||
description: options/form object
|
description: attributes based on your content type
|
||||||
|
|
||||||
|
|
|
@ -1641,14 +1641,18 @@
|
||||||
"content_type": {
|
"content_type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
"input_email",
|
||||||
|
"cards",
|
||||||
"input_select",
|
"input_select",
|
||||||
"form",
|
"form",
|
||||||
"cards"
|
"article"
|
||||||
]
|
],
|
||||||
|
"example": "cards",
|
||||||
|
"description": "if you want to create custom message types"
|
||||||
},
|
},
|
||||||
"content_attributes": {
|
"content_attributes": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "options/form object"
|
"description": "attributes based on your content type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue