From b33e20aa3cd8db885566b3e603daf0cd95dddf22 Mon Sep 17 00:00:00 2001 From: ulizman Date: Wed, 6 Sep 2017 21:19:22 +0300 Subject: [PATCH 1/3] Add migration to Intercom --- Gemfile | 2 ++ Gemfile.lock | 9 +++++- apps/web/config/routes.rb | 2 ++ apps/web/controllers/tickets/export.rb | 11 +++++++ apps/web/templates/tickets/show.html.erb | 5 +++ lib/mkdev_hanami/entities/ticket.rb | 13 ++++++++ lib/mkdev_hanami/jobs/export_tickets_job.rb | 9 ++++++ lib/mkdev_hanami/services/intercom.rb | 36 +++++++++++++++++++++ 8 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 apps/web/controllers/tickets/export.rb create mode 100644 lib/mkdev_hanami/jobs/export_tickets_job.rb create mode 100644 lib/mkdev_hanami/services/intercom.rb diff --git a/Gemfile b/Gemfile index 7a2ab49..0795d75 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,8 @@ gem 'hanami-bootstrap' gem 'omniauth-google-oauth2' gem 'warden' gem 'groovehq' +gem 'intercom' +gem 'sucker_punch', '~> 2.0' gem 'pg' diff --git a/Gemfile.lock b/Gemfile.lock index 5cc978c..919c6df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -110,6 +110,9 @@ GEM i18n (0.8.1) ice_nine (0.11.2) inflecto (0.0.2) + intercom (3.5.17) + json (>= 1.8) + json (2.1.0) jwt (1.5.6) mail (2.6.6) mime-types (>= 1.16, < 4) @@ -172,6 +175,8 @@ GEM sequel (4.49.0) shotgun (0.9.2) rack (>= 1.0) + sucker_punch (2.0.3) + concurrent-ruby (~> 1.0.0) thor (0.20.0) thread_safe (0.3.6) tilt (2.0.8) @@ -195,12 +200,14 @@ DEPENDENCIES hanami (~> 1.0) hanami-bootstrap hanami-model (~> 1.0) + intercom minitest omniauth-google-oauth2 pg rake shotgun + sucker_punch (~> 2.0) warden BUNDLED WITH - 1.14.6 + 1.15.3 diff --git a/apps/web/config/routes.rb b/apps/web/config/routes.rb index a7280ee..e9d47df 100644 --- a/apps/web/config/routes.rb +++ b/apps/web/config/routes.rb @@ -3,6 +3,8 @@ get '/', to: 'folders#index' +post '/tickets/:id/export', to: 'tickets#export', as: :export_ticket + resources :tickets resources :customers resources :folders diff --git a/apps/web/controllers/tickets/export.rb b/apps/web/controllers/tickets/export.rb new file mode 100644 index 0000000..d3c9c93 --- /dev/null +++ b/apps/web/controllers/tickets/export.rb @@ -0,0 +1,11 @@ +module Web::Controllers::Tickets + class Export + include Web::Action + + def call(params) + ticket = TicketRepository.new.find(params[:id]) + ticket.migrate + redirect_to '/' + end + end +end diff --git a/apps/web/templates/tickets/show.html.erb b/apps/web/templates/tickets/show.html.erb index 11e7f15..4cad2fe 100644 --- a/apps/web/templates/tickets/show.html.erb +++ b/apps/web/templates/tickets/show.html.erb @@ -2,6 +2,11 @@

#<%= ticket.id %>

+ <%= + form_for :ticket, routes.export_ticket_path(id: ticket.id) do + submit 'Migrate ticket to Intercom' + end + %> <% messages.each do |message| %>
diff --git a/lib/mkdev_hanami/entities/ticket.rb b/lib/mkdev_hanami/entities/ticket.rb index b4eaa07..2f3dcb5 100644 --- a/lib/mkdev_hanami/entities/ticket.rb +++ b/lib/mkdev_hanami/entities/ticket.rb @@ -1,2 +1,15 @@ +require_relative "../jobs/export_tickets_job" + class Ticket < Hanami::Entity + def migrate + ExportTicketsJob.perform_async(self) + end + + def customer + CustomerRepository.new.find(customer_id) + end + + def messages + MessageRepository.new.by_ticket(id) + end end diff --git a/lib/mkdev_hanami/jobs/export_tickets_job.rb b/lib/mkdev_hanami/jobs/export_tickets_job.rb new file mode 100644 index 0000000..80d493c --- /dev/null +++ b/lib/mkdev_hanami/jobs/export_tickets_job.rb @@ -0,0 +1,9 @@ +require_relative "../services/intercom" + +class ExportTicketsJob + include SuckerPunch::Job + + def perform(ticket) + Services::Intercom.new(ticket).export + end +end diff --git a/lib/mkdev_hanami/services/intercom.rb b/lib/mkdev_hanami/services/intercom.rb new file mode 100644 index 0000000..a44e481 --- /dev/null +++ b/lib/mkdev_hanami/services/intercom.rb @@ -0,0 +1,36 @@ +require 'intercom' + +module Services + class Intercom + attr_reader :client, :ticket, :messages, :customer + + def initialize(ticket) + @ticket = ticket + @messages = ticket.messages + @customer = ticket.customer + @client ||= ::Intercom::Client.new(token: ENV['INTERCOM_ACCESS_TOKEN']) + end + + def export + from = find_or_create_contact(customer.email) + conversation_message = client.messages.create({ from: from, body: messages.first.body }) + conversation = client.conversations.find_all(email: email).select do |conversation| + conversation.conversation_message.id == conversation_message.id + end.first + client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'comment', body: messages.last.body, admin_id: ENV["INTERCOM_ADMIN_ID"]) + if ticket.state == 'closed' + client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'close', admin_id: ENV["INTERCOM_ADMIN_ID"]) + end + end + + def find_or_create_contact(email) + contact = client.users.find(email: email) + type = 'user' + rescue ::Intercom::ResourceNotFound + contact = client.contacts.find_all(email: email).first || client.contacts.create(email: email) + type = 'contact' + ensure + return { type: type, id: contact.id } + end + end +end From e945d9d450ce52d7e84dd67661b2196640ceceb3 Mon Sep 17 00:00:00 2001 From: ulizman Date: Thu, 7 Sep 2017 16:27:19 +0300 Subject: [PATCH 2/3] Use interactors and sidekiq --- .env.development | 1 + Gemfile | 3 +- Gemfile.lock | 14 ++++-- apps/web/config/routes.rb | 8 ++-- apps/web/controllers/tickets/export.rb | 1 + apps/web/templates/application.html.erb | 7 +++ apps/web/templates/folders/index.html.erb | 1 - config/initializers/.gitkeep | 1 - config/initializers/sidekiq.rb | 7 +++ lib/mkdev_hanami/entities/ticket.rb | 8 +--- .../interactors/export_to_intercom.rb | 43 +++++++++++++++++++ lib/mkdev_hanami/jobs/export_tickets_job.rb | 9 ---- .../repositories/ticket_repository.rb | 8 ++++ lib/mkdev_hanami/services/intercom.rb | 36 ---------------- .../workers/export_tickets_worker.rb | 7 +++ 15 files changed, 93 insertions(+), 61 deletions(-) delete mode 100644 config/initializers/.gitkeep create mode 100644 config/initializers/sidekiq.rb create mode 100644 lib/mkdev_hanami/interactors/export_to_intercom.rb delete mode 100644 lib/mkdev_hanami/jobs/export_tickets_job.rb delete mode 100644 lib/mkdev_hanami/services/intercom.rb create mode 100644 lib/mkdev_hanami/workers/export_tickets_worker.rb diff --git a/.env.development b/.env.development index 90c706c..a1f65cd 100644 --- a/.env.development +++ b/.env.development @@ -2,3 +2,4 @@ DATABASE_URL="postgresql://postgres:secrets@localhost/mkdev_hanami_development" SERVE_STATIC_ASSETS="true" WEB_SESSIONS_SECRET="284cd0851871c77885d4281d081372eae4db2452fa616ebc91c3e102c4371428" +REDIS_URL="redis://localhost:6379" diff --git a/Gemfile b/Gemfile index 0795d75..852a95b 100644 --- a/Gemfile +++ b/Gemfile @@ -3,12 +3,13 @@ source 'https://rubygems.org' gem 'rake' gem 'hanami', '~> 1.0' gem 'hanami-model', '~> 1.0' +gem 'hanami-utils' gem 'hanami-bootstrap' gem 'omniauth-google-oauth2' gem 'warden' gem 'groovehq' gem 'intercom' -gem 'sucker_punch', '~> 2.0' +gem 'sidekiq' gem 'pg' diff --git a/Gemfile.lock b/Gemfile.lock index 919c6df..a64aec4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,6 +16,7 @@ GEM rack-test (>= 0.5.4) xpath (~> 2.0) concurrent-ruby (1.0.5) + connection_pool (2.2.1) dotenv (2.2.1) dry-configurable (0.7.0) concurrent-ruby (~> 1.0) @@ -147,9 +148,12 @@ GEM pg (0.21.0) public_suffix (2.0.5) rack (2.0.3) + rack-protection (2.0.0) + rack rack-test (0.7.0) rack (>= 1.0, < 3) rake (12.0.0) + redis (3.3.3) rom (3.3.1) concurrent-ruby (~> 1.0) dry-core (~> 0.3) @@ -175,8 +179,11 @@ GEM sequel (4.49.0) shotgun (0.9.2) rack (>= 1.0) - sucker_punch (2.0.3) - concurrent-ruby (~> 1.0.0) + sidekiq (5.0.4) + concurrent-ruby (~> 1.0) + connection_pool (~> 2.2, >= 2.2.0) + rack-protection (>= 1.5.0) + redis (~> 3.3, >= 3.3.3) thor (0.20.0) thread_safe (0.3.6) tilt (2.0.8) @@ -200,13 +207,14 @@ DEPENDENCIES hanami (~> 1.0) hanami-bootstrap hanami-model (~> 1.0) + hanami-utils intercom minitest omniauth-google-oauth2 pg rake shotgun - sucker_punch (~> 2.0) + sidekiq warden BUNDLED WITH diff --git a/apps/web/config/routes.rb b/apps/web/config/routes.rb index e9d47df..e259d99 100644 --- a/apps/web/config/routes.rb +++ b/apps/web/config/routes.rb @@ -3,8 +3,10 @@ get '/', to: 'folders#index' -post '/tickets/:id/export', to: 'tickets#export', as: :export_ticket - -resources :tickets +resources :tickets do + member do + post 'export' + end +end resources :customers resources :folders diff --git a/apps/web/controllers/tickets/export.rb b/apps/web/controllers/tickets/export.rb index d3c9c93..09c9113 100644 --- a/apps/web/controllers/tickets/export.rb +++ b/apps/web/controllers/tickets/export.rb @@ -3,6 +3,7 @@ class Export include Web::Action def call(params) + flash[:info] = "Exported started" ticket = TicketRepository.new.find(params[:id]) ticket.migrate redirect_to '/' diff --git a/apps/web/templates/application.html.erb b/apps/web/templates/application.html.erb index b561431..4fdf90e 100644 --- a/apps/web/templates/application.html.erb +++ b/apps/web/templates/application.html.erb @@ -20,6 +20,13 @@
+
+ <% if flash[:info] %> +
+ <%= flash[:info] %> +
+ <% end %> +
<%= yield %> diff --git a/apps/web/templates/folders/index.html.erb b/apps/web/templates/folders/index.html.erb index 05c6c95..9a00bb9 100644 --- a/apps/web/templates/folders/index.html.erb +++ b/apps/web/templates/folders/index.html.erb @@ -7,4 +7,3 @@ <% end %>
- diff --git a/config/initializers/.gitkeep b/config/initializers/.gitkeep deleted file mode 100644 index 792d600..0000000 --- a/config/initializers/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb new file mode 100644 index 0000000..c665c01 --- /dev/null +++ b/config/initializers/sidekiq.rb @@ -0,0 +1,7 @@ +Sidekiq.configure_server do |config| + config.redis = { url: ENV.fetch("REDIS_URL") } +end + +Sidekiq.configure_client do |config| + config.redis = { url: ENV.fetch("REDIS_URL") } +end diff --git a/lib/mkdev_hanami/entities/ticket.rb b/lib/mkdev_hanami/entities/ticket.rb index 2f3dcb5..2b037bd 100644 --- a/lib/mkdev_hanami/entities/ticket.rb +++ b/lib/mkdev_hanami/entities/ticket.rb @@ -1,15 +1,9 @@ -require_relative "../jobs/export_tickets_job" - class Ticket < Hanami::Entity def migrate - ExportTicketsJob.perform_async(self) + ExportTicketsWorker.perform_async(self) end def customer CustomerRepository.new.find(customer_id) end - - def messages - MessageRepository.new.by_ticket(id) - end end diff --git a/lib/mkdev_hanami/interactors/export_to_intercom.rb b/lib/mkdev_hanami/interactors/export_to_intercom.rb new file mode 100644 index 0000000..5cef4a4 --- /dev/null +++ b/lib/mkdev_hanami/interactors/export_to_intercom.rb @@ -0,0 +1,43 @@ +require 'hanami/interactor' +require 'intercom' + +class ExportToIntercom + include Hanami::Interactor + + def initialize(ticket) + @ticket = ticket + @messages = TicketRepository.new.find_with_messages(ticket.id).messages + @customer = ticket.customer + @client ||= Intercom::Client.new(token: ENV['INTERCOM_ACCESS_TOKEN']) + end + + def call + export! + end + + private + + def export! + from = find_or_create_contact(@customer.email) + conversation_message = @client.messages.create({ from: from, body: @messages.first.body }) + conversation = @client.conversations.find_all(email: @customer.email).select do |conversation| + conversation.conversation_message.id == conversation_message.id + end.first + @client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'comment', body: @messages.last.body, admin_id: ENV["INTERCOM_ADMIN_ID"]) + if ticket.state == 'closed' + @client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'close', admin_id: ENV["INTERCOM_ADMIN_ID"]) + end + rescue + fail! + end + + def find_or_create_contact(email) + contact = @client.users.find(email: email) + type = 'user' + rescue Intercom::ResourceNotFound + contact = @client.contacts.find_all(email: email).first || @client.contacts.create(email: email) + type = 'contact' + ensure + return { type: type, id: contact.id } + end +end diff --git a/lib/mkdev_hanami/jobs/export_tickets_job.rb b/lib/mkdev_hanami/jobs/export_tickets_job.rb deleted file mode 100644 index 80d493c..0000000 --- a/lib/mkdev_hanami/jobs/export_tickets_job.rb +++ /dev/null @@ -1,9 +0,0 @@ -require_relative "../services/intercom" - -class ExportTicketsJob - include SuckerPunch::Job - - def perform(ticket) - Services::Intercom.new(ticket).export - end -end diff --git a/lib/mkdev_hanami/repositories/ticket_repository.rb b/lib/mkdev_hanami/repositories/ticket_repository.rb index e1f2d1c..ac4eba3 100644 --- a/lib/mkdev_hanami/repositories/ticket_repository.rb +++ b/lib/mkdev_hanami/repositories/ticket_repository.rb @@ -1,4 +1,12 @@ class TicketRepository < Hanami::Repository + associations do + has_many :messages + end + + def find_with_messages(id) + aggregate(:messages).map_to(Ticket).one + end + def by_folder(folder_id) tickets.where("folder_ids @> ARRAY[?]::bigint[]", folder_id) end diff --git a/lib/mkdev_hanami/services/intercom.rb b/lib/mkdev_hanami/services/intercom.rb deleted file mode 100644 index a44e481..0000000 --- a/lib/mkdev_hanami/services/intercom.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'intercom' - -module Services - class Intercom - attr_reader :client, :ticket, :messages, :customer - - def initialize(ticket) - @ticket = ticket - @messages = ticket.messages - @customer = ticket.customer - @client ||= ::Intercom::Client.new(token: ENV['INTERCOM_ACCESS_TOKEN']) - end - - def export - from = find_or_create_contact(customer.email) - conversation_message = client.messages.create({ from: from, body: messages.first.body }) - conversation = client.conversations.find_all(email: email).select do |conversation| - conversation.conversation_message.id == conversation_message.id - end.first - client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'comment', body: messages.last.body, admin_id: ENV["INTERCOM_ADMIN_ID"]) - if ticket.state == 'closed' - client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'close', admin_id: ENV["INTERCOM_ADMIN_ID"]) - end - end - - def find_or_create_contact(email) - contact = client.users.find(email: email) - type = 'user' - rescue ::Intercom::ResourceNotFound - contact = client.contacts.find_all(email: email).first || client.contacts.create(email: email) - type = 'contact' - ensure - return { type: type, id: contact.id } - end - end -end diff --git a/lib/mkdev_hanami/workers/export_tickets_worker.rb b/lib/mkdev_hanami/workers/export_tickets_worker.rb new file mode 100644 index 0000000..5b1366d --- /dev/null +++ b/lib/mkdev_hanami/workers/export_tickets_worker.rb @@ -0,0 +1,7 @@ +class ExportTicketsWorker + include Sidekiq::Worker + + def perform(ticket) + Intercom.new(ticket).call + end +end From a47815aa77e678ddf4da121d10dea0520f1516b9 Mon Sep 17 00:00:00 2001 From: ulizman Date: Thu, 7 Sep 2017 16:31:44 +0300 Subject: [PATCH 3/3] Some fix --- lib/mkdev_hanami/interactors/export_to_intercom.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mkdev_hanami/interactors/export_to_intercom.rb b/lib/mkdev_hanami/interactors/export_to_intercom.rb index 5cef4a4..ed4e58d 100644 --- a/lib/mkdev_hanami/interactors/export_to_intercom.rb +++ b/lib/mkdev_hanami/interactors/export_to_intercom.rb @@ -24,7 +24,7 @@ def export! conversation.conversation_message.id == conversation_message.id end.first @client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'comment', body: @messages.last.body, admin_id: ENV["INTERCOM_ADMIN_ID"]) - if ticket.state == 'closed' + if @ticket.state == 'closed' @client.conversations.reply(id: conversation.id, type: 'admin', message_type: 'close', admin_id: ENV["INTERCOM_ADMIN_ID"]) end rescue