Skip to content

Commit

Permalink
Fixes #435 - GitLab integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfschmidt authored and thorsteneckel committed Mar 10, 2021
1 parent 20d3c50 commit 97c9d54
Show file tree
Hide file tree
Showing 42 changed files with 1,116 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .gitlab/ci/browser-core.yml
Expand Up @@ -70,7 +70,7 @@ include:
RAILS_ENV: "test"
script:
- bundle exec rake zammad:ci:test:prepare
- bundle exec rspec --fail-fast -t type:system
- bundle exec rspec --fail-fast -t type:system -t ~integration

# we need at least one job to store and include this template
# $IGNORE is not defined
Expand Down
17 changes: 17 additions & 0 deletions .gitlab/ci/browser-integration.yml
@@ -1,12 +1,29 @@
include:

# browser-integration
- local: '/.gitlab/ci/browser-integration/capybara_chrome.yml'
- local: '/.gitlab/ci/browser-integration/capybara_ff.yml'
- local: '/.gitlab/ci/browser-integration/facebook_chrome.yml'
- local: '/.gitlab/ci/browser-integration/facebook_ff.yml'
- local: '/.gitlab/ci/browser-integration/idoit_chrome.yml'
- local: '/.gitlab/ci/browser-integration/otrs_chrome.yml'
- local: '/.gitlab/ci/browser-integration/zendesk_chrome.yml'

.template_browser-integration_capybara: &template_browser-integration_capybara
stage: browser-integration
dependencies:
- browser:build
extends:
- .env_base
- .variables_app_restart_cmd
- .variables_es
- .services_mysql_postgresql_elasticsearch_selenium_imap
variables:
RAILS_ENV: "test"
script:
- bundle exec rake zammad:ci:test:prepare
- bundle exec rspec --fail-fast --pattern "spec/system/**/*_spec.rb" -t integration

.template_browser-integration: &template_browser-integration
stage: browser-integration
dependencies:
Expand Down
5 changes: 5 additions & 0 deletions .gitlab/ci/browser-integration/capybara_chrome.yml
@@ -0,0 +1,5 @@
integration_capybara_chrome:
extends:
- .template_browser-integration_capybara
variables:
BROWSER: "chrome"
5 changes: 5 additions & 0 deletions .gitlab/ci/browser-integration/capybara_ff.yml
@@ -0,0 +1,5 @@
integration_capybara_ff:
extends:
- .template_browser-integration_capybara
variables:
BROWSER: "firefox"
2 changes: 2 additions & 0 deletions .rubocop/todo.rspec.yml
Expand Up @@ -274,6 +274,7 @@ RSpec/ExampleLength:
- 'spec/requests/integration/check_mk_spec.rb'
- 'spec/requests/integration/cti_spec.rb'
- 'spec/requests/integration/idoit_spec.rb'
- 'spec/requests/integration/gitlab_spec.rb'
- 'spec/requests/integration/monitoring_spec.rb'
- 'spec/requests/integration/object_manager_attributes_spec.rb'
- 'spec/requests/integration/placetel_spec.rb'
Expand Down Expand Up @@ -554,6 +555,7 @@ RSpec/MultipleExpectations:
- 'spec/requests/integration/check_mk_spec.rb'
- 'spec/requests/integration/cti_spec.rb'
- 'spec/requests/integration/idoit_spec.rb'
- 'spec/requests/integration/gitlab_spec.rb'
- 'spec/requests/integration/monitoring_spec.rb'
- 'spec/requests/integration/object_manager_attributes_spec.rb'
- 'spec/requests/integration/placetel_spec.rb'
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -124,6 +124,7 @@ gem 'acts_as_list'

# integrations
gem 'clearbit'
gem 'graphql-client'
gem 'net-ldap'
gem 'slack-notifier'
gem 'zendesk_api'
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Expand Up @@ -218,6 +218,10 @@ GEM
activesupport (>= 4.2.0)
gmail_xoauth (0.4.2)
oauth (>= 0.3.6)
graphql (1.11.6)
graphql-client (0.16.0)
activesupport (>= 3.0)
graphql (~> 1.8)
guard (2.15.0)
formatador (>= 0.2.4)
listen (>= 2.7, < 4.0)
Expand Down Expand Up @@ -613,6 +617,7 @@ DEPENDENCIES
faker
github_changelog_generator
gmail_xoauth
graphql-client
guard
guard-livereload
guard-symlink
Expand Down
4 changes: 2 additions & 2 deletions LICENSE-ICONS-3RD-PARTY.json
Expand Up @@ -225,7 +225,7 @@
"license": ""
},
"gitlab-button.svg": {
"author": "Gitlab",
"author": "GitLab",
"url": "",
"license": ""
},
Expand Down Expand Up @@ -694,4 +694,4 @@
"url": "https://thenounproject.com/term/key/1247931/",
"license": "CC 3.0 Attribution"
}
}
}
82 changes: 82 additions & 0 deletions app/assets/javascripts/app/controllers/_integration/gitlab.coffee
@@ -0,0 +1,82 @@
class GitLab extends App.ControllerIntegrationBase
featureIntegration: 'gitlab_integration'
featureName: 'GitLab'
featureConfig: 'gitlab_config'
description: [
['This service allows you to connect %s with %s.', 'GitLab', 'Zammad']
]
events:
'change .js-switch input': 'switch'

render: =>
super
new Form(
el: @$('.js-form')
)

class Form extends App.Controller
events:
'submit form': 'update'

constructor: ->
super
@render()

render: =>
config = App.Setting.get('gitlab_config')

@html App.view('integration/gitlab')(
config: config
)

update: (e) =>
e.preventDefault()
config = @formParam(e.target)
@validateAndSave(config)

validateAndSave: (config) =>
App.Ajax.request(
id: 'gitlab'
type: 'POST'
url: "#{@apiPath}/integration/gitlab/verify"
data: JSON.stringify(
api_token: config.api_token
endpoint: config.endpoint
)
success: (data, status, xhr) =>
if data.result is 'failed'
new App.ControllerErrorModal(
message: data.message
container: @el.closest('.content')
)
return

config.schema = data.response
App.Setting.set('gitlab_config', config)

error: (data, status) ->

return if status is 'abort'

details = data.responseJSON || {}
App.Event.trigger 'notify', {
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to save!')
}
)

class State
@current: ->
App.Setting.get('gitlab_integration')

App.Config.set(
'IntegrationGitLab'
{
name: 'GitLab'
target: '#system/integration/gitlab'
description: 'Link GitLab issues to your tickets.'
controller: GitLab
state: State
}
'NavBarIntegrations'
)
23 changes: 23 additions & 0 deletions app/assets/javascripts/app/controllers/git_issue_link_modal.coffee
@@ -0,0 +1,23 @@
class App.GitIssueLinkModal extends App.ControllerModal
buttonClose: true
buttonCancel: true
buttonSubmit: true

constructor: (params) ->
@placeholder = params.placeholder
@head = params.head
super

content: ->
$(App.view('integration/git_issue_link_modal')(
placeholder: @placeholder
))

onSubmit: (e) =>
form = @el.find('.js-result')
params = @formParam(form)
return if _.isEmpty(params.link)

@formDisable(form)
@callback(params.link, @)

@@ -0,0 +1,167 @@
class App.SidebarGitIssue extends App.Controller
provider: '_need_to_be_defined_' # GitLab
urlPlaceholder: '_need_to_be_defined_' # https://git.example.com/group1/project1/-/issues/1

constructor: ->
super
@issueLinks = []
@providerIdentifier = @provider.toLowerCase()

sidebarItem: =>
return if !@Config.get("#{@providerIdentifier}_integration")
@item = {
name: @providerIdentifier
badgeCallback: @badgeRender
sidebarHead: @provider
sidebarCallback: @showObjects
sidebarActions: [
{
title: 'Link issue'
name: 'link-issue'
callback: @linkIssue
},
]
}
@item

shown: ->
return if !@ticket
return if !@ticket.id
@showIssues()

metaBadge: =>
counter = ''
counter = @issueLinks.length

{
name: 'customer'
icon: "#{@providerIdentifier}-logo"
counterPossible: true
counter: counter
}

badgeRender: (el) =>
@badgeEl = el
@badgeRenderLocal()

badgeRenderLocal: =>
@badgeEl.html(App.view('generic/sidebar_tabs_item')(@metaBadge()))

linkIssue: =>
new App.GitIssueLinkModal(
head: @provider
placeholder: @urlPlaceholder
taskKey: @taskKey
container: @el.closest('.content')
callback: (link, ui) =>
if @ticket && @ticket.id
@saveTicketIssues = true
ui.close()
@showIssues([link])
)

showObjects: (el) =>
@el = el

# show placeholder
if @ticket && @ticket.preferences && @ticket.preferences[@providerIdentifier] && @ticket.preferences[@providerIdentifier].issue_links
@issueLinks = @ticket.preferences[@providerIdentifier].issue_links
queryParams = @queryParam()

# TODO: what is 'gitlab_issue_links'
if queryParams && queryParams.gitlab_issue_links
@issueLinks.push queryParams.gitlab_issue_links
@showIssues()

showIssues: (issueLinks) =>
if issueLinks
@issueLinks = _.uniq(@issueLinks.concat(issueLinks))

# show placeholder
if _.isEmpty(@issueLinks)
@html("<div>#{App.i18n.translateInline('No linked issues')}</div>")
return

# AJAX call to show items
@ajax(
id: "#{@providerIdentifier}-#{@taskKey}"
type: 'POST'
url: "#{@apiPath}/integration/#{@providerIdentifier}"
data: JSON.stringify(links: @issueLinks)
success: (data, status, xhr) =>
if data.response
@showList(data.response)
if @saveTicketIssues
@saveTicketIssues = false
@issueLinks = data.response.map((issue) -> issue.url)
@updateTicket(@ticket.id, @issueLinks)
return
@showError('Unable to load data...')

error: (xhr, status, error) =>

# do not close window if request is aborted
return if status is 'abort'

# show error message
@showError('Unable to load data...')
)

showList: (issues) =>
list = $(App.view('ticket_zoom/sidebar_git_issue')(
issues: issues
))
list.delegate('.js-delete', 'click', (e) =>
e.preventDefault()
issueLink = $(e.currentTarget).attr 'data-issue-id'
@delete(issueLink)
)
@html(list)
@badgeRenderLocal()

showError: (message) =>
@html App.i18n.translateInline(message)

reload: =>
@showIssues()

delete: (issueLink) =>
localLinks = []
for localLink in @issueLinks
if issueLink.toString() isnt localLink.toString()
localLinks.push localLink
@issueLinks = localLinks
if @ticket && @ticket.id
@updateTicket(@ticket.id, @issueLinks)
@showIssues()

postParams: (args) =>
return if !args.ticket
return if args.ticket.created_at
return if !@issueLinks
return if _.isEmpty(@issueLinks)
args.ticket.preferences ||= {}
args.ticket.preferences[@providerIdentifier] ||= {}
args.ticket.preferences[@providerIdentifier].issue_links = @issueLinks

updateTicket: (ticket_id, issueLinks) =>
App.Ajax.request(
id: "#{@providerIdentifier}-update-#{ticket_id}"
type: 'POST'
url: "#{@apiPath}/integration/#{@providerIdentifier}_ticket_update"
data: JSON.stringify(ticket_id: ticket_id, issue_links: issueLinks)
success: (data, status, xhr) =>
@badgeRenderLocal()
error: (xhr, status, details) =>

# do not close window if request is aborted
return if status is 'abort'

# show error message
@log 'errors', details
@notify(
type: 'error'
msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update object!')
timeout: 6000
)
)
@@ -0,0 +1,6 @@
class SidebarGitLab extends App.SidebarGitIssue
provider: 'GitLab'
urlPlaceholder: 'https://git.example.com/group1/project1/-/issues/1'

App.Config.set('500-GitLab', SidebarGitLab, 'TicketCreateSidebar')
App.Config.set('500-GitLab', SidebarGitLab, 'TicketZoomSidebar')

0 comments on commit 97c9d54

Please sign in to comment.