Defer processing of transcripts to avoid timeouts

This commit is contained in:
David Taylor 2017-09-15 23:12:02 +01:00
parent 6a976c4d66
commit 47a6a89e5a
3 changed files with 76 additions and 45 deletions

View File

@ -137,6 +137,7 @@ en:
posted_in: "posted in %{name}" posted_in: "posted in %{name}"
change_first_message: "Change first message..." change_first_message: "Change first message..."
change_last_message: "Change last message..." change_last_message: "Change last message..."
loading: "Loading the transcript..."
####################################### #######################################
########## TELEGRAM STRINGS ########### ########## TELEGRAM STRINGS ###########

View File

@ -40,54 +40,63 @@ module DiscourseChat::Provider::SlackProvider
channel ||= DiscourseChat::Channel.create!(provider: provider, data: { identifier: channel_id }) channel ||= DiscourseChat::Channel.create!(provider: provider, data: { identifier: channel_id })
if tokens[0] == 'post' if tokens[0] == 'post'
return process_post_request(channel, tokens, params[:channel_id], channel_id) return process_post_request(channel, tokens, params[:channel_id], channel_id, params[:response_url])
end end
return { text: ::DiscourseChat::Helper.process_command(channel, tokens) } return { text: ::DiscourseChat::Helper.process_command(channel, tokens) }
end end
def process_post_request(channel, tokens, slack_channel_id, channel_name) def process_post_request(channel, tokens, slack_channel_id, channel_name, response_url)
if SiteSetting.chat_integration_slack_access_token.empty? if SiteSetting.chat_integration_slack_access_token.empty?
return { text: I18n.t("chat_integration.provider.slack.transcript.api_required") } return { text: I18n.t("chat_integration.provider.slack.transcript.api_required") }
end end
requested_messages = nil Scheduler::Defer.later "Processing slack transcript request" do
first_message_ts = nil requested_messages = nil
first_message_ts = nil
slack_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p([0-9]{16})\/?$/ slack_url_regex = /^https:\/\/\S+\.slack\.com\/archives\/\S+\/p([0-9]{16})\/?$/
if tokens.size > 1 && match = slack_url_regex.match(tokens[1]) if tokens.size > 1 && match = slack_url_regex.match(tokens[1])
first_message_ts = match.captures[0].insert(10, '.') first_message_ts = match.captures[0].insert(10, '.')
elsif tokens.size > 1 elsif tokens.size > 1
begin begin
requested_messages = Integer(tokens[1], 10) requested_messages = Integer(tokens[1], 10)
rescue ArgumentError rescue ArgumentError
return { text: I18n.t("chat_integration.provider.slack.parse_error") } return { text: I18n.t("chat_integration.provider.slack.parse_error") }
end
end end
error_message = { text: I18n.t("chat_integration.provider.slack.transcript.error") }
return error_message unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id)
return error_message unless transcript.load_user_data
return error_message unless transcript.load_chat_history
if first_message_ts
return error_message unless transcript.set_first_message_by_ts(first_message_ts)
elsif requested_messages
transcript.set_first_message_by_index(-requested_messages)
else
transcript.set_first_message_by_index(-10) unless transcript.guess_first_message
end
http = Net::HTTP.new("slack.com", 443)
http.use_ssl = true
req = Net::HTTP::Post.new(URI(response_url), 'Content-Type' => 'application/json')
req.body = transcript.build_slack_ui.to_json
response = http.request(req)
end end
error_message = { text: I18n.t("chat_integration.provider.slack.transcript.error") } return { text: I18n.t("chat_integration.provider.slack.transcript.loading") }
return error_message unless transcript = SlackTranscript.new(channel_name: channel_name, channel_id: slack_channel_id)
return error_message unless transcript.load_user_data
return error_message unless transcript.load_chat_history
if first_message_ts
return error_message unless transcript.set_first_message_by_ts(first_message_ts)
elsif requested_messages
transcript.set_first_message_by_index(-requested_messages)
else
transcript.set_first_message_by_index(-10) unless transcript.guess_first_message
end
return transcript.build_slack_ui
end end
def interactive def interactive
json = JSON.parse(params[:payload], symbolize_names: true) json = JSON.parse(params[:payload], symbolize_names: true)
process_interactive(json)
render json: process_interactive(json) render nothing: true, status: 200
end end
def process_interactive(json) def process_interactive(json)
@ -101,14 +110,20 @@ module DiscourseChat::Provider::SlackProvider
error_message = { text: I18n.t("chat_integration.provider.slack.transcript.error") } error_message = { text: I18n.t("chat_integration.provider.slack.transcript.error") }
return error_message unless transcript = SlackTranscript.new(channel_name: "##{json[:channel][:name]}", channel_id: json[:channel][:id]) Scheduler::Defer.later "Processing slack transcript update" do
return error_message unless transcript.load_user_data return error_message unless transcript = SlackTranscript.new(channel_name: "##{json[:channel][:name]}", channel_id: json[:channel][:id])
return error_message unless transcript.load_chat_history return error_message unless transcript.load_user_data
return error_message unless transcript.load_chat_history
return error_message unless transcript.set_first_message_by_ts(first_message) return error_message unless transcript.set_first_message_by_ts(first_message)
return error_message unless transcript.set_last_message_by_ts(last_message) return error_message unless transcript.set_last_message_by_ts(last_message)
return transcript.build_slack_ui http = Net::HTTP.new("slack.com", 443)
http.use_ssl = true
req = Net::HTTP::Post.new(URI(json[:response_url]), 'Content-Type' => 'application/json')
req.body = transcript.build_slack_ui.to_json
response = http.request(req)
end
end end
def slack_token_valid? def slack_token_valid?

View File

@ -180,50 +180,63 @@ describe 'Slack Command Controller', type: :request do
end end
it 'generates the transcript UI properly' do it 'generates the transcript UI properly' do
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.with(body: /attachments/)
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post", text: "post",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token
json = JSON.parse(response.body) expect(command_stub).to have_been_requested
expect(json["attachments"].length).to eq(2)
end end
it 'can select by url' do it 'can select by url' do
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.with(body: /1501801629\.052212/)
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212", text: "post https://sometestslack.slack.com/archives/C6029G78F/p1501801629052212",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token
json = JSON.parse(response.body) expect(command_stub).to have_been_requested
expect(json["attachments"].length).to eq(2)
expect(json["attachments"][0]["ts"]).to eq("1501801629.052212")
end end
it 'can select by count' do it 'can select by count' do
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.with(body: /1501801629\.052212/)
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post 4", text: "post 4",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token
json = JSON.parse(response.body) expect(command_stub).to have_been_requested
expect(json["attachments"].length).to eq(2)
expect(json["attachments"][0]["ts"]).to eq("1501801629.052212")
end end
it 'can auto select' do it 'can auto select' do
command_stub = stub_request(:post, "https://slack.com/commands/1234")
.with(body: /1501615820\.949638/)
.to_return(body: { ok: true }.to_json)
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post", text: "post",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token
json = JSON.parse(response.body) expect(command_stub).to have_been_requested
expect(json["attachments"].length).to eq(2)
expect(json["attachments"][0]["ts"]).to eq("1501615820.949638")
end end
end end
@ -232,6 +245,7 @@ describe 'Slack Command Controller', type: :request do
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post 2", text: "post 2",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token
@ -246,6 +260,7 @@ describe 'Slack Command Controller', type: :request do
post "/chat-integration/slack/command.json", post "/chat-integration/slack/command.json",
text: "post 2", text: "post 2",
response_url: 'https://hooks.slack.com/commands/1234',
channel_name: 'general', channel_name: 'general',
channel_id: 'C6029G78F', channel_id: 'C6029G78F',
token: token token: token