Merge branch 'zj-slash-commands-mattermost' into 'master'
Slash command for mattermost Closes #22540 ## Does this MR meet the acceptance criteria? - [x] [Changelog entry](https://docs.gitlab.com/ce/development/changelog.html) added - [ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md) - Tests - [x] Added for this feature/bug - [x] All builds are passing - [x] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html) - [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides) - [x] Branch has no merge conflicts with `master` (if it does - rebase it please) See merge request !7438
This commit is contained in:
commit
ffc5fc6a38
|
@ -28,6 +28,8 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def test
|
||||
return render_404 unless @service.can_test?
|
||||
|
||||
data = @service.test_data(project, current_user)
|
||||
outcome = @service.test(data)
|
||||
|
||||
|
|
|
@ -6,4 +6,8 @@ module TriggersHelper
|
|||
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds"
|
||||
end
|
||||
end
|
||||
|
||||
def service_trigger_url(service)
|
||||
"#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,9 @@ class Project < ActiveRecord::Base
|
|||
|
||||
cache_markdown_field :description, pipeline: :description
|
||||
|
||||
delegate :feature_available?, :builds_enabled?, :wiki_enabled?, :merge_requests_enabled?, to: :project_feature, allow_nil: true
|
||||
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
|
||||
:merge_requests_enabled?, :issues_enabled?, to: :project_feature,
|
||||
allow_nil: true
|
||||
|
||||
default_value_for :archived, false
|
||||
default_value_for :visibility_level, gitlab_config_features.visibility_level
|
||||
|
@ -75,6 +77,7 @@ 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
|
||||
|
@ -89,6 +92,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :assembla_service, dependent: :destroy
|
||||
has_one :asana_service, dependent: :destroy
|
||||
has_one :gemnasium_service, dependent: :destroy
|
||||
has_one :mattermost_slash_commands_service, dependent: :destroy
|
||||
has_one :slack_service, dependent: :destroy
|
||||
has_one :buildkite_service, dependent: :destroy
|
||||
has_one :bamboo_service, dependent: :destroy
|
||||
|
|
|
@ -60,6 +60,10 @@ class ProjectFeature < ActiveRecord::Base
|
|||
merge_requests_access_level > DISABLED
|
||||
end
|
||||
|
||||
def issues_enabled?
|
||||
issues_access_level > DISABLED
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Validates builds and merge requests access level
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Base class for Chat services
|
||||
# This class is not meant to be used directly, but only to inherrit from.
|
||||
class ChatService < Service
|
||||
default_value_for :category, 'chat'
|
||||
|
||||
has_many :chat_names, foreign_key: :service_id
|
||||
|
||||
def valid_token?(token)
|
||||
self.respond_to?(:token) &&
|
||||
self.token.present? &&
|
||||
ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
|
||||
end
|
||||
|
||||
def supported_events
|
||||
[]
|
||||
end
|
||||
|
||||
def trigger(params)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
class MattermostSlashCommandsService < ChatService
|
||||
include TriggersHelper
|
||||
|
||||
prop_accessor :token
|
||||
|
||||
def can_test?
|
||||
false
|
||||
end
|
||||
|
||||
def title
|
||||
'Mattermost Command'
|
||||
end
|
||||
|
||||
def description
|
||||
"Perform common operations on GitLab in Mattermost"
|
||||
end
|
||||
|
||||
def to_param
|
||||
'mattermost_slash_commands'
|
||||
end
|
||||
|
||||
def help
|
||||
"This service allows you to use slash commands with your Mattermost installation.<br/>
|
||||
To setup this Service you need to create a new <b>Slash commands</b> in your Mattermost integration panel.<br/>
|
||||
<br/>
|
||||
Create integration with URL #{service_trigger_url(self)} and enter the token below."
|
||||
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
|
|
@ -202,7 +202,6 @@ class Service < ActiveRecord::Base
|
|||
bamboo
|
||||
buildkite
|
||||
builds_email
|
||||
pipelines_email
|
||||
bugzilla
|
||||
campfire
|
||||
custom_issue_tracker
|
||||
|
@ -214,6 +213,8 @@ class Service < ActiveRecord::Base
|
|||
hipchat
|
||||
irker
|
||||
jira
|
||||
mattermost_slash_commands
|
||||
pipelines_email
|
||||
pivotaltracker
|
||||
pushover
|
||||
redmine
|
||||
|
|
|
@ -10,26 +10,27 @@
|
|||
.col-sm-10
|
||||
= form.check_box :active
|
||||
|
||||
.form-group
|
||||
= form.label :url, "Trigger", class: 'control-label'
|
||||
- if @service.supported_events.present?
|
||||
.form-group
|
||||
= form.label :url, "Trigger", class: 'control-label'
|
||||
|
||||
.col-sm-10
|
||||
- @service.supported_events.each do |event|
|
||||
%div
|
||||
= form.check_box service_event_field_name(event), class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= form.label service_event_field_name(event), class: 'list-label' do
|
||||
%strong
|
||||
= event.humanize
|
||||
.col-sm-10
|
||||
- @service.supported_events.each do |event|
|
||||
%div
|
||||
= form.check_box service_event_field_name(event), class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= form.label service_event_field_name(event), class: 'list-label' do
|
||||
%strong
|
||||
= event.humanize
|
||||
|
||||
- field = @service.event_field(event)
|
||||
- field = @service.event_field(event)
|
||||
|
||||
- if field
|
||||
%p
|
||||
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
|
||||
- if field
|
||||
%p
|
||||
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
|
||||
|
||||
%p.light
|
||||
= service_event_description(event)
|
||||
%p.light
|
||||
= service_event_description(event)
|
||||
|
||||
- @service.global_fields.each do |field|
|
||||
- type = field[:type]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Added Mattermost slash command
|
||||
merge_request: 7438
|
||||
author:
|
|
@ -85,8 +85,8 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
def project_service
|
||||
@project_service ||= user_project.find_or_initialize_service(params[:service_slug].underscore)
|
||||
def project_service(project = user_project)
|
||||
@project_service ||= project.find_or_initialize_service(params[:service_slug].underscore)
|
||||
@project_service || not_found!("Service")
|
||||
end
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
module API
|
||||
# Projects API
|
||||
class Services < Grape::API
|
||||
before { authenticate! }
|
||||
before { authorize_admin_project }
|
||||
|
||||
resource :projects do
|
||||
before { authenticate! }
|
||||
before { authorize_admin_project }
|
||||
|
||||
# Set <service_slug> service for project
|
||||
#
|
||||
# Example Request:
|
||||
|
@ -59,5 +59,28 @@ module API
|
|||
present project_service, with: Entities::ProjectService, include_passwords: current_user.is_admin?
|
||||
end
|
||||
end
|
||||
|
||||
resource :projects do
|
||||
desc 'Trigger a slash command' do
|
||||
detail 'Added in GitLab 8.13'
|
||||
end
|
||||
post ':id/services/:service_slug/trigger' do
|
||||
project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
|
||||
|
||||
# This is not accurate, but done to prevent leakage of the project names
|
||||
not_found!('Service') unless project
|
||||
|
||||
service = project_service(project)
|
||||
|
||||
result = service.try(:active?) && service.try(:trigger, params)
|
||||
|
||||
if result
|
||||
status result[:status] || 200
|
||||
present result
|
||||
else
|
||||
not_found!('Service')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
module Gitlab
|
||||
module ChatCommands
|
||||
class BaseCommand
|
||||
QUERY_LIMIT = 5
|
||||
|
||||
def self.match(_text)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.help_message
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.available?(_project)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def self.allowed?(_user, _ability)
|
||||
true
|
||||
end
|
||||
|
||||
def self.can?(object, action, subject)
|
||||
Ability.allowed?(object, action, subject)
|
||||
end
|
||||
|
||||
def execute(_)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def collection
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
attr_accessor :project, :current_user, :params
|
||||
|
||||
def initialize(project, user, params = {})
|
||||
@project, @current_user, @params = project, user, params.dup
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_by_iid(iid)
|
||||
resource = collection.find_by(iid: iid)
|
||||
|
||||
readable?(resource) ? resource : nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,61 @@
|
|||
module Gitlab
|
||||
module ChatCommands
|
||||
class Command < BaseCommand
|
||||
COMMANDS = [
|
||||
Gitlab::ChatCommands::IssueShow,
|
||||
Gitlab::ChatCommands::IssueCreate,
|
||||
].freeze
|
||||
|
||||
def execute
|
||||
command, match = match_command
|
||||
|
||||
if command
|
||||
if command.allowed?(project, current_user)
|
||||
present command.new(project, current_user, params).execute(match)
|
||||
else
|
||||
access_denied
|
||||
end
|
||||
else
|
||||
help(help_messages)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def match_command
|
||||
match = nil
|
||||
service = available_commands.find do |klass|
|
||||
match = klass.match(command)
|
||||
end
|
||||
|
||||
[service, match]
|
||||
end
|
||||
|
||||
def help_messages
|
||||
available_commands.map(&:help_message)
|
||||
end
|
||||
|
||||
def available_commands
|
||||
COMMANDS.select do |klass|
|
||||
klass.available?(project)
|
||||
end
|
||||
end
|
||||
|
||||
def command
|
||||
params[:text]
|
||||
end
|
||||
|
||||
def help(messages)
|
||||
Mattermost::Presenter.help(messages, params[:command])
|
||||
end
|
||||
|
||||
def access_denied
|
||||
Mattermost::Presenter.access_denied
|
||||
end
|
||||
|
||||
def present(resource)
|
||||
Mattermost::Presenter.present(resource)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module Gitlab
|
||||
module ChatCommands
|
||||
class IssueCommand < BaseCommand
|
||||
def self.available?(project)
|
||||
project.issues_enabled? && project.default_issues_tracker?
|
||||
end
|
||||
|
||||
def collection
|
||||
project.issues
|
||||
end
|
||||
|
||||
def readable?(issue)
|
||||
self.class.can?(current_user, :read_issue, issue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
module Gitlab
|
||||
module ChatCommands
|
||||
class IssueCreate < IssueCommand
|
||||
def self.match(text)
|
||||
/\Aissue\s+create\s+(?<title>[^\n]*)\n*(?<description>.*)\z/.match(text)
|
||||
end
|
||||
|
||||
def self.help_message
|
||||
'issue create <title>\n<description>'
|
||||
end
|
||||
|
||||
def self.allowed?(project, user)
|
||||
can?(user, :create_issue, project)
|
||||
end
|
||||
|
||||
def execute(match)
|
||||
title = match[:title]
|
||||
description = match[:description]
|
||||
|
||||
Issues::CreateService.new(project, current_user, title: title, description: description).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module Gitlab
|
||||
module ChatCommands
|
||||
class IssueShow < IssueCommand
|
||||
def self.match(text)
|
||||
/\Aissue\s+show\s+(?<iid>\d+)/.match(text)
|
||||
end
|
||||
|
||||
def self.help_message
|
||||
"issue show <id>"
|
||||
end
|
||||
|
||||
def execute(match)
|
||||
find_by_iid(match[:iid])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,117 @@
|
|||
module Mattermost
|
||||
class Presenter
|
||||
class << self
|
||||
include Gitlab::Routing.url_helpers
|
||||
|
||||
def authorize_chat_name(url)
|
||||
message = if url
|
||||
":wave: Hi there! Before I do anything for you, please [connect your GitLab account](#{url})."
|
||||
else
|
||||
":sweat_smile: Couldn't identify you, nor can I autorize you!"
|
||||
end
|
||||
|
||||
ephemeral_response(message)
|
||||
end
|
||||
|
||||
def help(commands, trigger)
|
||||
if commands.none?
|
||||
ephemeral_response("No commands configured")
|
||||
else
|
||||
commands.map! { |command| "#{trigger} #{command}" }
|
||||
message = header_with_list("Available commands", commands)
|
||||
|
||||
ephemeral_response(message)
|
||||
end
|
||||
end
|
||||
|
||||
def present(resource)
|
||||
return not_found unless resource
|
||||
|
||||
if resource.respond_to?(:count)
|
||||
if resource.count > 1
|
||||
return multiple_resources(resource)
|
||||
elsif resource.count == 0
|
||||
return not_found
|
||||
else
|
||||
resource = resource.first
|
||||
end
|
||||
end
|
||||
|
||||
single_resource(resource)
|
||||
end
|
||||
|
||||
def access_denied
|
||||
ephemeral_response("Whoops! That action is not allowed. This incident will be [reported](https://xkcd.com/838/).")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def not_found
|
||||
ephemeral_response("404 not found! GitLab couldn't find what you were looking for! :boom:")
|
||||
end
|
||||
|
||||
def single_resource(resource)
|
||||
return error(resource) if resource.errors.any? || !resource.persisted?
|
||||
|
||||
message = "### #{title(resource)}"
|
||||
message << "\n\n#{resource.description}" if resource.description
|
||||
|
||||
in_channel_response(message)
|
||||
end
|
||||
|
||||
def multiple_resources(resources)
|
||||
resources.map! { |resource| title(resource) }
|
||||
|
||||
message = header_with_list("Multiple results were found:", resources)
|
||||
|
||||
ephemeral_response(message)
|
||||
end
|
||||
|
||||
def error(resource)
|
||||
message = header_with_list("The action was not successful, because:", resource.errors.messages)
|
||||
|
||||
ephemeral_response(message)
|
||||
end
|
||||
|
||||
def title(resource)
|
||||
"[#{resource.to_reference} #{resource.title}](#{url(resource)})"
|
||||
end
|
||||
|
||||
def header_with_list(header, items)
|
||||
message = [header]
|
||||
|
||||
items.each do |item|
|
||||
message << "- #{item}"
|
||||
end
|
||||
|
||||
message.join("\n")
|
||||
end
|
||||
|
||||
def url(resource)
|
||||
url_for(
|
||||
[
|
||||
resource.project.namespace.becomes(Namespace),
|
||||
resource.project,
|
||||
resource
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def ephemeral_response(message)
|
||||
{
|
||||
response_type: :ephemeral,
|
||||
text: message,
|
||||
status: 200
|
||||
}
|
||||
end
|
||||
|
||||
def in_channel_response(message)
|
||||
{
|
||||
response_type: :in_channel,
|
||||
text: message,
|
||||
status: 200
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,55 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ChatCommands::Command, service: true do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
subject { described_class.new(project, user, params).execute }
|
||||
|
||||
describe '#execute' do
|
||||
context 'when no command is available' do
|
||||
let(:params) { { text: 'issue show 1' } }
|
||||
let(:project) { create(:project, has_external_issue_tracker: true) }
|
||||
|
||||
it 'displays 404 messages' do
|
||||
expect(subject[:response_type]).to be(:ephemeral)
|
||||
expect(subject[:text]).to start_with('404 not found')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an unknown command is triggered' do
|
||||
let(:params) { { command: '/gitlab', text: "unknown command 123" } }
|
||||
|
||||
it 'displays the help message' do
|
||||
expect(subject[:response_type]).to be(:ephemeral)
|
||||
expect(subject[:text]).to start_with('Available commands')
|
||||
expect(subject[:text]).to match('/gitlab issue show')
|
||||
end
|
||||
end
|
||||
|
||||
context 'the user can not create an issue' do
|
||||
let(:params) { { text: "issue create my new issue" } }
|
||||
|
||||
it 'rejects the actions' do
|
||||
expect(subject[:response_type]).to be(:ephemeral)
|
||||
expect(subject[:text]).to start_with('Whoops! That action is not allowed')
|
||||
end
|
||||
end
|
||||
|
||||
context 'issue is successfully created' do
|
||||
let(:params) { { text: "issue create my new issue" } }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
end
|
||||
|
||||
it 'presents the issue' do
|
||||
expect(subject[:text]).to match("my new issue")
|
||||
end
|
||||
|
||||
it 'shows a link to the new issue' do
|
||||
expect(subject[:text]).to match(/\/issues\/\d+/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ChatCommands::IssueCreate, service: true do
|
||||
describe '#execute' do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:regex_match) { described_class.match("issue create bird is the word") }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
end
|
||||
|
||||
subject do
|
||||
described_class.new(project, user).execute(regex_match)
|
||||
end
|
||||
|
||||
context 'without description' do
|
||||
it 'creates the issue' do
|
||||
expect { subject }.to change { project.issues.count }.by(1)
|
||||
|
||||
expect(subject.title).to eq('bird is the word')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with description' do
|
||||
let(:description) { "Surfin bird" }
|
||||
let(:regex_match) { described_class.match("issue create bird is the word\n#{description}") }
|
||||
|
||||
it 'creates the issue with description' do
|
||||
subject
|
||||
|
||||
expect(Issue.last.description).to eq(description)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.match' do
|
||||
it 'matches the title without description' do
|
||||
match = described_class.match("issue create my title")
|
||||
|
||||
expect(match[:title]).to eq('my title')
|
||||
expect(match[:description]).to eq("")
|
||||
end
|
||||
|
||||
it 'matches the title with description' do
|
||||
match = described_class.match("issue create my title\n\ndescription")
|
||||
|
||||
expect(match[:title]).to eq('my title')
|
||||
expect(match[:description]).to eq('description')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ChatCommands::IssueShow, service: true do
|
||||
describe '#execute' do
|
||||
let(:issue) { create(:issue) }
|
||||
let(:project) { issue.project }
|
||||
let(:user) { issue.author }
|
||||
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
end
|
||||
|
||||
subject do
|
||||
described_class.new(project, user).execute(regex_match)
|
||||
end
|
||||
|
||||
context 'the issue exists' do
|
||||
it 'returns the issue' do
|
||||
expect(subject.iid).to be issue.iid
|
||||
end
|
||||
end
|
||||
|
||||
context 'the issue does not exist' do
|
||||
let(:regex_match) { described_class.match("issue show 2343242") }
|
||||
|
||||
it "returns nil" do
|
||||
expect(subject).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'self.match' do
|
||||
it 'matches the iid' do
|
||||
match = described_class.match("issue show 123")
|
||||
|
||||
expect(match[:iid]).to eq("123")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -116,6 +116,7 @@ project:
|
|||
- base_tags
|
||||
- tag_taggings
|
||||
- tags
|
||||
- chat_services
|
||||
- creator
|
||||
- group
|
||||
- namespace
|
||||
|
@ -127,6 +128,7 @@ project:
|
|||
- emails_on_push_service
|
||||
- builds_email_service
|
||||
- pipelines_email_service
|
||||
- mattermost_slash_commands_service
|
||||
- irker_service
|
||||
- pivotaltracker_service
|
||||
- hipchat_service
|
||||
|
@ -188,4 +190,4 @@ award_emoji:
|
|||
- awardable
|
||||
- user
|
||||
priorities:
|
||||
- label
|
||||
- label
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
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
|
|
@ -0,0 +1,99 @@
|
|||
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
|
||||
end
|
|
@ -20,6 +20,7 @@ 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_service).dependent(:destroy) }
|
||||
it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
|
||||
|
@ -35,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(: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) }
|
||||
it { is_expected.to have_one(:bamboo_service).dependent(:destroy) }
|
||||
|
|
|
@ -88,4 +88,61 @@ describe API::API, api: true do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /projects/:id/services/:slug/trigger' do
|
||||
let!(:project) { create(:empty_project) }
|
||||
let(:service_name) { 'mattermost_slash_commands' }
|
||||
|
||||
context 'no service is available' do
|
||||
it 'returns a not found message' do
|
||||
post api("/projects/#{project.id}/services/idonotexist/trigger")
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
expect(json_response["message"]).to eq("404 Service Not Found")
|
||||
end
|
||||
end
|
||||
|
||||
context 'the service exists' do
|
||||
let(:params) { { token: 'token' } }
|
||||
|
||||
context 'the service is not active' do
|
||||
let!(:inactive_service) do
|
||||
project.create_mattermost_slash_commands_service(
|
||||
active: false,
|
||||
properties: { token: 'token' }
|
||||
)
|
||||
end
|
||||
|
||||
it 'when the service is inactive' do
|
||||
post api("/projects/#{project.id}/services/mattermost_slash_commands/trigger")
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'the service is active' do
|
||||
let!(:active_service) do
|
||||
project.create_mattermost_slash_commands_service(
|
||||
active: true,
|
||||
properties: { token: 'token' }
|
||||
)
|
||||
end
|
||||
|
||||
it 'retusn status 200' do
|
||||
post api("/projects/#{project.id}/services/mattermost_slash_commands/trigger"), params
|
||||
|
||||
expect(response).to have_http_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project can not be found' do
|
||||
it 'returns a generic 404' do
|
||||
post api("/projects/404/services/mattermost_slash_commands/trigger"), params
|
||||
|
||||
expect(response).to have_http_status(404)
|
||||
expect(json_response["message"]).to eq("404 Service Not Found")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ describe ChatNames::FindUserService, services: true do
|
|||
context 'when existing user is requested' do
|
||||
let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } }
|
||||
|
||||
it 'returns existing user' do
|
||||
it 'returns the existing user' do
|
||||
is_expected.to eq(user)
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue