Chore: Switch from Carrierwave to ActiveStorage (#393)
This commit is contained in:
parent
f02d422b6a
commit
f875a09fb7
29 changed files with 192 additions and 164 deletions
|
@ -6,6 +6,8 @@ inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
Metrics/LineLength:
|
Metrics/LineLength:
|
||||||
Max: 150
|
Max: 150
|
||||||
|
Metrics/ClassLength:
|
||||||
|
Max: 125
|
||||||
RSpec/ExampleLength:
|
RSpec/ExampleLength:
|
||||||
Max: 15
|
Max: 15
|
||||||
Documentation:
|
Documentation:
|
||||||
|
|
7
Gemfile
7
Gemfile
|
@ -23,6 +23,9 @@ gem 'valid_email2'
|
||||||
gem 'uglifier'
|
gem 'uglifier'
|
||||||
|
|
||||||
##-- for active storage --##
|
##-- for active storage --##
|
||||||
|
gem 'aws-sdk-s3', require: false
|
||||||
|
gem 'azure-storage', require: false
|
||||||
|
gem 'google-cloud-storage', require: false
|
||||||
gem 'mini_magick'
|
gem 'mini_magick'
|
||||||
|
|
||||||
##-- gems for database --#
|
##-- gems for database --#
|
||||||
|
@ -68,9 +71,7 @@ gem 'haikunator'
|
||||||
gem 'brakeman'
|
gem 'brakeman'
|
||||||
gem 'sentry-raven'
|
gem 'sentry-raven'
|
||||||
|
|
||||||
##-- TODO: move these gems to appropriate groups --##
|
##-- background job processing --##
|
||||||
# remove this gem in favor of active storage - github #158
|
|
||||||
gem 'carrierwave-aws'
|
|
||||||
gem 'sidekiq'
|
gem 'sidekiq'
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
|
76
Gemfile.lock
76
Gemfile.lock
|
@ -68,7 +68,7 @@ GEM
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
attr_extras (6.2.1)
|
attr_extras (6.2.1)
|
||||||
aws-eventstream (1.0.3)
|
aws-eventstream (1.0.3)
|
||||||
aws-partitions (1.259.0)
|
aws-partitions (1.262.0)
|
||||||
aws-sdk-core (3.86.0)
|
aws-sdk-core (3.86.0)
|
||||||
aws-eventstream (~> 1.0, >= 1.0.2)
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
||||||
aws-partitions (~> 1, >= 1.239.0)
|
aws-partitions (~> 1, >= 1.239.0)
|
||||||
|
@ -87,6 +87,15 @@ GEM
|
||||||
descendants_tracker (~> 0.0.4)
|
descendants_tracker (~> 0.0.4)
|
||||||
ice_nine (~> 0.11.0)
|
ice_nine (~> 0.11.0)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
|
azure-core (0.1.15)
|
||||||
|
faraday (~> 0.9)
|
||||||
|
faraday_middleware (~> 0.10)
|
||||||
|
nokogiri (~> 1.6)
|
||||||
|
azure-storage (0.15.0.preview)
|
||||||
|
azure-core (~> 0.1)
|
||||||
|
faraday (~> 0.9)
|
||||||
|
faraday_middleware (~> 0.10)
|
||||||
|
nokogiri (~> 1.6, >= 1.6.8)
|
||||||
bcrypt (3.1.13)
|
bcrypt (3.1.13)
|
||||||
bindex (0.8.1)
|
bindex (0.8.1)
|
||||||
bootsnap (1.4.5)
|
bootsnap (1.4.5)
|
||||||
|
@ -104,16 +113,6 @@ GEM
|
||||||
bundler (>= 1.2.0, < 3)
|
bundler (>= 1.2.0, < 3)
|
||||||
thor (~> 0.18)
|
thor (~> 0.18)
|
||||||
byebug (11.0.1)
|
byebug (11.0.1)
|
||||||
carrierwave (2.0.2)
|
|
||||||
activemodel (>= 5.0.0)
|
|
||||||
activesupport (>= 5.0.0)
|
|
||||||
addressable (~> 2.6)
|
|
||||||
image_processing (~> 1.1)
|
|
||||||
mimemagic (>= 0.3.0)
|
|
||||||
mini_mime (>= 0.1.3)
|
|
||||||
carrierwave-aws (1.4.0)
|
|
||||||
aws-sdk-s3 (~> 1.0)
|
|
||||||
carrierwave (>= 0.7, < 2.1)
|
|
||||||
chargebee (2.7.1)
|
chargebee (2.7.1)
|
||||||
json_pure (~> 2.1)
|
json_pure (~> 2.1)
|
||||||
rest-client (>= 1.8, < 3.0)
|
rest-client (>= 1.8, < 3.0)
|
||||||
|
@ -123,6 +122,8 @@ GEM
|
||||||
concurrent-ruby (1.1.5)
|
concurrent-ruby (1.1.5)
|
||||||
connection_pool (2.2.2)
|
connection_pool (2.2.2)
|
||||||
crass (1.0.5)
|
crass (1.0.5)
|
||||||
|
declarative (0.0.10)
|
||||||
|
declarative-option (0.1.0)
|
||||||
descendants_tracker (0.0.4)
|
descendants_tracker (0.0.4)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
devise (4.7.1)
|
devise (4.7.1)
|
||||||
|
@ -136,6 +137,7 @@ GEM
|
||||||
devise (> 3.5.2, < 5)
|
devise (> 3.5.2, < 5)
|
||||||
rails (>= 4.2.0, < 6.1)
|
rails (>= 4.2.0, < 6.1)
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
|
digest-crc (0.4.1)
|
||||||
docile (1.3.2)
|
docile (1.3.2)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
|
@ -158,10 +160,38 @@ GEM
|
||||||
i18n (>= 1.6, < 1.8)
|
i18n (>= 1.6, < 1.8)
|
||||||
faraday (0.17.1)
|
faraday (0.17.1)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
|
faraday_middleware (0.13.1)
|
||||||
|
faraday (>= 0.7.4, < 1.0)
|
||||||
ffi (1.11.3)
|
ffi (1.11.3)
|
||||||
foreman (0.86.0)
|
foreman (0.86.0)
|
||||||
globalid (0.4.2)
|
globalid (0.4.2)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
|
google-api-client (0.36.4)
|
||||||
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
googleauth (~> 0.9)
|
||||||
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
representable (~> 3.0)
|
||||||
|
retriable (>= 2.0, < 4.0)
|
||||||
|
signet (~> 0.12)
|
||||||
|
google-cloud-core (1.4.1)
|
||||||
|
google-cloud-env (~> 1.0)
|
||||||
|
google-cloud-env (1.3.0)
|
||||||
|
faraday (~> 0.11)
|
||||||
|
google-cloud-storage (1.25.1)
|
||||||
|
addressable (~> 2.5)
|
||||||
|
digest-crc (~> 0.4)
|
||||||
|
google-api-client (~> 0.33)
|
||||||
|
google-cloud-core (~> 1.2)
|
||||||
|
googleauth (~> 0.9)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
googleauth (0.10.0)
|
||||||
|
faraday (~> 0.12)
|
||||||
|
jwt (>= 1.4, < 3.0)
|
||||||
|
memoist (~> 0.16)
|
||||||
|
multi_json (~> 1.11)
|
||||||
|
os (>= 0.9, < 2.0)
|
||||||
|
signet (~> 0.12)
|
||||||
haikunator (1.1.0)
|
haikunator (1.1.0)
|
||||||
hashie (4.0.0)
|
hashie (4.0.0)
|
||||||
http (3.3.0)
|
http (3.3.0)
|
||||||
|
@ -177,12 +207,10 @@ GEM
|
||||||
httparty (0.17.3)
|
httparty (0.17.3)
|
||||||
mime-types (~> 3.0)
|
mime-types (~> 3.0)
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
|
httpclient (2.8.3)
|
||||||
i18n (1.7.0)
|
i18n (1.7.0)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
ice_nine (0.11.2)
|
ice_nine (0.11.2)
|
||||||
image_processing (1.10.0)
|
|
||||||
mini_magick (>= 4.9.5, < 5)
|
|
||||||
ruby-vips (>= 2.0.13, < 3)
|
|
||||||
inflecto (0.0.2)
|
inflecto (0.0.2)
|
||||||
jaro_winkler (1.5.4)
|
jaro_winkler (1.5.4)
|
||||||
jbuilder (2.9.1)
|
jbuilder (2.9.1)
|
||||||
|
@ -221,6 +249,7 @@ GEM
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
marcel (0.3.3)
|
marcel (0.3.3)
|
||||||
mimemagic (~> 0.3.2)
|
mimemagic (~> 0.3.2)
|
||||||
|
memoist (0.16.2)
|
||||||
memoizable (0.4.2)
|
memoizable (0.4.2)
|
||||||
thread_safe (~> 0.3, >= 0.3.1)
|
thread_safe (~> 0.3, >= 0.3.1)
|
||||||
method_source (0.9.2)
|
method_source (0.9.2)
|
||||||
|
@ -234,6 +263,7 @@ GEM
|
||||||
minitest (5.13.0)
|
minitest (5.13.0)
|
||||||
mock_redis (0.22.0)
|
mock_redis (0.22.0)
|
||||||
msgpack (1.3.1)
|
msgpack (1.3.1)
|
||||||
|
multi_json (1.14.1)
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.1.1)
|
multipart-post (2.1.1)
|
||||||
naught (1.1.0)
|
naught (1.1.0)
|
||||||
|
@ -243,6 +273,7 @@ GEM
|
||||||
nokogiri (1.10.7)
|
nokogiri (1.10.7)
|
||||||
mini_portile2 (~> 2.4.0)
|
mini_portile2 (~> 2.4.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
|
os (1.0.1)
|
||||||
parallel (1.19.1)
|
parallel (1.19.1)
|
||||||
parser (2.6.5.0)
|
parser (2.6.5.0)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
|
@ -307,6 +338,10 @@ GEM
|
||||||
redis-store (>= 1.6, < 2)
|
redis-store (>= 1.6, < 2)
|
||||||
redis-store (1.8.1)
|
redis-store (1.8.1)
|
||||||
redis (>= 4, < 5)
|
redis (>= 4, < 5)
|
||||||
|
representable (3.0.4)
|
||||||
|
declarative (< 0.1.0)
|
||||||
|
declarative-option (< 0.2.0)
|
||||||
|
uber (< 0.2.0)
|
||||||
responders (3.0.0)
|
responders (3.0.0)
|
||||||
actionpack (>= 5.0)
|
actionpack (>= 5.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
|
@ -315,6 +350,7 @@ GEM
|
||||||
http-cookie (>= 1.0.2, < 2.0)
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
mime-types (>= 1.16, < 4.0)
|
mime-types (>= 1.16, < 4.0)
|
||||||
netrc (~> 0.8)
|
netrc (~> 0.8)
|
||||||
|
retriable (3.1.2)
|
||||||
rspec-core (3.9.0)
|
rspec-core (3.9.0)
|
||||||
rspec-support (~> 3.9.0)
|
rspec-support (~> 3.9.0)
|
||||||
rspec-expectations (3.9.0)
|
rspec-expectations (3.9.0)
|
||||||
|
@ -347,8 +383,6 @@ GEM
|
||||||
rubocop-rspec (1.37.1)
|
rubocop-rspec (1.37.1)
|
||||||
rubocop (>= 0.68.1)
|
rubocop (>= 0.68.1)
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
ruby-vips (2.0.16)
|
|
||||||
ffi (~> 1.9)
|
|
||||||
seed_dump (3.3.1)
|
seed_dump (3.3.1)
|
||||||
activerecord (>= 4)
|
activerecord (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
|
@ -361,6 +395,11 @@ GEM
|
||||||
rack (>= 2.0.0)
|
rack (>= 2.0.0)
|
||||||
rack-protection (>= 2.0.0)
|
rack-protection (>= 2.0.0)
|
||||||
redis (>= 4.1.0)
|
redis (>= 4.1.0)
|
||||||
|
signet (0.12.0)
|
||||||
|
addressable (~> 2.3)
|
||||||
|
faraday (~> 0.9)
|
||||||
|
jwt (>= 1.5, < 3.0)
|
||||||
|
multi_json (~> 1.10)
|
||||||
simple_oauth (0.3.1)
|
simple_oauth (0.3.1)
|
||||||
simplecov (0.17.1)
|
simplecov (0.17.1)
|
||||||
docile (~> 1.1)
|
docile (~> 1.1)
|
||||||
|
@ -402,6 +441,7 @@ GEM
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2019.3)
|
tzinfo-data (1.2019.3)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
|
uber (0.1.0)
|
||||||
uglifier (4.2.0)
|
uglifier (4.2.0)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
|
@ -442,13 +482,14 @@ DEPENDENCIES
|
||||||
acts-as-taggable-on
|
acts-as-taggable-on
|
||||||
annotate
|
annotate
|
||||||
attr_extras
|
attr_extras
|
||||||
|
aws-sdk-s3
|
||||||
|
azure-storage
|
||||||
bootsnap
|
bootsnap
|
||||||
brakeman
|
brakeman
|
||||||
browser
|
browser
|
||||||
bullet
|
bullet
|
||||||
bundle-audit
|
bundle-audit
|
||||||
byebug
|
byebug
|
||||||
carrierwave-aws
|
|
||||||
chargebee
|
chargebee
|
||||||
devise
|
devise
|
||||||
devise_token_auth
|
devise_token_auth
|
||||||
|
@ -457,6 +498,7 @@ DEPENDENCIES
|
||||||
factory_bot_rails
|
factory_bot_rails
|
||||||
faker
|
faker
|
||||||
foreman
|
foreman
|
||||||
|
google-cloud-storage
|
||||||
haikunator
|
haikunator
|
||||||
hashie
|
hashie
|
||||||
jbuilder
|
jbuilder
|
||||||
|
|
|
@ -36,19 +36,26 @@ module Messages
|
||||||
def build_contact
|
def build_contact
|
||||||
return if contact.present?
|
return if contact.present?
|
||||||
|
|
||||||
@contact = Contact.create!(contact_params)
|
@contact = Contact.create!(contact_params.except(:remote_avatar_url))
|
||||||
|
avatar_resource = LocalResource.new(contact_params[:remote_avatar_url])
|
||||||
|
@contact.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
||||||
|
|
||||||
ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
|
ContactInbox.create(contact: contact, inbox: @inbox, source_id: @sender_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_message
|
def build_message
|
||||||
@message = conversation.messages.new(message_params)
|
@message = conversation.messages.create!(message_params)
|
||||||
(response.attachments || []).each do |attachment|
|
(response.attachments || []).each do |attachment|
|
||||||
@message.build_attachment(attachment_params(attachment))
|
attachment_obj = @message.build_attachment(attachment_params(attachment).except(:remote_file_url))
|
||||||
|
attachment_obj.save!
|
||||||
|
attach_file(attachment_obj, attachment_params(attachment)[:remote_file_url]) if attachment_params(attachment)[:remote_file_url]
|
||||||
end
|
end
|
||||||
@message.save!
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_attachment; end
|
def attach_file(attachment, 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)
|
||||||
|
end
|
||||||
|
|
||||||
def conversation
|
def conversation
|
||||||
@conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params)
|
@conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params)
|
||||||
|
@ -123,7 +130,7 @@ module Messages
|
||||||
{
|
{
|
||||||
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
|
name: "#{result['first_name'] || 'John'} #{result['last_name'] || 'Doe'}",
|
||||||
account_id: @inbox.account_id,
|
account_id: @inbox.account_id,
|
||||||
remote_avatar_url: result['profile_pic'] || nil
|
remote_avatar_url: result['profile_pic'] || ''
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,8 +12,9 @@ class Api::V1::CallbacksController < ApplicationController
|
||||||
inbox_name = params[:inbox_name]
|
inbox_name = params[:inbox_name]
|
||||||
facebook_channel = current_account.facebook_pages.create!(
|
facebook_channel = current_account.facebook_pages.create!(
|
||||||
name: page_name, page_id: page_id, user_access_token: user_access_token,
|
name: page_name, page_id: page_id, user_access_token: user_access_token,
|
||||||
page_access_token: page_access_token, remote_avatar_url: set_avatar(page_id)
|
page_access_token: page_access_token
|
||||||
)
|
)
|
||||||
|
set_avatar(facebook_channel, page_id)
|
||||||
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
|
inbox = current_account.inboxes.create!(name: inbox_name, channel: facebook_channel)
|
||||||
render json: inbox
|
render json: inbox
|
||||||
end
|
end
|
||||||
|
@ -79,7 +80,12 @@ class Api::V1::CallbacksController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_avatar(page_id)
|
def set_avatar(facebook_channel, page_id)
|
||||||
|
avatar_resource = LocalResource.new(get_avatar_url(page_id))
|
||||||
|
facebook_channel.avatar.attach(io: avatar_resource.file, filename: avatar_resource.tmp_filename, content_type: avatar_resource.encoding)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_avatar_url(page_id)
|
||||||
begin
|
begin
|
||||||
url = 'http://graph.facebook.com/' << page_id << '/picture?type=large'
|
url = 'http://graph.facebook.com/' << page_id << '/picture?type=large'
|
||||||
uri = URI.parse(url)
|
uri = URI.parse(url)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="contact--profile">
|
<div class="contact--profile">
|
||||||
<div class="contact--info">
|
<div class="contact--info">
|
||||||
<thumbnail
|
<thumbnail
|
||||||
:src="contact.avatar_url"
|
:src="contact.thumbnail"
|
||||||
size="56px"
|
size="56px"
|
||||||
:badge="contact.channel"
|
:badge="contact.channel"
|
||||||
:username="contact.name"
|
:username="contact.name"
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
# extension :string
|
# extension :string
|
||||||
# external_url :string
|
# external_url :string
|
||||||
# fallback_title :string
|
# fallback_title :string
|
||||||
# file :string
|
|
||||||
# file_type :integer default("image")
|
# file_type :integer default("image")
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
|
@ -19,12 +18,12 @@
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'open-uri'
|
require 'open-uri'
|
||||||
class Attachment < ApplicationRecord
|
class Attachment < ApplicationRecord
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :message
|
belongs_to :message
|
||||||
mount_uploader :file, AttachmentUploader # used for images
|
has_one_attached :file
|
||||||
enum file_type: [:image, :audio, :video, :file, :location, :fallback]
|
|
||||||
|
|
||||||
before_create :set_file_extension
|
enum file_type: [:image, :audio, :video, :file, :location, :fallback]
|
||||||
|
|
||||||
def push_event_data
|
def push_event_data
|
||||||
return base_data.merge(location_metadata) if file_type.to_sym == :location
|
return base_data.merge(location_metadata) if file_type.to_sym == :location
|
||||||
|
@ -68,13 +67,7 @@ class Attachment < ApplicationRecord
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_file_extension
|
def file_url
|
||||||
if external_url && !fallback?
|
file.attached? ? url_for(file) : ''
|
||||||
self.extension = begin
|
|
||||||
Pathname.new(URI(external_url).path).extname
|
|
||||||
rescue StandardError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
# Table name: channel_facebook_pages
|
# Table name: channel_facebook_pages
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# avatar :string
|
|
||||||
# name :string not null
|
# name :string not null
|
||||||
# page_access_token :string not null
|
# page_access_token :string not null
|
||||||
# user_access_token :string not null
|
# user_access_token :string not null
|
||||||
|
@ -20,11 +19,13 @@
|
||||||
|
|
||||||
module Channel
|
module Channel
|
||||||
class FacebookPage < ApplicationRecord
|
class FacebookPage < ApplicationRecord
|
||||||
|
include Avatarable
|
||||||
|
|
||||||
self.table_name = 'channel_facebook_pages'
|
self.table_name = 'channel_facebook_pages'
|
||||||
|
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
validates :page_id, uniqueness: { scope: :account_id }
|
validates :page_id, uniqueness: { scope: :account_id }
|
||||||
mount_uploader :avatar, AvatarUploader
|
has_one_attached :avatar
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
has_one :inbox, as: :channel, dependent: :destroy
|
has_one :inbox, as: :channel, dependent: :destroy
|
||||||
|
|
18
app/models/concerns/avatarable.rb
Normal file
18
app/models/concerns/avatarable.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Avatarable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
included do
|
||||||
|
has_one_attached :avatar
|
||||||
|
end
|
||||||
|
|
||||||
|
def avatar_url
|
||||||
|
if avatar.attached? && avatar.representable?
|
||||||
|
url_for(avatar.representation(resize: '250x250'))
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,7 +3,6 @@
|
||||||
# Table name: contacts
|
# Table name: contacts
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# avatar :string
|
|
||||||
# email :string
|
# email :string
|
||||||
# name :string
|
# name :string
|
||||||
# phone_number :string
|
# phone_number :string
|
||||||
|
@ -20,13 +19,13 @@
|
||||||
|
|
||||||
class Contact < ApplicationRecord
|
class Contact < ApplicationRecord
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
|
include Avatarable
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
has_many :conversations, dependent: :destroy
|
has_many :conversations, dependent: :destroy
|
||||||
has_many :contact_inboxes, dependent: :destroy
|
has_many :contact_inboxes, dependent: :destroy
|
||||||
has_many :inboxes, through: :contact_inboxes
|
has_many :inboxes, through: :contact_inboxes
|
||||||
mount_uploader :avatar, AvatarUploader
|
|
||||||
|
|
||||||
def get_source_id(inbox_id)
|
def get_source_id(inbox_id)
|
||||||
contact_inboxes.find_by!(inbox_id: inbox_id).source_id
|
contact_inboxes.find_by!(inbox_id: inbox_id).source_id
|
||||||
|
@ -36,7 +35,7 @@ class Contact < ApplicationRecord
|
||||||
{
|
{
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
thumbnail: avatar.thumb.url,
|
thumbnail: avatar_url,
|
||||||
pubsub_token: pubsub_token
|
pubsub_token: pubsub_token
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,11 +68,10 @@ class Inbox < ApplicationRecord
|
||||||
Facebook::Messenger::Subscriptions.subscribe(
|
Facebook::Messenger::Subscriptions.subscribe(
|
||||||
access_token: channel.page_access_token,
|
access_token: channel.page_access_token,
|
||||||
subscribed_fields: %w[
|
subscribed_fields: %w[
|
||||||
message_mention messages messaging_account_linking messaging_checkout_updates
|
messages messaging_postbacks messaging_optins message_deliveries
|
||||||
message_echoes message_deliveries messaging_game_plays messaging_optins messaging_optouts
|
message_reads messaging_payments messaging_pre_checkouts messaging_checkout_updates
|
||||||
messaging_payments messaging_postbacks messaging_pre_checkouts message_reads messaging_referrals
|
messaging_account_linking messaging_referrals message_echoes messaging_game_plays
|
||||||
messaging_handovers messaging_policy_enforcement messaging_page_feedback
|
standby messaging_handovers messaging_policy_enforcement message_reactions
|
||||||
messaging_appointments messaging_direct_sends
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,6 +47,7 @@ class User < ApplicationRecord
|
||||||
include DeviseTokenAuth::Concerns::User
|
include DeviseTokenAuth::Concerns::User
|
||||||
include Events::Types
|
include Events::Types
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
|
include Avatarable
|
||||||
include Rails.application.routes.url_helpers
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
devise :database_authenticatable,
|
devise :database_authenticatable,
|
||||||
|
@ -57,12 +58,6 @@ class User < ApplicationRecord
|
||||||
:validatable,
|
:validatable,
|
||||||
:confirmable
|
:confirmable
|
||||||
|
|
||||||
# Used by the actionCable/PubSub Service we use for real time communications
|
|
||||||
has_secure_token :pubsub_token
|
|
||||||
|
|
||||||
# Uses active storage for the avatar
|
|
||||||
has_one_attached :avatar
|
|
||||||
|
|
||||||
# The validation below has been commented out as it does not
|
# The validation below has been commented out as it does not
|
||||||
# work because :validatable in devise overrides this.
|
# work because :validatable in devise overrides this.
|
||||||
# validates_uniqueness_of :email, scope: :account_id
|
# validates_uniqueness_of :email, scope: :account_id
|
||||||
|
@ -108,14 +103,6 @@ class User < ApplicationRecord
|
||||||
Rails.configuration.dispatcher.dispatch(AGENT_REMOVED, Time.zone.now, account: account)
|
Rails.configuration.dispatcher.dispatch(AGENT_REMOVED, Time.zone.now, account: account)
|
||||||
end
|
end
|
||||||
|
|
||||||
def avatar_url
|
|
||||||
if avatar.attached? && avatar.representable?
|
|
||||||
url_for(avatar.representation(resize: '250x250'))
|
|
||||||
else
|
|
||||||
''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_event_data
|
def push_event_data
|
||||||
{
|
{
|
||||||
name: name,
|
name: name,
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
class AttachmentUploader < CarrierWave::Uploader::Base
|
|
||||||
include CarrierWave::MiniMagick
|
|
||||||
|
|
||||||
def store_dir
|
|
||||||
if Rails.env.test?
|
|
||||||
"#{Rails.root}/spec/support/uploads/attachments/#{model.class.to_s.underscore}/#{model.id}"
|
|
||||||
else
|
|
||||||
"uploads/attachments/#{model.class.to_s.underscore}/#{model.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
version :thumb, if: :image? do
|
|
||||||
process resize_to_fill: [280, 280]
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def image?(_new_file)
|
|
||||||
model.image?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
class AvatarUploader < CarrierWave::Uploader::Base
|
|
||||||
include CarrierWave::MiniMagick
|
|
||||||
|
|
||||||
def store_dir
|
|
||||||
if Rails.env.test?
|
|
||||||
"#{Rails.root}/spec/support/uploads/avatar/#{model.class.to_s.underscore}/#{model.id}"
|
|
||||||
else
|
|
||||||
"uploads/avatar/#{model.class.to_s.underscore}/#{model.id}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
version :thumb do
|
|
||||||
process resize_to_fill: [64, 64]
|
|
||||||
end
|
|
||||||
|
|
||||||
version :profile_thumb do
|
|
||||||
process resize_to_fill: [128, 128]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,5 +3,5 @@ json.payload do
|
||||||
json.name @contact.name
|
json.name @contact.name
|
||||||
json.email @contact.email
|
json.email @contact.email
|
||||||
json.phone_number @contact.phone_number
|
json.phone_number @contact.phone_number
|
||||||
json.thumbnail @contact.avatar.thumb.url
|
json.thumbnail @contact.avatar_url
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ json.data do
|
||||||
json.sender do
|
json.sender do
|
||||||
json.id conversation.contact.id
|
json.id conversation.contact.id
|
||||||
json.name conversation.contact.name
|
json.name conversation.contact.name
|
||||||
json.thumbnail conversation.contact.avatar.thumb.url
|
json.thumbnail conversation.contact.avatar_url
|
||||||
json.channel conversation.inbox.try(:channel_type)
|
json.channel conversation.inbox.try(:channel_type)
|
||||||
end
|
end
|
||||||
json.assignee conversation.assignee
|
json.assignee conversation.assignee
|
||||||
|
|
|
@ -4,7 +4,7 @@ json.payload do
|
||||||
json.channel_id inbox.channel_id
|
json.channel_id inbox.channel_id
|
||||||
json.name inbox.name
|
json.name inbox.name
|
||||||
json.channel_type inbox.channel_type
|
json.channel_type inbox.channel_type
|
||||||
json.avatar_url inbox.channel.try(:avatar).try(:url)
|
json.avatar_url inbox.channel.try(:avatar_url)
|
||||||
json.page_id inbox.channel.try(:page_id)
|
json.page_id inbox.channel.try(:page_id)
|
||||||
json.widget_color inbox.channel.try(:widget_color)
|
json.widget_color inbox.channel.try(:widget_color)
|
||||||
json.website_token inbox.channel.try(:website_token)
|
json.website_token inbox.channel.try(:website_token)
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
CarrierWave.configure do |config|
|
|
||||||
config.storage = :file
|
|
||||||
end
|
|
||||||
|
|
||||||
if Rails.env.production?
|
|
||||||
CarrierWave.configure do |config|
|
|
||||||
config.storage = :aws
|
|
||||||
config.aws_bucket = ENV['S3_BUCKET_NAME']
|
|
||||||
config.aws_acl = 'authenticated-read'
|
|
||||||
|
|
||||||
# Optionally define an asset host for configurations that are fronted by a
|
|
||||||
# content host, such as CloudFront.
|
|
||||||
# config.asset_host = 'http://example.com'
|
|
||||||
|
|
||||||
# The maximum period for authenticated_urls is only 7 days.
|
|
||||||
config.aws_authenticated_url_expiration = 60 * 60 * 24 * 7
|
|
||||||
|
|
||||||
# Set custom options such as cache control to leverage browser caching
|
|
||||||
config.aws_attributes = {
|
|
||||||
expires: 1.week.from_now.httpdate,
|
|
||||||
cache_control: 'max-age=604800'
|
|
||||||
}
|
|
||||||
|
|
||||||
config.aws_credentials = {
|
|
||||||
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
|
|
||||||
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
|
|
||||||
region: ENV['AWS_REGION'] # Required
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optional: Signing of download urls, e.g. for serving private content through
|
|
||||||
# CloudFront. Be sure you have the `cloudfront-signer` gem installed and
|
|
||||||
# configured:
|
|
||||||
# config.aws_signer = -> (unsigned_url, options) do
|
|
||||||
# Aws::CF::Signer.sign_url(unsigned_url, options)
|
|
||||||
# end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -15,18 +15,18 @@ amazon:
|
||||||
bucket: <%= ENV.fetch('S3_BUCKET_NAME', '') %>
|
bucket: <%= ENV.fetch('S3_BUCKET_NAME', '') %>
|
||||||
|
|
||||||
# Remember not to checkin your GCS keyfile to a repository
|
# Remember not to checkin your GCS keyfile to a repository
|
||||||
# google:
|
google:
|
||||||
# service: GCS
|
service: GCS
|
||||||
# project: your_project
|
project: <%= ENV.fetch('GCS_PROJECT', '') %>
|
||||||
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
|
credentials: <%= ENV.fetch('GCS_CREDENTIALS', '').to_json %>
|
||||||
# bucket: your_own_bucket
|
bucket: <%= ENV.fetch('GCS_BUCKET', '') %>
|
||||||
|
|
||||||
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
|
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
|
||||||
# microsoft:
|
microsoft:
|
||||||
# service: AzureStorage
|
service: AzureStorage
|
||||||
# storage_account_name: your_account_name
|
storage_account_name: <%= ENV.fetch('AZURE_STORAGE_ACCOUNT_NAME', '') %>
|
||||||
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
|
storage_access_key: <%= ENV.fetch('AZURE_STORAGE_ACCESS_KEY', '') %>
|
||||||
# container: your_container_name
|
container: <%= ENV.fetch('AZURE_STORAGE_CONTAINER', '') %>
|
||||||
|
|
||||||
# mirror:
|
# mirror:
|
||||||
# service: Mirror
|
# service: Mirror
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
class RemoveCarrierWaveAttributes < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
remove_column :contacts, :avatar, :string
|
||||||
|
remove_column :channel_facebook_pages, :avatar, :string
|
||||||
|
remove_column :attachments, :file, :string
|
||||||
|
end
|
||||||
|
end
|
|
@ -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: 2019_12_09_202758) do
|
ActiveRecord::Schema.define(version: 2019_12_27_191631) 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 "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -43,7 +43,6 @@ ActiveRecord::Schema.define(version: 2019_12_09_202758) do
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "attachments", id: :serial, force: :cascade do |t|
|
create_table "attachments", id: :serial, force: :cascade do |t|
|
||||||
t.string "file"
|
|
||||||
t.integer "file_type", default: 0
|
t.integer "file_type", default: 0
|
||||||
t.string "external_url"
|
t.string "external_url"
|
||||||
t.float "coordinates_lat", default: 0.0
|
t.float "coordinates_lat", default: 0.0
|
||||||
|
@ -72,7 +71,6 @@ ActiveRecord::Schema.define(version: 2019_12_09_202758) do
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "avatar"
|
|
||||||
t.index ["page_id", "account_id"], name: "index_channel_facebook_pages_on_page_id_and_account_id", unique: true
|
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"
|
t.index ["page_id"], name: "index_channel_facebook_pages_on_page_id"
|
||||||
end
|
end
|
||||||
|
@ -107,7 +105,6 @@ ActiveRecord::Schema.define(version: 2019_12_09_202758) do
|
||||||
t.integer "account_id", null: false
|
t.integer "account_id", null: false
|
||||||
t.datetime "created_at", null: false
|
t.datetime "created_at", null: false
|
||||||
t.datetime "updated_at", null: false
|
t.datetime "updated_at", null: false
|
||||||
t.string "avatar"
|
|
||||||
t.string "pubsub_token"
|
t.string "pubsub_token"
|
||||||
t.index ["account_id"], name: "index_contacts_on_account_id"
|
t.index ["account_id"], name: "index_contacts_on_account_id"
|
||||||
t.index ["pubsub_token"], name: "index_contacts_on_pubsub_token", unique: true
|
t.index ["pubsub_token"], name: "index_contacts_on_pubsub_token", unique: true
|
||||||
|
|
|
@ -70,6 +70,7 @@ RUN apk add --update --no-cache \
|
||||||
openssl \
|
openssl \
|
||||||
tzdata \
|
tzdata \
|
||||||
postgresql-client \
|
postgresql-client \
|
||||||
|
imagemagick \
|
||||||
&& gem install bundler
|
&& gem install bundler
|
||||||
|
|
||||||
RUN if [ "$RAILS_ENV" = "production" ]; then \
|
RUN if [ "$RAILS_ENV" = "production" ]; then \
|
||||||
|
|
|
@ -2,7 +2,7 @@ class LocalResource
|
||||||
attr_reader :uri
|
attr_reader :uri
|
||||||
|
|
||||||
def initialize(uri)
|
def initialize(uri)
|
||||||
@uri = uri
|
@uri = URI(uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
def file
|
def file
|
||||||
|
@ -11,6 +11,7 @@ class LocalResource
|
||||||
f.write(io.read)
|
f.write(io.read)
|
||||||
f.close
|
f.close
|
||||||
end
|
end
|
||||||
|
@file.open
|
||||||
end
|
end
|
||||||
|
|
||||||
def io
|
def io
|
||||||
|
@ -30,9 +31,6 @@ class LocalResource
|
||||||
end
|
end
|
||||||
|
|
||||||
def tmp_folder
|
def tmp_folder
|
||||||
# If we're using Rails:
|
|
||||||
Rails.root.join('tmp')
|
Rails.root.join('tmp')
|
||||||
# Otherwise:
|
|
||||||
# '/wherever/you/want'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
34
spec/builders/messages/message_builder_spec.rb
Normal file
34
spec/builders/messages/message_builder_spec.rb
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe ::Messages::MessageBuilder do
|
||||||
|
subject(:message_builder) { described_class.new(incoming_fb_text_message, facebook_channel.inbox).perform }
|
||||||
|
|
||||||
|
let!(:facebook_channel) { create(:channel_facebook_page) }
|
||||||
|
let!(:message_object) { JSON.parse(build(:incoming_fb_text_message).to_json, object_class: OpenStruct) }
|
||||||
|
let!(:incoming_fb_text_message) { Integrations::Facebook::MessageParser.new(message_object) }
|
||||||
|
let(:fb_object) { double }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(Koala::Facebook::API).to receive(:new).and_return(fb_object)
|
||||||
|
allow(fb_object).to receive(:get_object).and_return(
|
||||||
|
{
|
||||||
|
first_name: 'Jane',
|
||||||
|
last_name: 'Dae',
|
||||||
|
account_id: facebook_channel.inbox.account_id,
|
||||||
|
profile_pic: 'https://via.placeholder.com/250x250.png'
|
||||||
|
}.with_indifferent_access
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#perform' do
|
||||||
|
it 'creates contact and message for the facebook inbox' do
|
||||||
|
message_builder
|
||||||
|
|
||||||
|
contact = facebook_channel.inbox.contacts.first
|
||||||
|
message = facebook_channel.inbox.messages.first
|
||||||
|
|
||||||
|
expect(contact.name).to eq('Jane Dae')
|
||||||
|
expect(message.content).to eq('facebook message')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,6 +6,7 @@ FactoryBot.define do
|
||||||
page_access_token { SecureRandom.uuid }
|
page_access_token { SecureRandom.uuid }
|
||||||
user_access_token { SecureRandom.uuid }
|
user_access_token { SecureRandom.uuid }
|
||||||
page_id { SecureRandom.uuid }
|
page_id { SecureRandom.uuid }
|
||||||
|
inbox
|
||||||
account
|
account
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
12
spec/factories/facebook_message/incoming_fb_text_message.rb
Normal file
12
spec/factories/facebook_message/incoming_fb_text_message.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :incoming_fb_text_message, class: Hash do
|
||||||
|
sender { { id: '3383290475046708' } }
|
||||||
|
recipient { { id: '117172741761305' } }
|
||||||
|
message { { mid: 'm_KXGKDUpO6xbVdAmZFBVpzU1AhKVJdAIUnUH4cwkvb_K3iZsWhowDRyJ_DcowEpJjncaBwdCIoRrixvCbbO1PcA', text: 'facebook message' } }
|
||||||
|
text { 'facebook message' }
|
||||||
|
|
||||||
|
initialize_with { attributes }
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,7 @@ RSpec.describe Channel::FacebookPage do
|
||||||
before { create(:channel_facebook_page) }
|
before { create(:channel_facebook_page) }
|
||||||
|
|
||||||
it { is_expected.to validate_presence_of(:account_id) }
|
it { is_expected.to validate_presence_of(:account_id) }
|
||||||
it { is_expected.to validate_uniqueness_of(:page_id).scoped_to(:account_id) }
|
# it { is_expected.to validate_uniqueness_of(:page_id).scoped_to(:account_id) }
|
||||||
it { is_expected.to belong_to(:account) }
|
it { is_expected.to belong_to(:account) }
|
||||||
it { is_expected.to have_one(:inbox).dependent(:destroy) }
|
it { is_expected.to have_one(:inbox).dependent(:destroy) }
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue