1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# frozen_string_literal: true
# These endpoints partially mimic Github API behavior in order to successfully
# integrate with Jira Development Panel.
# Endpoints returning an empty list were temporarily added to avoid 404's
# during Jira's DVCS integration.
#
module API
module V3
class Github < ::API::Base
NO_SLASH_URL_PART_REGEX = %r{[^/]+}.freeze
ENDPOINT_REQUIREMENTS = {
namespace: NO_SLASH_URL_PART_REGEX,
project: NO_SLASH_URL_PART_REGEX,
username: NO_SLASH_URL_PART_REGEX
}.freeze
# Used to differentiate Jira Cloud requests from Jira Server requests
# Jira Cloud user agent format: Jira DVCS Connector Vertigo/version
# Jira Server user agent format: Jira DVCS Connector/version
JIRA_DVCS_CLOUD_USER_AGENT = 'Jira DVCS Connector Vertigo'.freeze
include PaginationParams
feature_category :integrations
before do
authorize_jira_user_agent!(request)
authenticate!
end
helpers do
params :project_full_path do
requires :namespace, type: String
requires :project, type: String
end
def authorize_jira_user_agent!(request)
not_found! unless Gitlab::Jira::Middleware.jira_dvcs_connector?(request.env)
end
def update_project_feature_usage_for(project)
# Prevent errors on GitLab Geo not allowing
# UPDATE statements to happen in GET requests.
return if Gitlab::Database.read_only?
project.log_jira_dvcs_integration_usage(cloud: jira_cloud?)
end
def jira_cloud?
request.env['HTTP_USER_AGENT'].include?(JIRA_DVCS_CLOUD_USER_AGENT)
end
def find_project_with_access(params)
project = find_project!(
::Gitlab::Jira::Dvcs.restore_full_path(**params.slice(:namespace, :project).symbolize_keys)
)
not_found! unless can?(current_user, :download_code, project)
project
end
# rubocop: disable CodeReuse/ActiveRecord
def find_merge_requests
merge_requests = authorized_merge_requests.reorder(updated_at: :desc)
paginate(merge_requests)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def find_merge_request_with_access(id, access_level = :read_merge_request)
merge_request = authorized_merge_requests.find_by(id: id)
not_found! unless can?(current_user, access_level, merge_request)
merge_request
end
# rubocop: enable CodeReuse/ActiveRecord
def authorized_merge_requests
MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?).execute
end
def authorized_merge_requests_for_project(project)
MergeRequestsFinder.new(current_user, authorized_only: !current_user.admin?, project_id: project.id).execute
end
# rubocop: disable CodeReuse/ActiveRecord
def find_notes(noteable)
# They're not presented on Jira Dev Panel ATM. A comments count with a
# redirect link is presented.
notes = paginate(noteable.notes.user.reorder(nil))
notes.select { |n| n.readable_by?(current_user) }
end
# rubocop: enable CodeReuse/ActiveRecord
end
resource :orgs do
get ':namespace/repos' do
present []
end
end
resource :user do
get :repos do
present []
end
end
resource :users do
params do
use :pagination
end
get ':namespace/repos' do
namespace = Namespace.find_by_full_path(params[:namespace])
not_found!('Namespace') unless namespace
projects = current_user.can_read_all_resources? ? Project.all : current_user.authorized_projects
projects = projects.in_namespace(namespace.self_and_descendants)
projects_cte = Project.wrap_with_cte(projects)
.eager_load_namespace_and_owner
.with_route
present paginate(projects_cte),
with: ::API::Github::Entities::Repository,
root_namespace: namespace.root_ancestor
end
get ':username' do
forbidden! unless can?(current_user, :read_users_list)
user = UsersFinder.new(current_user, { username: params[:username] }).execute.first
not_found! unless user
present user, with: ::API::Github::Entities::User
end
end
# Jira dev panel integration weirdly requests for "/-/jira/pulls" instead
# "/api/v3/repos/<namespace>/<project>/pulls". This forces us into
# returning _all_ Merge Requests from authorized projects (user is a member),
# instead just the authorized MRs from a project.
# Jira handles the filtering, presenting just MRs mentioning the Jira
# issue ID on the MR title / description.
resource :repos do
# Keeping for backwards compatibility with old Jira integration instructions
# so that users that do not change it will not suddenly have a broken integration
get '/-/jira/pulls' do
present find_merge_requests, with: ::API::Github::Entities::PullRequest
end
get '/-/jira/events' do
present []
end
params do
use :project_full_path
end
get ':namespace/:project/pulls' do
user_project = find_project_with_access(params)
merge_requests = authorized_merge_requests_for_project(user_project)
present paginate(merge_requests), with: ::API::Github::Entities::PullRequest
end
params do
use :project_full_path
end
get ':namespace/:project/pulls/:id' do
merge_request = find_merge_request_with_access(params[:id])
present merge_request, with: ::API::Github::Entities::PullRequest
end
# In Github, each Merge Request is automatically also an issue.
# Therefore we return its comments here.
# It'll present _just_ the comments counting with a link to GitLab on
# Jira dev panel, not the actual note content.
get ':namespace/:project/issues/:id/comments' do
merge_request = find_merge_request_with_access(params[:id])
present find_notes(merge_request), with: ::API::Github::Entities::NoteableComment
end
# This refer to "review" comments but Jira dev panel doesn't seem to
# present it accordingly.
get ':namespace/:project/pulls/:id/comments' do
present []
end
# Commits are not presented within "Pull Requests" modal on Jira dev
# panel.
get ':namespace/:project/pulls/:id/commits' do
present []
end
# Self-hosted Jira (tested on 7.11.1) requests this endpoint right
# after fetching branches.
# rubocop: disable CodeReuse/ActiveRecord
get ':namespace/:project/events' do
user_project = find_project_with_access(params)
merge_requests = authorized_merge_requests_for_project(user_project)
merge_requests = merge_requests.preload(:author, :assignees, :metrics, source_project: :namespace, target_project: :namespace)
present paginate(merge_requests), with: ::API::Github::Entities::PullRequestEvent
end
# rubocop: enable CodeReuse/ActiveRecord
params do
use :project_full_path
use :pagination
end
get ':namespace/:project/branches' do
user_project = find_project_with_access(params)
update_project_feature_usage_for(user_project)
branches = ::Kaminari.paginate_array(user_project.repository.branches.sort_by(&:name))
present paginate(branches), with: ::API::Github::Entities::Branch, project: user_project
end
params do
use :project_full_path
end
get ':namespace/:project/commits/:sha' do
user_project = find_project_with_access(params)
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
present commit, with: ::API::Github::Entities::RepoCommit
end
end
end
end
end
|