Commit 87d16063 authored by Z.J. van de Weg's avatar Z.J. van de Weg

Base work for auto config MM slash commands

parent dd385c7c
...@@ -3,7 +3,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -3,7 +3,7 @@ class Projects::ServicesController < Projects::ApplicationController
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :service, only: [:edit, :update, :test] before_action :service, only: [:edit, :update, :test, :configure]
respond_to :html respond_to :html
...@@ -44,9 +44,35 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -44,9 +44,35 @@ class Projects::ServicesController < Projects::ApplicationController
redirect_back_or_default(options: message) redirect_back_or_default(options: message)
end end
def configure
host = Gitlab.config.mattermost.host
if @service.auto_config? && host
@service.configure(host, current_user, params)
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, @service.to_param),
notice: 'This service is now configured.'
)
else
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, @service.to_param),
alert: 'This service can not be automatticly configured.'
)
end
rescue Mattermost::NoSessionError
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, @service.to_param),
alert: 'An error occurred, is Mattermost configured with Single Sign on?'
)
end
private private
def service def service
@service ||= @project.find_or_initialize_service(params[:id]) @service ||= @project.find_or_initialize_service(params[:id])
end end
def configure_params
params.require(:auto_configure).permit(:trigger, :team_id)
end
end end
module MattermostHelper
def mattermost_teams_for(current_user)
return unless Gitlab.config.mattermost.enabled
# Hack to make frontend work better
return [{"id"=>"qz8gdr1fopncueb8n9on8ohk3h", "create_at"=>1479992105904, "update_at"=>1479992105904, "delete_at"=>0, "display_name"=>"chatops", "name"=>"chatops", "email"=>"admin@example.com", "type"=>"O", "company_name"=>"", "allowed_domains"=>"", "invite_id"=>"gthxi47gj7rxtcx6zama63zd1w", "allow_open_invite"=>false}]
host = Gitlab.config.mattermost.host
Mattermost::Mattermost.new(host, current_user).with_session do
Mattermost::Team.all
end
end
end
...@@ -25,6 +25,21 @@ class MattermostSlashCommandsService < ChatService ...@@ -25,6 +25,21 @@ class MattermostSlashCommandsService < ChatService
] ]
end end
def auto_config?
Gitlab.config.mattermost.enabled
end
def configure(host, current_user, params)
token = Mattermost::Mattermost.new(host, current_user).with_session do
Mattermost::Commands.create(params[:team_id],
trigger: params[:trigger] || @service.project.path,
url: service_trigger_url(@service),
icon_url: asset_url('gitlab_logo.png'))
end
update_attributes(token: token)
end
def trigger(params) def trigger(params)
return nil unless valid_token?(params[:token]) return nil unless valid_token?(params[:token])
......
...@@ -54,6 +54,10 @@ class Service < ActiveRecord::Base ...@@ -54,6 +54,10 @@ class Service < ActiveRecord::Base
template template
end end
def auto_config?
false
end
def category def category
read_attribute(:category).to_sym read_attribute(:category).to_sym
end end
......
...@@ -6,16 +6,12 @@ ...@@ -6,16 +6,12 @@
%p= @service.description %p= @service.description
.col-lg-9 .col-lg-9
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| - # This returns an array of hashes, could you make a fancy dropdown :D
= render 'shared/service_settings', form: form, subject: @service - # Also, this is mocked for now, checkout the MattermostHelper to edit the data
= mattermost_teams_for(current_user)
= form_for(:auto_configure, method: :post, url: configure_namespace_project_service_path(@project.namespace, @project, @service.to_param)) do |f|
= "Team ID"
= f.text_field(:team_id)
= "Team ID"
= f.submit 'Save changes', class: 'btn btn-save'
.footer-block.row-content-block
= form.submit 'Save changes', class: 'btn btn-save'
&nbsp;
- if @service.valid? && @service.activated?
- unless @service.can_test?
- disabled_class = 'disabled'
- disabled_title = @service.disabled_title
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled_class}", title: disabled_title
= link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
- teams = Mattermost::Mattermost.new(Gitlab.config.mattermost.host, current_user).with_session do
Mattermost::Mattermost::Team.all
end
- pretty_path_with_namespace = "#{@project ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}" - pretty_path_with_namespace = "#{@project ? @project.namespace.name : 'namespace'} / #{@project ? @project.name : 'name'}"
- run_actions_text = "Perform common operations on this project: #{pretty_path_with_namespace}" - run_actions_text = "Perform common operations on this project: #{pretty_path_with_namespace}"
.well - unless GitLab.config.mattermost.enabled
This service allows GitLab users to perform common operations on this .well
project by entering slash commands in Mattermost. This service allows GitLab users to perform common operations on this
%br project by entering slash commands in Mattermost.
See list of available commands in Mattermost after setting up this service, %br
by entering See list of available commands in Mattermost after setting up this service,
%code /&lt;command_trigger_word&gt; help by entering
%br %code /&lt;command_trigger_word&gt; help
%br %br
To setup this service: %br
%ul.list-unstyled To setup this service:
%li %ul.list-unstyled
1. %li
= link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands' 1.
on your Mattermost installation = link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands'
%li on your Mattermost installation
2. %li
= link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command' 2.
in Mattermost with these options: = link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command'
in Mattermost with these options:
%hr
%hr
.help-form
.form-group .help-form
= label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :display_name, "GitLab / #{pretty_path_with_namespace}", class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :display_name, "GitLab / #{pretty_path_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#display_name') .input-group-btn
= clipboard_button(clipboard_target: '#display_name')
.form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#description') .input-group-btn
= clipboard_button(clipboard_target: '#description')
.form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.text-block = label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
%p Fill in the word that works best for your team. .col-sm-10.col-xs-12.text-block
%p %p Fill in the word that works best for your team.
Suggestions: %p
%code= 'gitlab' Suggestions:
%code= @project.path # Path contains no spaces, but dashes %code= 'gitlab'
%code= @project.path_with_namespace %code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#request_url') .input-group-btn
= clipboard_button(clipboard_target: '#request_url')
.form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.text-block POST = label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#response_username') .input-group-btn
= clipboard_button(clipboard_target: '#response_username')
.form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#response_icon') .input-group-btn
= clipboard_button(clipboard_target: '#response_icon')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.text-block Yes = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Yes
.form-group
= label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#autocomplete_hint') .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label' .form-group
.col-sm-10.col-xs-12.input-group = label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly' .col-sm-10.col-xs-12.input-group
.input-group-btn = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
= clipboard_button(clipboard_target: '#autocomplete_description') .input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
%hr
%hr
%ul.list-unstyled
%li %ul.list-unstyled
3. After adding the slash command, paste the %li
%strong token 3. After adding the slash command, paste the
into the field below %strong token
into the field below
...@@ -13,38 +13,41 @@ ...@@ -13,38 +13,41 @@
.col-sm-10 .col-sm-10
= form.check_box :active = form.check_box :active
- if @service.supported_events.present? - if @service.auto_config?
.form-group
= form.label :url, "Trigger", class: 'control-label' - else
- if @service.supported_events.present?
.col-sm-10 .form-group
- @service.supported_events.each do |event| = form.label :url, "Trigger", class: 'control-label'
%div
= form.check_box service_event_field_name(event), class: 'pull-left' .col-sm-10
.prepend-left-20 - @service.supported_events.each do |event|
= form.label service_event_field_name(event), class: 'list-label' do %div
%strong = form.check_box service_event_field_name(event), class: 'pull-left'
= event.humanize .prepend-left-20
= form.label service_event_field_name(event), class: 'list-label' do
- field = @service.event_field(event) %strong
= event.humanize
- if field
%p - field = @service.event_field(event)
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
- if field
%p.light %p
= service_event_description(event) = form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
- @service.global_fields.each do |field| %p.light
- type = field[:type] = service_event_description(event)
- if type == 'fieldset' - @service.global_fields.each do |field|
- fields = field[:fields] - type = field[:type]
- legend = field[:legend]
- if type == 'fieldset'
%fieldset - fields = field[:fields]
%legend= legend - legend = field[:legend]
- fields.each do |subfield|
= render 'shared/field', form: form, field: subfield %fieldset
- else %legend= legend
= render 'shared/field', form: form, field: field - fields.each do |subfield|
= render 'shared/field', form: form, field: subfield
- else
= render 'shared/field', form: form, field: field
...@@ -153,6 +153,11 @@ production: &base ...@@ -153,6 +153,11 @@ production: &base
# The location where LFS objects are stored (default: shared/lfs-objects). # The location where LFS objects are stored (default: shared/lfs-objects).
# storage_path: shared/lfs-objects # storage_path: shared/lfs-objects
# For executing commands from GitLab on Mattermost
mattermost:
enabled: false
host: 'http://locahost:8065'
## Gravatar ## Gravatar
## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
gravatar: gravatar:
......
...@@ -61,6 +61,7 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -61,6 +61,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
member do member do
get :test get :test
post :configure
end end
end end
......
module Mattermost
class Command
def self.all(team_id)
Mattermost::Mattermost.get("/teams/#{team_id}/commands/list_team_commands")
end
# params should be a hash, which supplies _at least_:
# - trigger => The slash command, no spaces, cannot start with a /
# - url => What is the URL to trigger here?
# - icon_url => Supply a link to the icon
def self.create(team_id, params)
params = {
auto_complete: true,
auto_complete_desc: 'List all available commands',
auto_complete_hint: '[help]',
description: 'Perform common operations on GitLab',
display_name: 'GitLab',
method: 'P',
user_name: 'GitLab'
}..merge(params)
Mattermost::Mattermost.post( "/teams/#{team_id}/commands/create", params.to_json).
parsed_response['token']
end
end
end
...@@ -20,6 +20,7 @@ module Mattermost ...@@ -20,6 +20,7 @@ module Mattermost
attr_accessor :current_resource_owner attr_accessor :current_resource_owner
def initialize(uri, current_user) def initialize(uri, current_user)
uri = normalize_uri(uri)
self.class.base_uri(uri) self.class.base_uri(uri)
@current_resource_owner = current_user @current_resource_owner = current_user
...@@ -31,6 +32,8 @@ module Mattermost ...@@ -31,6 +32,8 @@ module Mattermost
destroy destroy
result result
rescue Errno::ECONNREFUSED
raise NoSessionError
end end
# Next methods are needed for Doorkeeper # Next methods are needed for Doorkeeper
...@@ -67,11 +70,11 @@ module Mattermost ...@@ -67,11 +70,11 @@ module Mattermost
end end
def destroy def destroy
post('/api/v3/users/logout') post('/users/logout')
end end
def oauth_uri def oauth_uri
response = get("/api/v3/oauth/gitlab/login", follow_redirects: false) response = get("/oauth/gitlab/login", follow_redirects: false)
return unless 300 <= response.code && response.code < 400 return unless 300 <= response.code && response.code < 400
redirect_uri = response.headers['location'] redirect_uri = response.headers['location']
...@@ -100,5 +103,11 @@ module Mattermost ...@@ -100,5 +103,11 @@ module Mattermost
def post(path, options = {}) def post(path, options = {})
self.class.post(path, options) self.class.post(path, options)
end end
def normalize_uri(uri)
uri << '/' unless uri.end_with?('/')
uri << 'api/v3'
end
end end
end end
module Mattermost
class Team < Mattermost
# After normalization this returns an array of hashes
#
# [{"id"=>"paf573pj9t81urupw3fanozeda", "display_name"=>"my team", <snip>}]
def self.all
@all_teams ||= get('/teams/all').parsed_response.values
end
end
end
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