Skip to content
Snippets Groups Projects
Unverified Commit 57719d34 authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Expose ChatName objects to slash commands

Instead of only exposing a User to slash commands we now also expose the
ChatName object that the User object is retrieved from. This is
necessary for GitLab Chatops as we need for example the user ID of the
chat user.
parent c679fa16
No related branches found
No related tags found
No related merge requests found
Showing with 81 additions and 24 deletions
class ChatName < ActiveRecord::Base
LAST_USED_AT_INTERVAL = 1.hour
belongs_to :service
belongs_to :user
 
Loading
Loading
@@ -9,4 +11,23 @@ class ChatName < ActiveRecord::Base
 
validates :user_id, uniqueness: { scope: [:service_id] }
validates :chat_id, uniqueness: { scope: [:service_id, :team_id] }
# Updates the "last_used_timestamp" but only if it wasn't already updated
# recently.
#
# The throttling this method uses is put in place to ensure that high chat
# traffic doesn't result in many UPDATE queries being performed.
def update_last_used_at
return unless update_last_used_at?
obtained = Gitlab::ExclusiveLease
.new("chat_name/last_used_at/#{id}", timeout: LAST_USED_AT_INTERVAL.to_i)
.try_obtain
touch(:last_used_at) if obtained
end
def update_last_used_at?
last_used_at.nil? || last_used_at > LAST_USED_AT_INTERVAL.ago
end
end
Loading
Loading
@@ -30,10 +30,10 @@ class SlashCommandsService < Service
def trigger(params)
return unless valid_token?(params[:token])
 
user = find_chat_user(params)
chat_user = find_chat_user(params)
 
if user
Gitlab::SlashCommands::Command.new(project, user, params).execute
if chat_user&.user
Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
else
url = authorize_chat_name_url(params)
Gitlab::SlashCommands::Presenters::Access.new(url).authorize
Loading
Loading
Loading
Loading
@@ -9,8 +9,8 @@ module ChatNames
chat_name = find_chat_name
return unless chat_name
 
chat_name.touch(:last_used_at)
chat_name.user
chat_name.update_last_used_at
chat_name
end
 
private
Loading
Loading
Loading
Loading
@@ -31,10 +31,11 @@ module Gitlab
raise NotImplementedError
end
 
attr_accessor :project, :current_user, :params
attr_accessor :project, :current_user, :params, :chat_name
 
def initialize(project, user, params = {})
@project, @current_user, @params = project, user, params.dup
def initialize(project, chat_name, params = {})
@project, @current_user, @params = project, chat_name.user, params.dup
@chat_name = chat_name
end
 
private
Loading
Loading
Loading
Loading
@@ -13,12 +13,13 @@ module Gitlab
 
if command
if command.allowed?(project, current_user)
command.new(project, current_user, params).execute(match)
command.new(project, chat_name, params).execute(match)
else
Gitlab::SlashCommands::Presenters::Access.new.access_denied
end
else
Gitlab::SlashCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
Gitlab::SlashCommands::Help.new(project, chat_name, params)
.execute(available_commands, params[:text])
end
end
 
Loading
Loading
Loading
Loading
@@ -3,10 +3,11 @@ require 'spec_helper'
describe Gitlab::SlashCommands::Command do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:chat_name) { double(:chat_name, user: user) }
 
describe '#execute' do
subject do
described_class.new(project, user, params).execute
described_class.new(project, chat_name, params).execute
end
 
context 'when no command is available' do
Loading
Loading
@@ -88,7 +89,7 @@ describe Gitlab::SlashCommands::Command do
end
 
describe '#match_command' do
subject { described_class.new(project, user, params).match_command.first }
subject { described_class.new(project, chat_name, params).match_command.first }
 
context 'IssueShow is triggered' do
let(:params) { { text: 'issue show 123' } }
Loading
Loading
Loading
Loading
@@ -4,6 +4,7 @@ describe Gitlab::SlashCommands::Deploy do
describe '#execute' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match('deploy staging to production') }
 
before do
Loading
Loading
@@ -16,7 +17,7 @@ describe Gitlab::SlashCommands::Deploy do
end
 
subject do
described_class.new(project, user).execute(regex_match)
described_class.new(project, chat_name).execute(regex_match)
end
 
context 'if no environment is defined' do
Loading
Loading
Loading
Loading
@@ -4,6 +4,7 @@ describe Gitlab::SlashCommands::IssueNew do
describe '#execute' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue create bird is the word") }
 
before do
Loading
Loading
@@ -11,7 +12,7 @@ describe Gitlab::SlashCommands::IssueNew do
end
 
subject do
described_class.new(project, user).execute(regex_match)
described_class.new(project, chat_name).execute(regex_match)
end
 
context 'without description' do
Loading
Loading
Loading
Loading
@@ -6,10 +6,11 @@ describe Gitlab::SlashCommands::IssueSearch do
let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue search find") }
 
subject do
described_class.new(project, user).execute(regex_match)
described_class.new(project, chat_name).execute(regex_match)
end
 
context 'when the user has no access' do
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:issue) { create(:issue, project: project) }
let(:project) { create(:project) }
let(:user) { issue.author }
let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
 
before do
Loading
Loading
@@ -12,7 +13,7 @@ describe Gitlab::SlashCommands::IssueShow do
end
 
subject do
described_class.new(project, user).execute(regex_match)
described_class.new(project, chat_name).execute(regex_match)
end
 
context 'the issue exists' do
Loading
Loading
Loading
Loading
@@ -14,4 +14,24 @@ describe ChatName do
 
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:service_id) }
it { is_expected.to validate_uniqueness_of(:chat_id).scoped_to(:service_id, :team_id) }
describe '#update_last_used_at', :clean_gitlab_redis_shared_state do
it 'updates the last_used_at timestamp' do
expect(subject.last_used_at).to be_nil
subject.update_last_used_at
expect(subject.last_used_at).to be_present
end
it 'does not update last_used_at if it was recently updated' do
subject.update_last_used_at
time = subject.last_used_at
subject.update_last_used_at
expect(subject.last_used_at).to eq(time)
end
end
end
require 'spec_helper'
 
describe ChatNames::FindUserService do
describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
describe '#execute' do
let(:service) { create(:service) }
 
Loading
Loading
@@ -13,21 +13,30 @@ describe ChatNames::FindUserService do
context 'when existing user is requested' do
let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } }
 
it 'returns the existing user' do
is_expected.to eq(user)
it 'returns the existing chat_name' do
is_expected.to eq(chat_name)
end
 
it 'updates when last time chat name was used' do
it 'updates the last used timestamp if one is not already set' do
expect(chat_name.last_used_at).to be_nil
 
subject
 
initial_last_used = chat_name.reload.last_used_at
expect(initial_last_used).to be_present
expect(chat_name.reload.last_used_at).to be_present
end
it 'only updates an existing timestamp once within a certain time frame' do
service = described_class.new(service, params)
expect(chat_name.last_used_at).to be_nil
service.execute
time = chat_name.reload.last_used_at
 
Timecop.travel(2.days.from_now) { described_class.new(service, params).execute }
service.execute
 
expect(chat_name.reload.last_used_at).to be > initial_last_used
expect(chat_name.reload.last_used_at).to eq(time)
end
end
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment