Read about our upcoming Code of Conduct on this issue

entities.rb 31.5 KB
Newer Older
1
module API
Nihad Abbasov's avatar
Nihad Abbasov committed
2
  module Entities
3
    class UserSafe < Grape::Entity
4
      expose :id, :name, :username
5
    end
6

7
    class UserBasic < UserSafe
8
      expose :state
9
10
11
      expose :avatar_url do |user, options|
        user.avatar_url(only_path: false)
      end
Douwe Maan's avatar
Douwe Maan committed
12
13

      expose :web_url do |user, options|
14
        Gitlab::Routing.url_helpers.user_url(user)
Douwe Maan's avatar
Douwe Maan committed
15
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
16
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
17

18
19
    class User < UserBasic
      expose :created_at
20
      expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
21
22
    end

23
24
    class UserActivity < Grape::Entity
      expose :username
25
26
      expose :last_activity_on
      expose :last_activity_on, as: :last_activity_at # Back-compat
27
28
    end

29
30
31
32
    class Identity < Grape::Entity
      expose :provider, :extern_uid
    end

33
    class UserPublic < User
34
35
      expose :last_sign_in_at
      expose :confirmed_at
36
      expose :last_activity_on
37
      expose :email
38
      expose :color_scheme_id, :projects_limit, :current_sign_in_at
39
      expose :identities, using: Entities::Identity
40
41
      expose :can_create_group?, as: :can_create_group
      expose :can_create_project?, as: :can_create_project
42
      expose :two_factor_enabled?, as: :two_factor_enabled
43
      expose :external
44
45
    end

46
    class UserWithAdmin < UserPublic
47
      expose :admin?, as: :is_admin
48
49
    end

50
51
52
53
    class UserWithPrivateDetails < UserWithAdmin
      expose :private_token
    end

54
55
56
57
    class Email < Grape::Entity
      expose :id, :email
    end

miks's avatar
miks committed
58
    class Hook < Grape::Entity
59
      expose :id, :url, :created_at, :push_events, :tag_push_events, :repository_update_events
60
      expose :enable_ssl_verification
miks's avatar
miks committed
61
62
    end

63
    class ProjectHook < Hook
64
      expose :project_id, :issues_events, :merge_requests_events
65
      expose :note_events, :pipeline_events, :wiki_page_events
66
      expose :job_events
67
68
    end

69
70
71
72
73
74
75
76
    class SharedGroup < Grape::Entity
      expose :group_id
      expose :group_name do |group_link, options|
        group_link.group.name
      end
      expose :group_access, as: :group_access_level
    end

77
78
79
80
81
82
83
84
85
    class BasicProjectDetails < Grape::Entity
      expose :id, :description, :default_branch, :tag_list
      expose :ssh_url_to_repo, :http_url_to_repo, :web_url
      expose :name, :name_with_namespace
      expose :path, :path_with_namespace
      expose :star_count, :forks_count
      expose :created_at, :last_activity_at
    end

86
    class Project < BasicProjectDetails
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
      include ::API::Helpers::RelatedResourcesHelpers

      expose :_links do
        expose :self do |project|
          expose_url(api_v4_projects_path(id: project.id))
        end

        expose :issues, if: -> (*args) { issues_available?(*args) } do |project|
          expose_url(api_v4_projects_issues_path(id: project.id))
        end

        expose :merge_requests, if: -> (*args) { mrs_available?(*args) } do |project|
          expose_url(api_v4_projects_merge_requests_path(id: project.id))
        end

        expose :repo_branches do |project|
          expose_url(api_v4_projects_repository_branches_path(id: project.id))
        end

        expose :labels do |project|
          expose_url(api_v4_projects_labels_path(id: project.id))
        end

        expose :events do |project|
          expose_url(api_v4_projects_events_path(id: project.id))
        end

        expose :members do |project|
          expose_url(api_v4_projects_members_path(id: project.id))
        end
      end

119
      expose :archived?, as: :archived
120
      expose :visibility
121
      expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
122
      expose :resolve_outdated_diff_discussions
Felipe Artur's avatar
Felipe Artur committed
123
124
125
      expose :container_registry_enabled

      # Expose old field names with the new permissions methods to keep API compatible
126
127
128
      expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
      expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
      expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
129
      expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
130
      expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
Felipe Artur's avatar
Felipe Artur committed
131

132
133
      expose :shared_runners_enabled
      expose :lfs_enabled?, as: :lfs_enabled
134
      expose :creator_id
135
      expose :namespace, using: 'API::Entities::Namespace'
136
      expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
137
138
      expose :import_status
      expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
139
140
141
      expose :avatar_url do |user, options|
        user.avatar_url(only_path: false)
      end
142
      expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
143
      expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
144
      expose :public_builds, as: :public_jobs
145
      expose :ci_config_path
146
147
148
      expose :shared_with_groups do |project, options|
        SharedGroup.represent(project.project_group_links.all, options)
      end
149
      expose :only_allow_merge_if_pipeline_succeeds
150
      expose :request_access_enabled
151
      expose :only_allow_merge_if_all_discussions_are_resolved
152
      expose :printing_merge_request_link_enabled
Markus Koller's avatar
Markus Koller committed
153
154
155
156
157
158
159
160
161

      expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
    end

    class ProjectStatistics < Grape::Entity
      expose :commit_count
      expose :storage_size
      expose :repository_size
      expose :lfs_objects_size
162
      expose :build_artifacts_size, as: :job_artifacts_size
Nihad Abbasov's avatar
Nihad Abbasov committed
163
164
    end

165
    class Member < UserBasic
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
166
      expose :access_level do |user, options|
167
        member = options[:member] || options[:source].members.find_by(user_id: user.id)
168
169
        member.access_level
      end
170
      expose :expires_at do |user, options|
171
        member = options[:member] || options[:source].members.find_by(user_id: user.id)
172
173
        member.expires_at
      end
174
175
176
177
    end

    class AccessRequester < UserBasic
      expose :requested_at do |user, options|
178
        access_requester = options[:access_requester] || options[:source].requesters.find_by(user_id: user.id)
179
        access_requester.requested_at
180
      end
miks's avatar
miks committed
181
182
    end

183
    class Group < Grape::Entity
184
      expose :id, :name, :path, :description, :visibility
185
      expose :lfs_enabled?, as: :lfs_enabled
186
187
188
      expose :avatar_url do |user, options|
        user.avatar_url(only_path: false)
      end
189
      expose :web_url
190
      expose :request_access_enabled
191
      expose :full_name, :full_path
192
193
194
195

      if ::Group.supports_nested_groups?
        expose :parent_id
      end
Markus Koller's avatar
Markus Koller committed
196
197
198
199
200
201

      expose :statistics, if: :statistics do
        with_options format_with: -> (value) { value.to_i } do
          expose :storage_size
          expose :repository_size
          expose :lfs_objects_size
202
          expose :build_artifacts_size, as: :job_artifacts_size
Markus Koller's avatar
Markus Koller committed
203
204
        end
      end
205
    end
Andrew8xx8's avatar
Andrew8xx8 committed
206

207
    class GroupDetail < Group
208
      expose :projects, using: Entities::Project
209
      expose :shared_projects, using: Entities::Project
210
211
    end

212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
    class RepoCommit < Grape::Entity
      expose :id, :short_id, :title, :created_at
      expose :parent_ids
      expose :safe_message, as: :message
      expose :author_name, :author_email, :authored_date
      expose :committer_name, :committer_email, :committed_date
    end

    class RepoCommitStats < Grape::Entity
      expose :additions, :deletions, :total
    end

    class RepoCommitDetail < RepoCommit
      expose :stats, using: Entities::RepoCommitStats
      expose :status
    end

229
    class RepoBranch < Grape::Entity
230
231
      expose :name

232
      expose :commit, using: Entities::RepoCommit do |repo_branch, options|
233
        options[:project].repository.commit(repo_branch.dereferenced_target)
234
235
      end

Robert Schilling's avatar
Robert Schilling committed
236
237
238
239
      expose :merged do |repo_branch, options|
        options[:project].repository.merged_to_root_ref?(repo_branch.name)
      end

240
      expose :protected do |repo_branch, options|
Eric's avatar
Eric committed
241
        ::ProtectedBranch.protected?(options[:project], repo_branch.name)
242
243
      end

244
      expose :developers_can_push do |repo_branch, options|
245
        options[:project].protected_branches.developers_can?(:push, repo_branch.name)
246
      end
247

248
      expose :developers_can_merge do |repo_branch, options|
249
        options[:project].protected_branches.developers_can?(:merge, repo_branch.name)
250
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
251
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
252

253
    class RepoTreeObject < Grape::Entity
254
      expose :id, :name, :type, :path
255
256

      expose :mode do |obj, options|
mhasbini's avatar
mhasbini committed
257
        filemode = obj.mode
258
259
260
261
262
        filemode = "0" + filemode if filemode.length < 6
        filemode
      end
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
263
    class ProjectSnippet < Grape::Entity
264
      expose :id, :title, :file_name, :description
265
      expose :author, using: Entities::UserBasic
Robert Speicher's avatar
Robert Speicher committed
266
      expose :updated_at, :created_at
267

268
269
270
      expose :web_url do |snippet, options|
        Gitlab::UrlBuilder.build(snippet)
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
271
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
272

273
    class PersonalSnippet < Grape::Entity
274
      expose :id, :title, :file_name, :description
275
276
277
278
279
280
281
282
283
284
285
      expose :author, using: Entities::UserBasic
      expose :updated_at, :created_at

      expose :web_url do |snippet|
        Gitlab::UrlBuilder.build(snippet)
      end
      expose :raw_url do |snippet|
        Gitlab::UrlBuilder.build(snippet) + "/raw"
      end
    end

286
287
    class ProjectEntity < Grape::Entity
      expose :id, :iid
Felipe Artur's avatar
Felipe Artur committed
288
      expose(:project_id) { |entity| entity&.project.try(:id) }
289
290
      expose :title, :description
      expose :state, :created_at, :updated_at
291
292
    end

293
294
    class RepoDiff < Grape::Entity
      expose :old_path, :new_path, :a_mode, :b_mode, :diff
295
296
297
      expose :new_file?, as: :new_file
      expose :renamed_file?, as: :renamed_file
      expose :deleted_file?, as: :deleted_file
298
299
    end

Eric's avatar
Eric committed
300
301
302
303
304
305
306
307
308
309
310
311
312
    class ProtectedRefAccess < Grape::Entity
      expose :access_level
      expose :access_level_description do |protected_ref_access|
        protected_ref_access.humanize
      end
    end

    class ProtectedBranch < Grape::Entity
      expose :name
      expose :push_access_levels, using: Entities::ProtectedRefAccess
      expose :merge_access_levels, using: Entities::ProtectedRefAccess
    end

Felipe Artur's avatar
Felipe Artur committed
313
314
    class Milestone < Grape::Entity
      expose :id, :iid
315
316
      expose :project_id, if: -> (entity, options) { entity&.project_id }
      expose :group_id, if: -> (entity, options) { entity&.group_id }
Felipe Artur's avatar
Felipe Artur committed
317
318
      expose :title, :description
      expose :state, :created_at, :updated_at
319
      expose :due_date
320
      expose :start_date
Nihad Abbasov's avatar
Nihad Abbasov committed
321
322
    end

323
    class IssueBasic < ProjectEntity
324
325
326
327
      expose :labels do |issue, options|
        # Avoids an N+1 query since labels are preloaded
        issue.labels.map(&:title).sort
      end
328
      expose :milestone, using: Entities::Milestone
329
330
331
332
333
      expose :assignees, :author, using: Entities::UserBasic

      expose :assignee, using: ::API::Entities::UserBasic do |issue, options|
        issue.assignees.first
      end
334

Z.J. van de Weg's avatar
Z.J. van de Weg committed
335
      expose :user_notes_count
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
      expose :upvotes do |issue, options|
        if options[:issuable_metadata]
          # Avoids an N+1 query when metadata is included
          options[:issuable_metadata][issue.id].upvotes
        else
          issue.upvotes
        end
      end
      expose :downvotes do |issue, options|
        if options[:issuable_metadata]
          # Avoids an N+1 query when metadata is included
          options[:issuable_metadata][issue.id].downvotes
        else
          issue.downvotes
        end
      end
352
      expose :due_date
353
      expose :confidential
354
355
356
357

      expose :web_url do |issue, options|
        Gitlab::UrlBuilder.build(issue)
      end
358
359
360
361

      expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
        issue
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
362
    end
Alex Denisov's avatar
Alex Denisov committed
363

364
    class Issue < IssueBasic
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
      include ::API::Helpers::RelatedResourcesHelpers

      expose :_links do
        expose :self do |issue|
          expose_url(api_v4_project_issue_path(id: issue.project_id, issue_iid: issue.iid))
        end

        expose :notes do |issue|
          expose_url(api_v4_projects_issues_notes_path(id: issue.project_id, noteable_id: issue.iid))
        end

        expose :award_emoji do |issue|
          expose_url(api_v4_projects_issues_award_emoji_path(id: issue.project_id, issue_iid: issue.iid))
        end

        expose :project do |issue|
          expose_url(api_v4_projects_path(id: issue.project_id))
        end
      end

385
386
387
388
389
      expose :subscribed do |issue, options|
        issue.subscribed?(options[:current_user], options[:project] || issue.project)
      end
    end

390
    class IssuableTimeStats < Grape::Entity
391
392
393
394
      format_with(:time_tracking_formatter) do |time_spent|
        Gitlab::TimeTrackingFormatter.output(time_spent)
      end

395
396
397
      expose :time_estimate
      expose :total_time_spent
      expose :human_time_estimate
398
399
400
401
402
403
404
405
406

      with_options(format_with: :time_tracking_formatter) do
        expose :total_time_spent, as: :human_total_time_spent
      end

      def total_time_spent
        # Avoids an N+1 query since timelogs are preloaded
        object.timelogs.map(&:time_spent).sum
      end
407
408
    end

409
410
411
412
413
    class ExternalIssue < Grape::Entity
      expose :title
      expose :id
    end

414
415
416
417
418
419
420
    class MergeRequestSimple < ProjectEntity
      expose :title
      expose :web_url do |merge_request, options|
        Gitlab::UrlBuilder.build(merge_request)
      end
    end

421
    class MergeRequestBasic < ProjectEntity
Valery Sizov's avatar
Valery Sizov committed
422
      expose :target_branch, :source_branch
423
424
425
426
427
428
429
430
431
432
433
434
435
436
      expose :upvotes do |merge_request, options|
        if options[:issuable_metadata]
          options[:issuable_metadata][merge_request.id].upvotes
        else
          merge_request.upvotes
        end
      end
      expose :downvotes do |merge_request, options|
        if options[:issuable_metadata]
          options[:issuable_metadata][merge_request.id].downvotes
        else
          merge_request.downvotes
        end
      end
437
438
      expose :author, :assignee, using: Entities::UserBasic
      expose :source_project_id, :target_project_id
439
440
441
442
      expose :labels do |merge_request, options|
        # Avoids an N+1 query since labels are preloaded
        merge_request.labels.map(&:title).sort
      end
443
      expose :work_in_progress?, as: :work_in_progress
444
      expose :milestone, using: Entities::Milestone
445
      expose :merge_when_pipeline_succeeds
446
      expose :merge_status
447
448
      expose :diff_head_sha, as: :sha
      expose :merge_commit_sha
Z.J. van de Weg's avatar
Z.J. van de Weg committed
449
      expose :user_notes_count
450
451
      expose :should_remove_source_branch?, as: :should_remove_source_branch
      expose :force_remove_source_branch?, as: :force_remove_source_branch
452
453
454
455

      expose :web_url do |merge_request, options|
        Gitlab::UrlBuilder.build(merge_request)
      end
456
457
458
459

      expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |merge_request|
        merge_request
      end
Alex Denisov's avatar
Alex Denisov committed
460
    end
Valeriy Sizov's avatar
Valeriy Sizov committed
461

462
463
464
465
466
467
    class MergeRequest < MergeRequestBasic
      expose :subscribed do |merge_request, options|
        merge_request.subscribed?(options[:current_user], options[:project])
      end
    end

468
469
    class MergeRequestChanges < MergeRequest
      expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _|
Douwe Maan's avatar
Douwe Maan committed
470
        compare.raw_diffs(limits: false).to_a
471
472
473
      end
    end

474
475
476
    class MergeRequestDiff < Grape::Entity
      expose :id, :head_commit_sha, :base_commit_sha, :start_commit_sha,
        :created_at, :merge_request_id, :state, :real_size
477
    end
478

479
    class MergeRequestDiffFull < MergeRequestDiff
480
481
482
      expose :commits, using: Entities::RepoCommit

      expose :diffs, using: Entities::RepoDiff do |compare, _|
Douwe Maan's avatar
Douwe Maan committed
483
        compare.raw_diffs(limits: false).to_a
484
485
486
      end
    end

487
    class SSHKey < Grape::Entity
488
      expose :id, :title, :key, :created_at, :can_push
Valeriy Sizov's avatar
Valeriy Sizov committed
489
    end
490

491
    class SSHKeyWithUser < SSHKey
492
      expose :user, using: Entities::UserPublic
493
494
    end

Robert Schilling's avatar
Robert Schilling committed
495
496
497
498
    class GPGKey < Grape::Entity
      expose :id, :key, :created_at
    end

499
    class Note < Grape::Entity
sue445's avatar
sue445 committed
500
501
502
      # Only Issue and MergeRequest have iid
      NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze

503
504
      expose :id
      expose :note, as: :body
505
      expose :attachment_identifier, as: :attachment
506
      expose :author, using: Entities::UserBasic
507
      expose :created_at, :updated_at
508
      expose :system?, as: :system
509
      expose :noteable_id, :noteable_type
sue445's avatar
sue445 committed
510
511
512

      # Avoid N+1 queries as much as possible
      expose(:noteable_iid) { |note| note.noteable.iid if NOTEABLE_TYPES_WITH_IID.include?(note.noteable_type) }
513
    end
514

515
516
517
518
519
520
521
522
    class AwardEmoji < Grape::Entity
      expose :id
      expose :name
      expose :user, using: Entities::UserBasic
      expose :created_at, :updated_at
      expose :awardable_id, :awardable_type
    end

523
524
525
526
    class MRNote < Grape::Entity
      expose :note
      expose :author, using: Entities::UserBasic
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
527

528
529
    class CommitNote < Grape::Entity
      expose :note
530
531
532
      expose(:path) { |note| note.diff_file.try(:file_path) if note.diff_note? }
      expose(:line) { |note| note.diff_line.try(:new_line) if note.diff_note? }
      expose(:line_type) { |note| note.diff_line.try(:type) if note.diff_note? }
533
      expose :author, using: Entities::UserBasic
534
      expose :created_at
535
536
    end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
537
538
    class CommitStatus < Grape::Entity
      expose :id, :sha, :ref, :status, :name, :target_url, :description,
539
             :created_at, :started_at, :finished_at, :allow_failure, :coverage
Kamil Trzcinski's avatar
Kamil Trzcinski committed
540
      expose :author, using: Entities::UserBasic
Kamil Trzcinski's avatar
Kamil Trzcinski committed
541
542
    end

543
544
545
546
547
    class PushEventPayload < Grape::Entity
      expose :commit_count, :action, :ref_type, :commit_from, :commit_to
      expose :ref, :commit_title
    end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
548
549
    class Event < Grape::Entity
      expose :title, :project_id, :action_name
sue445's avatar
sue445 committed
550
      expose :target_id, :target_iid, :target_type, :author_id
551
      expose :target_title
552
      expose :created_at
553
554
      expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
      expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
555

556
557
558
559
560
      expose :push_event_payload,
        as: :push_data,
        using: PushEventPayload,
        if: -> (event, _) { event.push? }

561
      expose :author_username do |event, options|
Z.J. van de Weg's avatar
Z.J. van de Weg committed
562
        event.author&.username
563
      end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
564
    end
565

566
    class ProjectGroupLink < Grape::Entity
567
      expose :id, :project_id, :group_id, :group_access, :expires_at
568
569
    end

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
570
571
572
573
    class Todo < Grape::Entity
      expose :id
      expose :project, using: Entities::BasicProjectDetails
      expose :author, using: Entities::UserBasic
Robert Schilling's avatar
Robert Schilling committed
574
      expose :action_name
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
575
      expose :target_type
576
577

      expose :target do |todo, options|
578
579
        target = todo.target_type == 'Commit' ? 'RepoCommit' : todo.target_type
        Entities.const_get(target).represent(todo.target, options)
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
580
581
582
583
584
      end

      expose :target_url do |todo, options|
        target_type   = todo.target_type.underscore
        target_url    = "namespace_project_#{target_type}_url"
585
        target_anchor = "note_#{todo.note_id}" if todo.note_id?
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
586

587
588
589
        Gitlab::Routing
          .url_helpers
          .public_send(target_url, todo.project.namespace, todo.project, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
590
591
592
593
594
595
596
      end

      expose :body
      expose :state
      expose :created_at
    end

597
    class Namespace < Grape::Entity
598
599
      expose :id, :name, :path, :kind, :full_path, :parent_id

600
601
602
603
604
605
      expose :members_count_with_descendants, if: -> (namespace, opts) { expose_members_count_with_descendants?(namespace, opts) } do |namespace, _|
        namespace.users_with_descendants.count
      end

      def expose_members_count_with_descendants?(namespace, opts)
        namespace.kind == 'group' && Ability.allowed?(opts[:current_user], :admin_group, namespace)
606
      end
607
    end
608

609
    class MemberAccess < Grape::Entity
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
610
      expose :access_level
611
612
      expose :notification_level do |member, options|
        if member.notification_setting
613
          ::NotificationSetting.levels[member.notification_setting.level]
614
615
        end
      end
616
617
    end

618
    class ProjectAccess < MemberAccess
619
620
    end

621
    class GroupAccess < MemberAccess
622
623
    end

624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
    class NotificationSetting < Grape::Entity
      expose :level
      expose :events, if: ->(notification_setting, _) { notification_setting.custom? } do
        ::NotificationSetting::EMAIL_EVENTS.each do |event|
          expose event
        end
      end
    end

    class GlobalNotificationSetting < NotificationSetting
      expose :notification_email do |notification_setting, options|
        notification_setting.user.notification_email
      end
    end

639
640
    class ProjectService < Grape::Entity
      expose :id, :title, :created_at, :updated_at, :active
641
      expose :push_events, :issues_events, :merge_requests_events
642
      expose :tag_push_events, :note_events, :pipeline_events
643
      expose :job_events
644
645
      # Expose serialized properties
      expose :properties do |service, options|
646
647
648
        field_names = service.fields
          .select { |field| options[:include_passwords] || field[:type] != 'password' }
          .map { |field| field[:name] }
649
650
651
652
        service.properties.slice(*field_names)
      end
    end

653
654
655
    class ProjectWithAccess < Project
      expose :permissions do
        expose :project_access, using: Entities::ProjectAccess do |project, options|
656
657
658
659
660
          if options.key?(:project_members)
            (options[:project_members] || []).find { |member| member.source_id == project.id }
          else
            project.project_members.find_by(user_id: options[:current_user].id)
          end
661
662
663
        end

        expose :group_access, using: Entities::GroupAccess do |project, options|
664
          if project.group
665
666
667
668
669
            if options.key?(:group_members)
              (options[:group_members] || []).find { |member| member.source_id == project.namespace_id }
            else
              project.group.group_members.find_by(user_id: options[:current_user].id)
            end
670
          end
671
672
673
        end
      end
    end
674

Andre Guedes's avatar
Andre Guedes committed
675
    class LabelBasic < Grape::Entity
Rares Sfirlogea's avatar
Rares Sfirlogea committed
676
      expose :id, :name, :color, :description
Andre Guedes's avatar
Andre Guedes committed
677
678
679
    end

    class Label < LabelBasic
680
      expose :open_issues_count do |label, options|
Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
681
682
        label.open_issues_count(options[:current_user])
      end
683

Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
684
685
686
      expose :closed_issues_count do |label, options|
        label.closed_issues_count(options[:current_user])
      end
687

Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
688
689
      expose :open_merge_requests_count do |label, options|
        label.open_merge_requests_count(options[:current_user])
690
691
      end

692
693
694
      expose :priority do |label, options|
        label.priority(options[:project])
      end
695
696

      expose :subscribed do |label, options|
697
        label.subscribed?(options[:current_user], options[:project])
698
      end
699
    end
700

Andre Guedes's avatar
Andre Guedes committed
701
702
703
704
705
706
707
708
709
710
711
712
713
    class List < Grape::Entity
      expose :id
      expose :label, using: Entities::LabelBasic
      expose :position
    end

    class Board < Grape::Entity
      expose :id
      expose :lists, using: Entities::List do |board|
        board.lists.destroyable
      end
    end

714
715
    class Compare < Grape::Entity
      expose :commit, using: Entities::RepoCommit do |compare, options|
716
        Commit.decorate(compare.commits, nil).last
717
      end
718

719
      expose :commits, using: Entities::RepoCommit do |compare, options|
720
        Commit.decorate(compare.commits, nil)
721
      end
722

723
      expose :diffs, using: Entities::RepoDiff do |compare, options|
Douwe Maan's avatar
Douwe Maan committed
724
        compare.diffs(limits: false).to_a
725
      end
726
727

      expose :compare_timeout do |compare, options|
728
        compare.diffs.overflow?
729
730
731
      end

      expose :same, as: :compare_same_ref
732
    end
733
734
735
736

    class Contributor < Grape::Entity
      expose :name, :email, :commits, :additions, :deletions
    end
737
738
739
740

    class BroadcastMessage < Grape::Entity
      expose :message, :starts_at, :ends_at, :color, :font
    end
741
742
743

    class ApplicationSetting < Grape::Entity
      expose :id
744
      expose(*::ApplicationSettingsHelper.visible_attributes)
745
746
747
748
749
750
      expose(:restricted_visibility_levels) do |setting, _options|
        setting.restricted_visibility_levels.map { |level| Gitlab::VisibilityLevel.string_level(level) }
      end
      expose(:default_project_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_project_visibility) }
      expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) }
      expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) }
751
      expose :password_authentication_enabled, as: :signin_enabled
752
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
753
754

    class Release < Grape::Entity
755
756
      expose :tag, as: :tag_name
      expose :description
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
757
    end
758
759

    class RepoTag < Grape::Entity
760
      expose :name, :message
761

762
      expose :commit, using: Entities::RepoCommit do |repo_tag, options|
763
        options[:project].repository.commit(repo_tag.dereferenced_target)
764
765
      end

766
767
      expose :release, using: Entities::Release do |repo_tag, options|
        options[:project].releases.find_by(tag: repo_tag.name)
768
769
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
770

771
    class Runner < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
772
773
774
775
776
777
778
      expose :id
      expose :description
      expose :active
      expose :is_shared
      expose :name
    end

779
780
    class RunnerDetails < Runner
      expose :tag_list
781
      expose :run_untagged
782
      expose :locked
Shinya Maeda's avatar
Shinya Maeda committed
783
      expose :access_level
784
      expose :version, :revision, :platform, :architecture
785
      expose :contacted_at
blackst0ne's avatar
blackst0ne committed
786
      expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.is_shared? }
787
      expose :projects, with: Entities::BasicProjectDetails do |runner, options|
blackst0ne's avatar
blackst0ne committed
788
        if options[:current_user].admin?
789
790
          runner.projects
        else
791
          options[:current_user].authorized_projects.where(id: runner.projects)
792
793
        end
      end
794
795
    end

796
797
798
799
    class RunnerRegistrationDetails < Grape::Entity
      expose :id, :token
    end

800
    class JobArtifactFile < Grape::Entity
801
802
803
      expose :filename, :size
    end

804
805
806
807
    class PipelineBasic < Grape::Entity
      expose :id, :sha, :ref, :status
    end

808
    class Job < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
809
      expose :id, :status, :stage, :name, :ref, :tag, :coverage
810
      expose :created_at, :started_at, :finished_at
Tomasz Maczukin's avatar
Tomasz Maczukin committed
811
      expose :user, with: User
Z.J. van de Weg's avatar
Z.J. van de Weg committed
812
      expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? }
813
      expose :commit, with: RepoCommit
814
      expose :runner, with: Runner
815
      expose :pipeline, with: PipelineBasic
816
    end
817

Tomasz Maczukin's avatar
Tomasz Maczukin committed
818
    class Trigger < Grape::Entity
819
      expose :id
820
821
822
      expose :token, :description
      expose :created_at, :updated_at, :deleted_at, :last_used
      expose :owner, using: Entities::UserBasic
Tomasz Maczukin's avatar
Tomasz Maczukin committed
823
    end
824

825
    class Variable < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
826
      expose :key, :value
Shinya Maeda's avatar
Shinya Maeda committed
827
      expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
828
    end
829

830
831
    class Pipeline < PipelineBasic
      expose :before_sha, :tag, :yaml_errors
Z.J. van de Weg's avatar
Z.J. van de Weg committed
832
833
834
835

      expose :user, with: Entities::UserBasic
      expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
      expose :duration
836
      expose :coverage
Z.J. van de Weg's avatar
Z.J. van de Weg committed
837
838
    end

839
840
841
    class PipelineSchedule < Grape::Entity
      expose :id
      expose :description, :ref, :cron, :cron_timezone, :next_run_at, :active
842
      expose :created_at, :updated_at
843
844
845
      expose :owner, using: Entities::UserBasic
    end

Shinya Maeda's avatar
Shinya Maeda committed
846
847
    class PipelineScheduleDetails < PipelineSchedule
      expose :last_pipeline, using: Entities::PipelineBasic
848
      expose :variables, using: Entities::Variable
Shinya Maeda's avatar
Shinya Maeda committed
849
850
    end

851
    class EnvironmentBasic < Grape::Entity
Nick Thomas's avatar
Nick Thomas committed
852
      expose :id, :name, :slug, :external_url
853
854
    end

855
    class Environment < EnvironmentBasic
856
      expose :project, using: Entities::BasicProjectDetails
Z.J. van de Weg's avatar
Z.J. van de Weg committed
857
858
859
860
    end

    class Deployment < Grape::Entity
      expose :id, :iid, :ref, :sha, :created_at
861
862
      expose :user,        using: Entities::UserBasic
      expose :environment, using: Entities::EnvironmentBasic
863
      expose :deployable,  using: Entities::Job
864
865
    end

866
    class RepoLicense < Grape::Entity
867
868
      expose :key, :name, :nickname
      expose :featured, as: :popular
869
870
871
      expose :url, as: :html_url
      expose(:source_url) { |license| license.meta['source'] }
      expose(:description) { |license| license.meta['description'] }
872
873
874
      expose(:conditions) { |license| license.meta['conditions'] }
      expose(:permissions) { |license| license.meta['permissions'] }
      expose(:limitations) { |license| license.meta['limitations'] }
875
876
      expose :content
    end
877

ZJ van de Weg's avatar
ZJ van de Weg committed
878
    class TemplatesList < Grape::Entity
879
880
881
      expose :name
    end

ZJ van de Weg's avatar
ZJ van de Weg committed
882
    class Template < Grape::Entity
883
884
      expose :name, :content
    end
885
886
887
888
889

    class BroadcastMessage < Grape::Entity
      expose :id, :message, :starts_at, :ends_at, :color, :font
      expose :active?, as: :active
    end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
890

891
    class PersonalAccessToken < Grape::Entity
892
893
894
895
896
897
898
      expose :id, :name, :revoked, :created_at, :scopes
      expose :active?, as: :active
      expose :expires_at do |personal_access_token|
        personal_access_token.expires_at ? personal_access_token.expires_at.strftime("%Y-%m-%d") : nil
      end
    end

899
    class PersonalAccessTokenWithToken < PersonalAccessToken
900
901
      expose :token
    end
902
903
904
905

    class ImpersonationToken < PersonalAccessTokenWithToken
      expose :impersonation
    end
906

907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
    class FeatureGate < Grape::Entity
      expose :key
      expose :value
    end

    class Feature < Grape::Entity
      expose :name
      expose :state
      expose :gates, using: FeatureGate do |model|
        model.gates.map do |gate|
          value = model.gate_values[gate.key]

          # By default all gate values are populated. Only show relevant ones.
          if (value.is_a?(Integer) && value.zero?) || (value.is_a?(Set) && value.empty?)
            next
          end

          { key: gate.key, value: value }
        end.compact
      end
    end

929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
    module JobRequest
      class JobInfo < Grape::Entity
        expose :name, :stage
        expose :project_id, :project_name
      end

      class GitInfo < Grape::Entity
        expose :repo_url, :ref, :sha, :before_sha
        expose :ref_type do |model|
          if model.tag
            'tag'
          else
            'branch'
          end
        end
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
945

946
947
948
      class RunnerInfo < Grape::Entity
        expose :timeout
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
949

950
      class Step < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
951
        expose :name, :script, :timeout, :when, :allow_failure
952
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
953

954
      class Image < Grape::Entity
955
956
957
        expose :name, :entrypoint
      end

958
      class Service < Image
959
        expose :alias, :command
960
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
961

962
963
      class Artifacts < Grape::Entity
        expose :name, :untracked, :paths, :when, :expire_in
Tomasz Maczukin's avatar
Tomasz Maczukin committed
964
965
      end

966
      class Cache < Grape::Entity
967
        expose :key, :untracked, :paths, :policy
Tomasz Maczukin's avatar
Tomasz Maczukin committed
968
969
      end

970
971
972
      class Credentials < Grape::Entity
        expose :type, :url, :username, :password
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
973

974
975
976
977
978
      class ArtifactFile < Grape::Entity
        expose :filename, :size
      end

      class Dependency < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
979
        expose :id, :name, :token
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996