- Fixes #3455: Gitlab and Github integration cause application reload when using [Enter]

- Fixes #3456: Wrong API-Key for Gitlab does not throw error message
- Fixes #3458: Gitlab and Github integration should provide visual feedback during adding issue.
- Fixes #3459: Provide error message if user provided wrong information for Github and Gitlab
- Refactoring: Multiple loads of GitLab/GitHub GraphQL schema causes Memory leak.
This commit is contained in:
Rolf Schmidt 2021-03-24 16:41:05 +00:00 committed by Thorsten Eckel
parent 92eea19f8a
commit 1d2a1a1163
24 changed files with 287 additions and 265 deletions

View file

@ -123,7 +123,6 @@ gem 'acts_as_list'
# integrations # integrations
gem 'clearbit' gem 'clearbit'
gem 'graphql-client'
gem 'net-ldap' gem 'net-ldap'
gem 'slack-notifier' gem 'slack-notifier'
gem 'zendesk_api' gem 'zendesk_api'

View file

@ -218,10 +218,6 @@ GEM
activesupport (>= 4.2.0) activesupport (>= 4.2.0)
gmail_xoauth (0.4.2) gmail_xoauth (0.4.2)
oauth (>= 0.3.6) oauth (>= 0.3.6)
graphql (1.11.6)
graphql-client (0.16.0)
activesupport (>= 3.0)
graphql (~> 1.8)
guard (2.15.0) guard (2.15.0)
formatador (>= 0.2.4) formatador (>= 0.2.4)
listen (>= 2.7, < 4.0) listen (>= 2.7, < 4.0)
@ -617,7 +613,6 @@ DEPENDENCIES
faker faker
github_changelog_generator github_changelog_generator
gmail_xoauth gmail_xoauth
graphql-client
guard guard
guard-livereload guard-livereload
guard-symlink guard-symlink

View file

@ -51,8 +51,7 @@ class Form extends App.Controller
) )
return return
config.schema = data.response App.Setting.set('github_config', config, notify: true)
App.Setting.set('github_config', config)
error: (data, status) -> error: (data, status) ->

View file

@ -51,8 +51,7 @@ class Form extends App.Controller
) )
return return
config.schema = data.response App.Setting.set('gitlab_config', config, notify: true)
App.Setting.set('gitlab_config', config)
error: (data, status) -> error: (data, status) ->

View file

@ -4,7 +4,8 @@ class App.SidebarGitIssue extends App.Controller
constructor: -> constructor: ->
super super
@issueLinks = [] @issueLinks = []
@issueLinkData = []
@providerIdentifier = @provider.toLowerCase() @providerIdentifier = @provider.toLowerCase()
sidebarItem: => sidebarItem: =>
@ -13,7 +14,7 @@ class App.SidebarGitIssue extends App.Controller
name: @providerIdentifier name: @providerIdentifier
badgeCallback: @badgeRender badgeCallback: @badgeRender
sidebarHead: @provider sidebarHead: @provider
sidebarCallback: @showObjects sidebarCallback: @reloadIssues
sidebarActions: [ sidebarActions: [
{ {
title: 'Link issue' title: 'Link issue'
@ -26,8 +27,8 @@ class App.SidebarGitIssue extends App.Controller
shown: -> shown: ->
return if !@ticket return if !@ticket
return if !@ticket.id
@showIssues() @listIssues()
metaBadge: => metaBadge: =>
counter = '' counter = ''
@ -45,6 +46,7 @@ class App.SidebarGitIssue extends App.Controller
@badgeRenderLocal() @badgeRenderLocal()
badgeRenderLocal: => badgeRenderLocal: =>
return if !@badgeEl
@badgeEl.html(App.view('generic/sidebar_tabs_item')(@metaBadge())) @badgeEl.html(App.view('generic/sidebar_tabs_item')(@metaBadge()))
linkIssue: => linkIssue: =>
@ -54,86 +56,141 @@ class App.SidebarGitIssue extends App.Controller
taskKey: @taskKey taskKey: @taskKey
container: @el.closest('.content') container: @el.closest('.content')
callback: (link, ui) => callback: (link, ui) =>
if @ticket && @ticket.id @getIssues(
@saveTicketIssues = true links: [link]
ui.close() success: (result) =>
@showIssues([link]) if !_.contains(@issueLinks, link)
@issueLinks.push(result[0].url)
@issueLinkData = @issueLinkData.concat(result)
if @ticket && @ticket.id
@saveIssues(
ticket_id: @ticket.id
links: @issueLinks
success: =>
ui.close()
@renderIssues()
error: (message = 'Unable to save issue') =>
ui.showAlert(App.i18n.translatePlain(message))
form = ui.el.find('.js-result')
@formEnable(form)
)
else
ui.close()
@renderIssues()
error: (message = 'Unable to load issues') =>
ui.showAlert(App.i18n.translatePlain(message))
form = ui.el.find('.js-result')
@formEnable(form)
)
) )
showObjects: (el) => reloadIssues: (el) =>
@el = el if el
@el = el
# show placeholder return @renderIssues() if !@ticket
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' ticketLinks = @ticket?.preferences?[@providerIdentifier]?.issue_links || []
if queryParams && queryParams.gitlab_issue_links return @renderIssues() if _.isEqual(@issueLinks, ticketLinks)
@issueLinks.push queryParams.gitlab_issue_links
@showIssues()
showIssues: (issueLinks) => @issueLinks = ticketLinks
if issueLinks @listIssues(true)
@issueLinks = _.uniq(@issueLinks.concat(issueLinks))
# show placeholder renderIssues: =>
if _.isEmpty(@issueLinks) if _.isEmpty(@issueLinkData)
@html("<div>#{App.i18n.translateInline('No linked issues')}</div>") @showEmpty()
return 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')( list = $(App.view('ticket_zoom/sidebar_git_issue')(
issues: issues issues: @issueLinkData
)) ))
list.delegate('.js-delete', 'click', (e) => list.delegate('.js-delete', 'click', (e) =>
e.preventDefault() e.preventDefault()
issueLink = $(e.currentTarget).attr 'data-issue-id' issueLink = $(e.currentTarget).attr 'data-issue-id'
@delete(issueLink) @deleteIssue(issueLink)
) )
@html(list) @html(list)
@badgeRenderLocal() @badgeRenderLocal()
listIssues: (force = false) =>
return @renderIssues() if !force && @fetchFullActive && @fetchFullActive > new Date().getTime() - 5000
@fetchFullActive = new Date().getTime()
return @renderIssues() if _.isEmpty(@issueLinks)
@getIssues(
links: @issueLinks
success: (result) =>
@issueLinks = result.map((element) -> element.url)
@issueLinkData = result
@renderIssues()
error: =>
@showError(App.i18n.translateInline('Unable to load issues'))
)
getIssues: (params) ->
@ajax(
id: "#{@providerIdentifier}-#{@taskKey}"
type: 'POST'
url: "#{@apiPath}/integration/#{@providerIdentifier}"
data: JSON.stringify(links: params.links)
success: (data, status, xhr) ->
if data.response
# some issues redirect to pull requests like
# https://github.com/zammad/zammad/issues/1574
# in this case throw error
return params.error('Unable to load issues') if _.isEmpty(data.response)
params.success(data.response)
else
params.error(data.message)
error: (xhr, status, error) ->
return if status is 'abort'
params.error()
)
saveIssues: (params) ->
App.Ajax.request(
id: "#{@providerIdentifier}-update-#{params.ticket_id}"
type: 'POST'
url: "#{@apiPath}/integration/#{@providerIdentifier}_ticket_update"
data: JSON.stringify(ticket_id: params.ticket_id, issue_links: params.links)
success: (data, status, xhr) ->
params.success(data)
error: (xhr, status, details) ->
return if status is 'abort'
params.error()
)
deleteIssue: (link) ->
@issueLinks = _.filter(@issueLinks, (element) -> element isnt link)
@issueLinkData = _.filter(@issueLinkData, (element) -> element.url isnt link)
if @ticket && @ticket.id
@saveIssues(
ticket_id: @ticket.id
links: @issueLinks
success: =>
@renderIssues()
error: (message = 'Unable to save issue') =>
@showError(App.i18n.translateInline(message))
)
else
@renderIssues()
showEmpty: ->
@html("<div>#{App.i18n.translateInline('No linked issues')}</div>")
@badgeRenderLocal()
showError: (message) => showError: (message) =>
@html App.i18n.translateInline(message) @html App.i18n.translateInline(message)
reload: => reload: =>
@showIssues() @reloadIssues()
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) => postParams: (args) =>
return if !args.ticket return if !args.ticket
@ -143,25 +200,3 @@ class App.SidebarGitIssue extends App.Controller
args.ticket.preferences ||= {} args.ticket.preferences ||= {}
args.ticket.preferences[@providerIdentifier] ||= {} args.ticket.preferences[@providerIdentifier] ||= {}
args.ticket.preferences[@providerIdentifier].issue_links = @issueLinks 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
)
)

View file

@ -1,3 +1,3 @@
<form class="flex horizontal js-result"> <div class="flex horizontal js-result">
<input type="text" name="link" value="" autocomplete="off" placeholder="<%= @placeholder %>" class="form-control"/> <input type="text" name="link" value="" autocomplete="off" placeholder="<%= @placeholder %>" class="form-control"/>
</form> </div>

View file

@ -5,9 +5,11 @@ class Integration::GitHubController < ApplicationController
def verify def verify
github = ::GitHub.new(params[:endpoint], params[:api_token]) github = ::GitHub.new(params[:endpoint], params[:api_token])
github.verify!
render json: { render json: {
result: 'ok', result: 'ok',
response: github.schema.to_json,
} }
rescue => e rescue => e
logger.error e logger.error e
@ -21,7 +23,7 @@ class Integration::GitHubController < ApplicationController
def query def query
config = Setting.get('github_config') config = Setting.get('github_config')
github = ::GitHub.new(config['endpoint'], config['api_token'], schema: config['schema']) github = ::GitHub.new(config['endpoint'], config['api_token'])
render json: { render json: {
result: 'ok', result: 'ok',

View file

@ -5,9 +5,11 @@ class Integration::GitLabController < ApplicationController
def verify def verify
gitlab = ::GitLab.new(params[:endpoint], params[:api_token]) gitlab = ::GitLab.new(params[:endpoint], params[:api_token])
gitlab.verify!
render json: { render json: {
result: 'ok', result: 'ok',
response: gitlab.schema.to_json,
} }
rescue => e rescue => e
logger.error e logger.error e
@ -21,7 +23,7 @@ class Integration::GitLabController < ApplicationController
def query def query
config = Setting.get('gitlab_config') config = Setting.get('gitlab_config')
gitlab = ::GitLab.new(config['endpoint'], config['api_token'], schema: config['schema']) gitlab = ::GitLab.new(config['endpoint'], config['api_token'])
render json: { render json: {
result: 'ok', result: 'ok',

View file

@ -1,14 +1,14 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class GitHub class GitHub
extend Forwardable
attr_reader :client attr_reader :client
def_delegator :client, :schema def initialize(endpoint, api_token)
@client = GitHub::HttpClient.new(endpoint, api_token)
end
def initialize(*args, **kargs) def verify!
@client = GitHub::Client.new(*args, **kargs) GitHub::Credentials.new(client).verify!
end end
def issues_by_urls(urls) def issues_by_urls(urls)

View file

@ -1,38 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
require 'graphql/client'
class GitHub
class Client
delegate_missing_to :client
attr_reader :endpoint
def initialize(endpoint, api_token, schema: nil)
@endpoint = endpoint
@api_token = api_token
schema(schema) if schema.present?
end
def schema(source = http_client)
@schema ||= ::GraphQL::Client.load_schema(source)
end
private
def http_client
@http_client ||= GitHub::HttpClient.new(@endpoint, @api_token)
end
def client
@client ||= begin
GraphQL::Client.new(
schema: schema,
execute: http_client,
).tap do |client|
client.allow_dynamic_queries = true
end
end
end
end
end

28
lib/github/credentials.rb Normal file
View file

@ -0,0 +1,28 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class GitHub
class Credentials
QUERY = <<-'GRAPHQL'.freeze
query {
viewer {
login
}
}
GRAPHQL
attr_reader :client
def initialize(client)
@client = client
end
def verify!
response = client.perform(
query: GitHub::Credentials::QUERY,
)
return if response.dig('data', 'viewer', 'login').present?
raise 'Invalid GitHub GraphQL API token'
end
end
end

View file

@ -1,22 +1,45 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
require 'graphql/client'
require 'graphql/client/http'
class GitHub class GitHub
class HttpClient < ::GraphQL::Client::HTTP class HttpClient
attr_reader :api_token, :endpoint
def initialize(endpoint, api_token) def initialize(endpoint, api_token)
raise 'api_token required' if api_token.blank? raise 'api_token required' if api_token.blank?
raise 'endpoint required' if endpoint.blank? raise 'endpoint required' if endpoint.blank?
@api_token = api_token @api_token = api_token
@endpoint = endpoint
super(endpoint)
end end
def headers(_context) def perform(payload)
response = UserAgent.post(
endpoint,
payload,
{
headers: headers,
json: true,
open_timeout: 6,
read_timeout: 16,
log: {
facility: 'GitHub',
},
},
)
if !response.success?
Rails.logger.error response.error
raise "Error while requesting GitHub GraphQL API: #{response.error}"
end
response.data
end
private
def headers
{ {
Authorization: "bearer #{@api_token}" Authorization: "bearer #{api_token}"
} }
end end
end end

View file

@ -87,28 +87,28 @@ class GitHub
@result.dig('milestone', 'title') @result.dig('milestone', 'title')
end end
def query
@query ||= client.parse GitHub::LinkedIssue::QUERY
end
def query_by_url(url) def query_by_url(url)
variables = variables(url) response = client.perform(
return if variables.blank? query: GitHub::LinkedIssue::QUERY,
variables: variables!(url)
)
response = client.query(query, variables: variables) response.dig('data', 'repository', 'issue')
response&.data&.repository&.issue&.to_h&.deep_dup
end end
def variables(url) def variables!(url)
return if url !~ %r{^https://([^/]+)/([^/]+)/([^/]+)/issues/(\d+)$} if url !~ %r{^https://([^/]+)/([^/]+)/([^/]+)/issues/(\d+)$}
raise Exceptions::UnprocessableEntity, 'Invalid GitHub issue link format'
end
host = $1 host = $1
repositor_owner = $2 repositor_owner = $2
repository_name = $3 repository_name = $3
id = $4 id = $4
return if client.endpoint.exclude?(host) if client.endpoint.exclude?(host)
raise Exceptions::UnprocessableEntity, "Issue link doesn't match configured GitHub endpoint '#{client.endpoint}'"
end
{ {
repositor_owner: repositor_owner, repositor_owner: repositor_owner,

View file

@ -1,14 +1,14 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class GitLab class GitLab
extend Forwardable
attr_reader :client attr_reader :client
def_delegator :client, :schema def initialize(endpoint, api_token)
@client = GitLab::HttpClient.new(endpoint, api_token)
end
def initialize(*args, **kargs) def verify!
@client = GitLab::Client.new(*args, **kargs) GitLab::Credentials.new(client).verify!
end end
def issues_by_urls(urls) def issues_by_urls(urls)

View file

@ -1,38 +0,0 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
require 'graphql/client'
class GitLab
class Client
delegate_missing_to :client
attr_reader :endpoint
def initialize(endpoint, api_token, schema: nil)
@endpoint = endpoint
@api_token = api_token
schema(schema) if schema.present?
end
def schema(source = http_client)
@schema ||= ::GraphQL::Client.load_schema(source)
end
private
def http_client
@http_client ||= GitLab::HttpClient.new(@endpoint, @api_token)
end
def client
@client ||= begin
GraphQL::Client.new(
schema: schema,
execute: http_client,
).tap do |client|
client.allow_dynamic_queries = true
end
end
end
end
end

28
lib/gitlab/credentials.rb Normal file
View file

@ -0,0 +1,28 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
class GitLab
class Credentials
QUERY = <<-'GRAPHQL'.freeze
query {
currentUser {
username
}
}
GRAPHQL
attr_reader :client
def initialize(client)
@client = client
end
def verify!
response = client.perform(
query: GitLab::Credentials::QUERY,
)
return if response.dig('data', 'currentUser', 'username').present?
raise 'Invalid GitLab GraphQL API token'
end
end
end

View file

@ -1,20 +1,43 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
require 'graphql/client'
require 'graphql/client/http'
class GitLab class GitLab
class HttpClient < ::GraphQL::Client::HTTP class HttpClient
attr_reader :api_token, :endpoint
def initialize(endpoint, api_token) def initialize(endpoint, api_token)
raise 'api_token required' if api_token.blank? raise 'api_token required' if api_token.blank?
raise 'endpoint required' if endpoint.blank? raise 'endpoint required' if endpoint.blank?
@api_token = api_token @api_token = api_token
@endpoint = endpoint
super(endpoint)
end end
def headers(_context) def perform(payload)
response = UserAgent.post(
endpoint,
payload,
{
headers: headers,
json: true,
open_timeout: 6,
read_timeout: 16,
log: {
facility: 'GitLab',
},
},
)
if !response.success?
Rails.logger.error response.error
raise "Error while requesting GitLab GraphQL API: #{response.error}"
end
response.data
end
private
def headers
{ {
"PRIVATE-TOKEN": @api_token "PRIVATE-TOKEN": @api_token
} }

View file

@ -84,27 +84,30 @@ class GitLab
@result.dig('milestone', 'title') @result.dig('milestone', 'title')
end end
def query
@query ||= client.parse GitLab::LinkedIssue::QUERY
end
def query_by_url(url) def query_by_url(url)
variables = variables(url) variables = variables(url)
return if variables.blank? return if variables.blank?
response = client.query(query, variables: variables) response = client.perform(
query: GitLab::LinkedIssue::QUERY,
variables: variables
)
response&.data&.project&.issue&.to_h&.deep_dup response.dig('data', 'project', 'issue')
end end
def variables(url) def variables(url)
return if url !~ %r{^https://([^/]+)/(.*)/-/issues/(\d+)$} if url !~ %r{^https://([^/]+)/(.*)/-/issues/(\d+)$}
raise Exceptions::UnprocessableEntity, 'Invalid GitLab issue link format'
end
host = $1 host = $1
fullpath = $2 fullpath = $2
id = $3 id = $3
return if client.endpoint.exclude?(host) if client.endpoint.exclude?(host)
raise Exceptions::UnprocessableEntity, "Issue link doesn't match configured GitLab endpoint '#{client.endpoint}'"
end
{ {
fullpath: fullpath, fullpath: fullpath,

View file

@ -6,13 +6,9 @@ RSpec.describe GitHub, type: :integration do # rubocop:disable RSpec/FilePath
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = described_class.new(ENV['GITHUB_ENDPOINT'], ENV['GITHUB_APITOKEN']).schema
end end
let(:instance) { described_class.new(ENV['GITHUB_ENDPOINT'], ENV['GITHUB_APITOKEN'], schema: schema) } let(:instance) { described_class.new(ENV['GITHUB_ENDPOINT'], ENV['GITHUB_APITOKEN']) }
let(:schema) { @cached_schema } # rubocop:disable RSpec/InstanceVariable
let(:issue_data) do let(:issue_data) do
{ {
id: '1575', id: '1575',
@ -37,12 +33,6 @@ RSpec.describe GitHub, type: :integration do # rubocop:disable RSpec/FilePath
end end
let(:invalid_issue_url) { 'https://github.com/organization/repository/issues/42' } let(:invalid_issue_url) { 'https://github.com/organization/repository/issues/42' }
describe '#schema' do
it 'returns GraphQL schema' do
expect(instance.schema).to respond_to(:to_graphql)
end
end
describe '#issues_by_urls' do describe '#issues_by_urls' do
let(:result) { instance.issues_by_urls([ issue_url ]) } let(:result) { instance.issues_by_urls([ issue_url ]) }

View file

@ -6,13 +6,9 @@ RSpec.describe GitLab, type: :integration do # rubocop:disable RSpec/FilePath
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = described_class.new(ENV['GITLAB_ENDPOINT'], ENV['GITLAB_APITOKEN']).schema
end end
let(:instance) { described_class.new(ENV['GITLAB_ENDPOINT'], ENV['GITLAB_APITOKEN'], schema: schema) } let(:instance) { described_class.new(ENV['GITLAB_ENDPOINT'], ENV['GITLAB_APITOKEN']) }
let(:schema) { @cached_schema } # rubocop:disable RSpec/InstanceVariable
let(:issue_data) do let(:issue_data) do
{ {
id: '1', id: '1',
@ -40,13 +36,7 @@ RSpec.describe GitLab, type: :integration do # rubocop:disable RSpec/FilePath
], ],
} }
end end
let(:invalid_issue_url) { 'https://git.example.com/group/project/-/issues/1' } let(:invalid_issue_url) { "https://#{URI.parse(ENV['GITLAB_ISSUE_LINK']).host}/group/project/-/issues/1" }
describe '#schema' do
it 'returns GraphQL schema' do
expect(instance.schema).to respond_to(:to_graphql)
end
end
describe '#issues_by_urls' do describe '#issues_by_urls' do
let(:result) { instance.issues_by_urls([ issue_url ]) } let(:result) { instance.issues_by_urls([ issue_url ]) }

View file

@ -60,14 +60,13 @@ RSpec.describe 'GitHub', type: :request do
authenticated_as(admin) authenticated_as(admin)
instance = instance_double('GitHub') instance = instance_double('GitHub')
expect(GitHub).to receive(:new).with(endpoint, token).and_return instance expect(GitHub).to receive(:new).with(endpoint, token).and_return instance
expect(instance).to receive(:schema).and_return(dummy_schema) expect(instance).to receive(:verify!).and_return(true)
post '/api/v1/integration/github/verify', params: params, as: :json post '/api/v1/integration/github/verify', params: params, as: :json
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank expect(json_response).not_to be_blank
expect(json_response['result']).to eq('ok') expect(json_response['result']).to eq('ok')
expect(json_response['response']).to eq(dummy_schema.to_json)
end end
it 'does query objects' do it 'does query objects' do

View file

@ -65,14 +65,13 @@ RSpec.describe 'GitLab', type: :request do
authenticated_as(admin) authenticated_as(admin)
instance = instance_double('GitLab') instance = instance_double('GitLab')
expect(GitLab).to receive(:new).with(endpoint, token).and_return instance expect(GitLab).to receive(:new).with(endpoint, token).and_return instance
expect(instance).to receive(:schema).and_return(dummy_schema) expect(instance).to receive(:verify!).and_return(true)
post '/api/v1/integration/gitlab/verify', params: params, as: :json post '/api/v1/integration/gitlab/verify', params: params, as: :json
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json_response).to be_a_kind_of(Hash) expect(json_response).to be_a_kind_of(Hash)
expect(json_response).not_to be_blank expect(json_response).not_to be_blank
expect(json_response['result']).to eq('ok') expect(json_response['result']).to eq('ok')
expect(json_response['response']).to eq(dummy_schema.to_json)
end end
it 'does query objects' do it 'does query objects' do

View file

@ -352,9 +352,6 @@ RSpec.describe 'Ticket Create', type: :system do
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = GitLab.new(ENV['GITLAB_ENDPOINT'], ENV['GITLAB_APITOKEN']).schema.to_json
end end
def authenticate def authenticate
@ -362,7 +359,6 @@ RSpec.describe 'Ticket Create', type: :system do
Setting.set('gitlab_config', { Setting.set('gitlab_config', {
api_token: ENV['GITLAB_APITOKEN'], api_token: ENV['GITLAB_APITOKEN'],
endpoint: ENV['GITLAB_ENDPOINT'], endpoint: ENV['GITLAB_ENDPOINT'],
schema: @cached_schema, # rubocop:disable RSpec/InstanceVariable
}) })
true true
end end
@ -410,9 +406,6 @@ RSpec.describe 'Ticket Create', type: :system do
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = GitHub.new(ENV['GITHUB_ENDPOINT'], ENV['GITHUB_APITOKEN']).schema.to_json
end end
def authenticate def authenticate
@ -420,7 +413,6 @@ RSpec.describe 'Ticket Create', type: :system do
Setting.set('github_config', { Setting.set('github_config', {
api_token: ENV['GITHUB_APITOKEN'], api_token: ENV['GITHUB_APITOKEN'],
endpoint: ENV['GITHUB_ENDPOINT'], endpoint: ENV['GITHUB_ENDPOINT'],
schema: @cached_schema, # rubocop:disable RSpec/InstanceVariable
}) })
true true
end end

View file

@ -1513,9 +1513,6 @@ RSpec.describe 'Ticket zoom', type: :system do
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = GitLab.new(ENV['GITLAB_ENDPOINT'], ENV['GITLAB_APITOKEN']).schema.to_json
end end
def authenticate def authenticate
@ -1523,7 +1520,6 @@ RSpec.describe 'Ticket zoom', type: :system do
Setting.set('gitlab_config', { Setting.set('gitlab_config', {
api_token: ENV['GITLAB_APITOKEN'], api_token: ENV['GITLAB_APITOKEN'],
endpoint: ENV['GITLAB_ENDPOINT'], endpoint: ENV['GITLAB_ENDPOINT'],
schema: @cached_schema, # rubocop:disable RSpec/InstanceVariable
}) })
true true
end end
@ -1577,9 +1573,6 @@ RSpec.describe 'Ticket zoom', type: :system do
required_envs.each do |key| required_envs.each do |key|
skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank? skip("NOTICE: Missing environment variable #{key} for test! (Please fill up: #{required_envs.join(' && ')})") if ENV[key].blank?
end end
# request schema only once for performance reasons
@cached_schema = GitHub.new(ENV['GITHUB_ENDPOINT'], ENV['GITHUB_APITOKEN']).schema.to_json
end end
def authenticate def authenticate
@ -1587,7 +1580,6 @@ RSpec.describe 'Ticket zoom', type: :system do
Setting.set('github_config', { Setting.set('github_config', {
api_token: ENV['GITHUB_APITOKEN'], api_token: ENV['GITHUB_APITOKEN'],
endpoint: ENV['GITHUB_ENDPOINT'], endpoint: ENV['GITHUB_ENDPOINT'],
schema: @cached_schema, # rubocop:disable RSpec/InstanceVariable
}) })
true true
end end