Commit 52278412 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'zj-kamil-slack-slash-commands' into 'master'

Slack slash commands

## What does this MR do?

Implement Slack Slash Commands by utilizing generalized Mattermost presenter to fulfill Slack requirements.

## Why was this MR needed?

We want to expose Slack Slash Commands as a first-class service.

## What are the relevant issue numbers?

Supersedes !8007  
Closes #22182

See merge request !8126
parents 7572a314 e06f88ef
......@@ -169,7 +169,7 @@ gem 'gitlab-flowdock-git-hook', '~> 1.0.1'
gem 'gemnasium-gitlab-service', '~> 0.2'
# Slack integration
gem 'slack-notifier', '~> 1.2.0'
gem 'slack-notifier', '~> 1.5.1'
# Asana integration
gem 'asana', '~> 0.4.0'
......
......@@ -683,7 +683,7 @@ GEM
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
slack-notifier (1.2.1)
slack-notifier (1.5.1)
slop (3.6.0)
spinach (0.8.10)
colorize
......@@ -952,7 +952,7 @@ DEPENDENCIES
sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4)
simplecov (= 0.12.0)
slack-notifier (~> 1.2.0)
slack-notifier (~> 1.5.1)
spinach-rails (~> 0.2.1)
spinach-rerun-reporter (~> 0.0.2)
spring (~> 1.7.0)
......
......@@ -96,6 +96,10 @@ label {
code {
line-height: 1.8;
}
img {
margin-right: $gl-padding;
}
}
@media(max-width: $screen-xs-max) {
......
......@@ -79,7 +79,6 @@ class Project < ActiveRecord::Base
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
has_many :boards, before_add: :validate_board_limit, dependent: :destroy
has_many :chat_services
# Project services
has_one :campfire_service, dependent: :destroy
......@@ -96,6 +95,7 @@ class Project < ActiveRecord::Base
has_one :gemnasium_service, dependent: :destroy
has_one :mattermost_slash_commands_service, dependent: :destroy
has_one :mattermost_notification_service, dependent: :destroy
has_one :slack_slash_commands_service, dependent: :destroy
has_one :slack_notification_service, dependent: :destroy
has_one :buildkite_service, dependent: :destroy
has_one :bamboo_service, dependent: :destroy
......
# Base class for Chat services
# This class is not meant to be used directly, but only to inherit from.
class ChatService < Service
# This class is not meant to be used directly, but only to inherrit from.
class ChatSlashCommandsService < Service
default_value_for :category, 'chat'
has_many :chat_names, foreign_key: :service_id
prop_accessor :token
has_many :chat_names, foreign_key: :service_id, dependent: :destroy
def valid_token?(token)
self.respond_to?(:token) &&
......@@ -15,7 +17,40 @@ class ChatService < Service
[]
end
def can_test?
false
end
def fields
[
{ type: 'text', name: 'token', placeholder: '' }
]
end
def trigger(params)
raise NotImplementedError
return unless valid_token?(params[:token])
user = find_chat_user(params)
unless user
url = authorize_chat_name_url(params)
return presenter.authorize_chat_name(url)
end
Gitlab::ChatCommands::Command.new(project, user,
params).execute
end
private
def find_chat_user(params)
ChatNames::FindUserService.new(self, params).execute
end
def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute
end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end
class MattermostSlashCommandsService < ChatService
class MattermostSlashCommandsService < ChatSlashCommandsService
include TriggersHelper
prop_accessor :token
......@@ -18,32 +18,4 @@ class MattermostSlashCommandsService < ChatService
def to_param
'mattermost_slash_commands'
end
def fields
[
{ type: 'text', name: 'token', placeholder: '' }
]
end
def trigger(params)
return nil unless valid_token?(params[:token])
user = find_chat_user(params)
unless user
url = authorize_chat_name_url(params)
return Mattermost::Presenter.authorize_chat_name(url)
end
Gitlab::ChatCommands::Command.new(project, user, params).execute
end
private
def find_chat_user(params)
ChatNames::FindUserService.new(self, params).execute
end
def authorize_chat_name_url(params)
ChatNames::AuthorizeUserService.new(self, params).execute
end
end
class SlackSlashCommandsService < ChatSlashCommandsService
include TriggersHelper
def title
'Slack Command'
end
def description
"Perform common operations on GitLab in Slack"
end
def to_param
'slack_slash_commands'
end
def trigger(params)
# Format messages to be Slack-compatible
super.tap do |result|
result[:text] = format(result[:text])
end
end
private
def format(text)
Slack::Notifier::LinkFormatter.format(text) if text
end
end
......@@ -216,11 +216,12 @@ class Service < ActiveRecord::Base
jira
kubernetes
mattermost_slash_commands
mattermost_notification
pipelines_email
pivotaltracker
pushover
redmine
mattermost_notification
slack_slash_commands
slack_notification
teamcity
]
......
- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}"
.well
This service allows GitLab users to perform common operations on this
project by entering slash commands in Slack.
%br
See list of available commands in Slack after setting up this service,
by entering
%code /&lt;command&gt; help
%br
%br
To setup this service:
%ul.list-unstyled
%li
1.
= link_to 'Add a slash command', 'https://my.slack.com/services/new/slash-commands'
in your Slack team with these options:
%hr
.help-form
.form-group
= label_tag nil, 'Command', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :url, 'URL', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#url')
.form-group
= label_tag nil, 'Method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :customize_name, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#customize_name')
.form-group
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
= image_tag(asset_url('gitlab_logo.png'), width: 36, height: 36)
= link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Show this command in the autocomplete list
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
.form-group
= label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_usage_hint')
.form-group
= label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#descriptive_label')
%hr
%ul.list-unstyled
%li
2. Paste the
%strong Token
into the field below
%li
3. Select the
%strong Active
checkbox, press
%strong Save changes
and start using GitLab inside Slack!
---
title: Refactor presenters ChatCommands
merge_request: 7846
author:
......@@ -37,11 +37,11 @@ Feature: Project Services
And I fill Assembla settings
Then I should see Assembla service settings saved
Scenario: Activate Slack service
Scenario: Activate Slack notifications service
When I visit project "Shop" services page
And I click Slack service link
And I fill Slack settings
Then I should see Slack service settings saved
And I click Slack notifications service link
And I fill Slack notifications settings
Then I should see Slack Notifications service settings saved
Scenario: Activate Pushover service
When I visit project "Shop" services page
......
......@@ -137,17 +137,17 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
expect(find_field('Colorize messages').value).to eq '1'
end
step 'I click Slack service link' do
click_link 'Slack'
step 'I click Slack notifications service link' do
click_link 'Slack notifications'
end
step 'I fill Slack settings' do
step 'I fill Slack notifications settings' do
check 'Active'
fill_in 'Webhook', with: 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685'
click_button 'Save'
end
step 'I should see Slack service settings saved' do
step 'I should see Slack Notifications service settings saved' do
expect(find_field('Webhook').value).to eq 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685'
end
......
......@@ -378,7 +378,6 @@ module API
desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
},
],
'mattermost-slash-commands' => [
{
required: true,
......@@ -387,6 +386,14 @@ module API
desc: 'The Mattermost token'
}
],
'slack-slash-commands' => [
{
required: true,
name: :token,
type: String,
desc: 'The Slack token'
}
],
'pipelines-email' => [
{
required: true,
......
......@@ -42,6 +42,10 @@ module Gitlab
def find_by_iid(iid)
collection.find_by(iid: iid)
end
def presenter
Gitlab::ChatCommands::Presenter.new
end
end
end
end
......@@ -22,8 +22,6 @@ module Gitlab
end
end
private
def match_command
match = nil
service = available_commands.find do |klass|
......@@ -33,6 +31,8 @@ module Gitlab
[service, match]
end
private
def help_messages
available_commands.map(&:help_message)
end
......@@ -48,15 +48,15 @@ module Gitlab
end
def help(messages)
Mattermost::Presenter.help(messages, params[:command])
presenter.help(messages, params[:command])
end
def access_denied
Mattermost::Presenter.access_denied
presenter.access_denied
end
def present(resource)
Mattermost::Presenter.present(resource)
presenter.present(resource)
end
end
end
......
......@@ -4,7 +4,7 @@ module Gitlab
include Gitlab::Routing.url_helpers
def self.match(text)
/\Adeploy\s+(?<from>.*)\s+to+\s+(?<to>.*)\z/.match(text)
/\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text)
end
def self.help_message
......
module Mattermost
class Presenter
class << self
include Gitlab::Routing.url_helpers
module Gitlab
module ChatCommands
class Presenter
include Gitlab::Routing
def authorize_chat_name(url)
message = if url
......@@ -64,7 +64,7 @@ module Mattermost
def single_resource(resource)
return error(resource) if resource.errors.any? || !resource.persisted?
message = "### #{title(resource)}"
message = "#{title(resource)}:"
message << "\n\n#{resource.description}" if resource.try(:description)
in_channel_response(message)
......
......@@ -17,9 +17,9 @@ feature 'Admin updates settings', feature: true do
expect(page).to have_content "Application settings saved successfully"
end
scenario 'Change Slack Service template settings' do
scenario 'Change Slack Notifications Service template settings' do
click_link 'Service Templates'
click_link 'Slack'
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
......@@ -30,7 +30,7 @@ feature 'Admin updates settings', feature: true do
expect(page).to have_content 'Application settings saved successfully'
click_link 'Slack'
click_link 'Slack notifications'
page.all('input[type=checkbox]').each do |checkbox|
expect(checkbox).to be_checked
......
require 'spec_helper'
feature 'Slack slash commands', feature: true do
include WaitForAjax
given(:user) { create(:user) }
given(:project) { create(:project) }
given(:service) { project.create_slack_slash_commands_service }
background do
project.team << [user, :master]
login_as(user)
end
scenario 'user visits the slack slash command config page and shows a help message', js: true do
visit edit_namespace_project_service_path(project.namespace, project, service)
wait_for_ajax
expect(page).to have_content('This service allows GitLab users to perform common')
end
scenario 'shows the token after saving' do
visit edit_namespace_project_service_path(project.namespace, project, service)
fill_in 'service_token', with: 'token'
click_on 'Save'
value = find_field('service_token').value
expect(value).to eq('token')
end
scenario 'shows the correct trigger url' do
visit edit_namespace_project_service_path(project.namespace, project, service)
value = find_field('url').value
expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger")
end
end
......@@ -5,7 +5,9 @@ describe Gitlab::ChatCommands::Command, service: true do
let(:user) { create(:user) }
describe '#execute' do
subject { described_class.new(project, user, params).execute }
subject do
described_class.new(project, user, params).execute
end
context 'when no command is available' do
let(:params) { { text: 'issue show 1' } }
......@@ -74,7 +76,7 @@ describe Gitlab::ChatCommands::Command, service: true do
end
it 'returns action' do
expect(subject[:text]).to include('Deployment from staging to production started')
expect(subject[:text]).to include('Deployment from staging to production started.')
expect(subject[:response_type]).to be(:in_channel)
end
......@@ -91,4 +93,26 @@ describe Gitlab::ChatCommands::Command, service: true do
end
end
end
describe '#match_command' do
subject { described_class.new(project, user, params).match_command.first }
context 'IssueShow is triggered' do
let(:params) { { text: 'issue show 123' } }
it { is_expected.to eq(Gitlab::ChatCommands::IssueShow) }
end
context 'IssueCreate is triggered' do
let(:params) { { text: 'issue create my title' } }
it { is_expected.to eq(Gitlab::ChatCommands::IssueCreate) }
end
context 'IssueSearch is triggered' do
let(:params) { { text: 'issue search my query' } }
it { is_expected.to eq(Gitlab::ChatCommands::IssueSearch) }
end
end
end
......@@ -129,6 +129,7 @@ project:
- builds_email_service
- pipelines_email_service
- mattermost_slash_commands_service
- slack_slash_commands_service
- irker_service
- pivotaltracker_service
- hipchat_service
......
......@@ -10,7 +10,7 @@ describe ChatMessage::BuildMessage do
tag: false,
project_name: 'project_name',
project_url: 'example.gitlab.com',
project_url: 'http://example.gitlab.com',
commit: {
status: status,
......@@ -48,10 +48,10 @@ describe ChatMessage::BuildMessage do
end
def build_message(status_text = status)
"<example.gitlab.com|project_name>:" \
" Commit <example.gitlab.com/commit/" \
"<http://example.gitlab.com|project_name>:" \
" Commit <http://example.gitlab.com/commit/" \
"97de212e80737a608d939f648d959671fb0a0142/builds|97de212e>" \
" of <example.gitlab.com/commits/develop|develop> branch" \
" of <http://example.gitlab.com/commits/develop|develop> branch" \
" by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}"
end
end
......@@ -10,14 +10,14 @@ describe ChatMessage::IssueMessage, models: true do
username: 'test.user'
},
project_name: 'project_name',
project_url: 'somewhere.com',
project_url: 'http://somewhere.com',
object_attributes: {
title: 'Issue title',
id: 10,
iid: 100,
assignee_id: 1,
url: 'url',
url: 'http://url.com',
action: 'open',
state: 'opened',
description: 'issue description'
......@@ -40,11 +40,11 @@ describe ChatMessage::IssueMessage, models: true do
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
'<somewhere.com|[project_name>] Issue opened by test.user')
'[<http://somewhere.com|project_name>] Issue opened by test.user')
expect(subject.attachments).to eq([
{
title: "#100 Issue title",
title_link: "url",
title_link: "http://url.com",
text: "issue description",
color: color,
}
......@@ -60,7 +60,7 @@ describe ChatMessage::IssueMessage, models: true do
it 'returns a message regarding closing of issues' do
expect(subject.pretext). to eq(
'<somewhere.com|[project_name>] Issue <url|#100 Issue title> closed by test.user')
'[<http://somewhere.com|project_name>] Issue <http://url.com|#100 Issue title> closed by test.user')
expect(subject.attachments).to be_empty
end
end
......
......@@ -10,14 +10,14 @@ describe ChatMessage::MergeMessage, models: true do
username: 'test.user'
},
project_name: 'project_name',
project_url: 'somewhere.com',
project_url: 'http://somewhere.com',
object_attributes: {
title: "Issue title\nSecond line",
id: 10,
iid: 100,
assignee_id: 1,
url: 'url',
url: 'http://url.com',
state: 'opened',
description: 'issue description',
source_branch: 'source_branch',
......@@ -31,8 +31,8 @@ describe ChatMessage::MergeMessage, models: true do
context 'open' do
it 'returns a message regarding opening of merge requests' do
expect(subject.pretext).to eq(
'test.user opened <somewhere.com/merge_requests/100|merge request !100> '\
'in <somewhere.com|project_name>: *Issue title*')
'test.user opened <http://somewhere.com/merge_requests/100|merge request !100> '\
'in <http://somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
end
......@@ -43,8 +43,8 @@ describe ChatMessage::MergeMessage, models: true do
end
it 'returns a message regarding closing of merge requests' do
expect(subject.pretext).to eq(
'test.user closed <somewhere.com/merge_requests/100|merge request !100> '\
'in <somewhere.com|project_name>: *Issue title*')
'test.user closed <http://somewhere.com/merge_requests/100|merge request !100> '\
'in <http://somewhere.com|project_name>: *Issue title*')
expect(subject.attachments).to be_empty
end
end
......
......@@ -11,15 +11,15 @@ describe ChatMessage::NoteMessage, models: true do
avatar_url: 'http://fakeavatar'
},
project_name: 'project_name',
project_url: 'somewhere.com',
project_url: 'http://somewhere.com',
repository: {
name: 'project_name',
url: 'somewhere.com',
url: 'http://somewhere.com',
},
object_attributes: {
id: 10,
note: 'comment on a commit',
url: 'url',
url: 'http://url.com',
noteable_type: 'Commit'
}
}
......@@ -37,8 +37,8 @@ describe ChatMessage::NoteMessage, models: true do
it 'returns a message regarding notes on commits' do
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"commit 5f163b2b> in <somewhere.com|project_name>: " \
expect(message.pretext).to eq("test.user <http://url.com|commented on " \
"commit 5f163b2b> in <http://somewhere.com|project_name>: " \
"*Added a commit message*")
expected_attachments = [
{
......@@ -63,8 +63,8 @@ describe ChatMessage::NoteMessage, models: true do
it 'returns a message regarding notes on a merge request' do
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"merge request !30> in <somewhere.com|project_name>: " \
expect(message.pretext).to eq("test.user <http://url.com|commented on " \
"merge request !30> in <http://somewhere.com|project_name>: " \
"*merge request title*")
expected_attachments = [
{
......@@ -90,8 +90,8 @@ describe ChatMessage::NoteMessage, models: true do
it 'returns a message regarding notes on an issue' do
message = described_class.new(@args)
expect(message.pretext).to eq(
"test.user <url|commented on " \
"issue #20> in <somewhere.com|project_name>: " \
"test.user <http://url.com|commented on " \
"issue #20> in <http://somewhere.com|project_name>: " \
"*issue title*")
expected_attachments = [
{
......@@ -115,8 +115,8 @@ describe ChatMessage::NoteMessage, models: true do
it 'returns a message regarding notes on a project snippet' do
message = described_class.new(@args)
expect(message.pretext).to eq("test.user <url|commented on " \
"snippet #5> in <somewhere.com|project_name>: " \
expect(message.pretext).to eq("test.user <http://url.com|commented on " \
"snippet #5> in <http://somewhere.com|project_name>: " \
"*snippet title*")
expected_attachments = [
{
......
......@@ -15,7 +15,7 @@ describe ChatMessage::PipelineMessage do
duration: duration
},
project: { path_with_namespace: 'project_name',
web_url: 'example.gitlab.com' },
web_url: 'http://example.gitlab.com' },
user: user
}
end
......@@ -59,9 +59,9 @@ describe ChatMessage::PipelineMessage do
end
def build_message(status_text = status, name = user[:name])
"<example.gitlab.com|project_name>:" \
" Pipeline <example.gitlab.com/pipelines/123|#123>" \
" of <example.gitlab.com/commits/develop|develop> branch" \
"<http://example.gitlab.com|project_name>:" \
" Pipeline <http://example.gitlab.com/pipelines/123|#123>" \
" of <http://example.gitlab.com/commits/develop|develop> branch" \
" by #{name} #{status_text} in #{duration} #{'second'.pluralize(duration)}"
end
end
......@@ -10,7 +10,7 @@ describe ChatMessage::PushMessage, models: true do
project_name: 'project_name',
ref: 'refs/heads/master',
user_name: 'test.user',
project_url: 'url'
project_url: 'http://url.com'
}
end
......@@ -19,20 +19,20 @@ describe ChatMessage::PushMessage, models: true do
context 'push' do
before do
args[:commits] = [
{ message: 'message1', url: 'url1', id: 'abcdefghijkl', author: { name: 'author1' } },
{ message: 'message2', url: 'url2', id: '123456789012', author: { name: 'author2' } },
{ message: 'message1', url: 'http://url1.com', id: 'abcdefghijkl', author: { name: 'author1' } },
{ message: 'message2', url: 'http://url2.com', id: '123456789012', author: { name: 'author2' } },
]
end
it 'returns a message regarding pushes' do
expect(subject.pretext).to eq(
'test.user pushed to branch <url/commits/master|master> of '\
'<url|project_name> (<url/compare/before...after|Compare changes>)'
'test.user pushed to branch <http://url.com/commits/master|master> of '\
'<http://url.com|project_name> (<http://url.com/compare/before...after|Compare changes>)'
)
expect(subject.attachments).to eq([
{
text: "<url1|abcdefgh>: message1 - author1\n"\
"<url2|12345678>: message2 - author2",
text: "<http://url1.com|abcdefgh>: message1 - author1\n"\
"<http://url2.com|12345678>: message2 - author2",
color: color,
}
])
......@@ -47,14 +47,14 @@ describe ChatMessage::PushMessage, models: true do
project_name: 'project_name',
ref: 'refs/tags/new_tag',
user_name: 'test.user',
project_url: 'url'
project_url: 'http://url.com'
}
end
it 'returns a message regarding pushes' do
expect(subject.pretext).to eq('test.user pushed new tag ' \
'<url/commits/new_tag|new_tag> to ' \
'<url|project_name>')
'<http://url.com/commits/new_tag|new_tag> to ' \
'<http://url.com|project_name>')
expect(subject.attachments).to be_empty
end
end
......@@ -66,8 +66,8 @@ describe ChatMessage::PushMessage, models: true do
it 'returns a message regarding a new branch' do
expect(subject.pretext).to eq(
'test.user pushed new branch <url/commits/master|master> to '\
'<url|project_name>'
'test.user pushed new branch <http://url.com/commits/master|master> to '\
'<http://url.com|project_name>'
)
expect(subject.attachments).to be_empty
end
......@@ -80,7 +80,7 @@ describe ChatMessage::PushMessage, models: true do
it 'returns a message regarding a removed branch' do
expect(subject.pretext).to eq(
'test.user removed branch master from <url|project_name>'
'test.user removed branch master from <http://url.com|project_name>'
)
expect(subject.attachments).to be_empty
end
......
......@@ -10,10 +10,10 @@ describe ChatMessage::WikiPageMessage, models: true do
username: 'test.user'
},
project_name: 'project_name',
project_url: 'somewhere.com',
project_url: 'http://somewhere.com',
object_attributes: {
title: 'Wiki page title',
url: 'url',
url: 'http://url.com',
content: 'Wiki page description'
}
}
......@@ -25,7 +25,7 @@ describe ChatMessage::WikiPageMessage, models: true do
it 'returns a message that a new wiki page was created' do
expect(subject.pretext).to eq(
'test.user created <url|wiki page> in <somewhere.com|project_name>: '\
'test.user created <http://url.com|wiki page> in <http://somewhere.com|project_name>: '\
'*Wiki page title*')
end
end
......@@ -35,7 +35,7 @@ describe ChatMessage::WikiPageMessage, models: true do
it 'returns a message that a wiki page was updated' do
expect(subject.pretext).to eq(
'test.user edited <url|wiki page> in <somewhere.com|project_name>: '\
'test.user edited <http://url.com|wiki page> in <http://somewhere.com|project_name>: '\
'*Wiki page title*')
end
end
......
require 'spec_helper'
describe ChatService, models: true do
describe "Associations" do
it { is_expected.to have_many :chat_names }
end
describe '#valid_token?' do
subject { described_class.new }
it 'is false as it has no token' do
expect(subject.valid_token?('wer')).to be_falsey
end
end
end
require 'spec_helper'
describe MattermostNotificationService, models: true do
it_behaves_like "slack or mattermost"
it_behaves_like "slack or mattermost notifications"
end
require 'spec_helper'
describe MattermostSlashCommandsService, models: true do
describe "Associations" do
it { is_expected.to respond_to :token }
end
describe '#valid_token?' do
subject { described_class.new }
context 'when the token is empty' do
it 'is false' do
expect(subject.valid_token?('wer')).to be_falsey
end
end
context 'when there is a token' do
before do
subject.token = '123'
end
it 'accepts equal tokens' do
expect(subject.valid_token?('123')).to be_truthy
end
end
end
describe '#trigger' do
subject { described_class.new }
context 'no token is passed' do
let(:params) { Hash.new }
it 'returns nil' do
expect(subject.trigger(params)).to be_nil
end
end
context 'with a token passed' do
let(:project) { create(:empty_project) }
let(:params) { { token: 'token' } }
before do
allow(subject).to receive(:token).and_return('token')
end
context 'no user can be found' do
context 'when no url can be generated' do
it 'responds with the authorize url' do
response = subject.trigger(params)
expect(response[:response_type]).to eq :ephemeral
expect(response[:text]).to start_with ":sweat_smile: Couldn't identify you"
end
end
context 'when an auth url can be generated' do
let(:params) do
{
team_domain: 'http://domain.tld',
team_id: 'T3423423',
user_id: 'U234234',
user_name: 'mepmep',
token: 'token'
}
end
let(:service) do
project.create_mattermost_slash_commands_service(
properties: { token: 'token' }
)
end
it 'generates the url' do
response = service.trigger(params)
expect(response[:text]).to start_with(':wave: Hi there!')
end
end
end
context 'when the user is authenticated' do
let!(:chat_name) { create(:chat_name, service: service) }
let(:service) do
project.create_mattermost_slash_commands_service(
properties: { token: 'token' }
)
end
let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } }
it 'triggers the command' do
expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute)
service.trigger(params)
end
end
end
end
describe MattermostSlashCommandsService, :models do
it_behaves_like "chat slash commands service"
end
require 'spec_helper'
describe SlackNotificationService, models: true do
it_behaves_like "slack or mattermost"
it_behaves_like "slack or mattermost notifications"
end
require 'spec_helper'
describe SlackSlashCommandsService, :models do
it_behaves_like "chat slash commands service"
describe '#trigger' do
context 'when an auth url is generated' do
let(:project) { create(:empty_project) }
let(:params) do
{
team_domain: 'http://domain.tld',
team_id: 'T3423423',
user_id: 'U234234',
user_name: 'mepmep',
token: 'token'
}
end
let(:service) do
project.create_slack_slash_commands_service(
properties: { token: 'token' }
)
end
let(:authorize_url) do
'http://authorize.example.com/'
end
before do
allow(service).to receive(:authorize_chat_name_url).and_return(authorize_url)
end
it 'uses slack compatible links' do
response = service.trigger(params)
expect(response[:text]).to include("<#{authorize_url}|connect your GitLab account>")
end
end
end
end
......@@ -20,7 +20,6 @@ describe Project, models: true do
it { is_expected.to have_many(:deploy_keys) }
it { is_expected.to have_many(:hooks).dependent(:destroy) }
it { is_expected.to have_many(:protected_branches).dependent(:destroy) }
it { is_expected.to have_many(:chat_services) }
it { is_expected.to have_one(:forked_project_link).dependent(:destroy) }
it { is_expected.to have_one(:slack_notification_service).dependent(:destroy) }
it { is_expected.to have_one(:mattermost_notification_service).dependent(:destroy) }
......@@ -37,6 +36,7 @@ describe Project, models: true do
it { is_expected.to have_one(:hipchat_service).dependent(:destroy) }
it { is_expected.to have_one(:flowdock_service).dependent(:destroy) }
it { is_expected.to have_one(:assembla_service).dependent(:destroy) }
it { is_expected.to have_one(:slack_slash_commands_service).dependent(:destroy) }
it { is_expected.to have_one(:mattermost_slash_commands_service).dependent(:destroy) }
it { is_expected.to have_one(:gemnasium_service).dependent(:destroy) }
it { is_expected.to have_one(:buildkite_service).dependent(:destroy) }
......
RSpec.shared_examples 'chat slash commands service' do
describe "Associations" do
it { is_expected.to respond_to :token }
it { is_expected.to have_many :chat_names }
end
describe '#valid_token?' do
subject { described_class.new }
context 'when the token is empty' do
it 'is false' do
expect(subject.valid_token?('wer')).to be_falsey
end
end
context 'when there is a token' do
before do
subject.token = '123'
end
it 'accepts equal tokens' do
expect(subject.valid_token?('123')).to be_truthy
end
end
end
describe '#trigger' do
subject { described_class.new }
context 'no token is passed' do
let(:params) { Hash.new }
it 'returns nil' do
expect(subject.trigger(params)).to be_nil
end
end
context 'with a token passed' do
let(:project) { create(:empty_project) }
let(:params) { { token: 'token' } }
before do
allow(subject).to receive(:token).and_return('token')
end
context 'no user can be found' do
context 'when no url can be generated' do
it 'responds with the authorize url' do
response = subject.trigger(params)
expect(response[:response_type]).to eq :ephemeral
expect(response[:text]).to start_with ":sweat_smile: Couldn't identify you"
end
end
context 'when an auth url can be generated' do
let(:params) do
{
team_domain: 'http://domain.tld',
team_id: 'T3423423',
user_id: 'U234234',
user_name: 'mepmep',
token: 'token'
}
end
let(:service) do
project.create_mattermost_slash_commands_service(
properties: { token: 'token' }
)
end
it 'generates the url' do
response = service.trigger(params)
expect(response[:text]).to start_with(':wave: Hi there!')
end
end
end
context 'when the user is authenticated' do
let!(:chat_name) { create(:chat_name, service: subject) }
let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } }
subject do
described_class.create(project: project, properties: { token: 'token' })
end
it 'triggers the command' do
expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute)
subject.trigger(params)
end
end
end
end
end
Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f }
RSpec.shared_examples 'slack or mattermost' do
RSpec.shared_examples 'slack or mattermost notifications' do
let(:chat_service) { described_class.new }
let(:webhook_url) { 'https://example.gitlab.com/' }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment