entities.rb 31.9 KB
Newer Older
1
module API
Nihad Abbasov's avatar
Nihad Abbasov committed
2
  module Entities
3 4 5 6 7 8 9 10 11 12
    class WikiPageBasic < Grape::Entity
      expose :format
      expose :slug
      expose :title
    end

    class WikiPage < WikiPageBasic
      expose :content
    end

13
    class UserSafe < Grape::Entity
14
      expose :id, :name, :username
15
    end
16

17
    class UserBasic < UserSafe
18
      expose :state
19 20 21
      expose :avatar_url do |user, options|
        user.avatar_url(only_path: false)
      end
Douwe Maan's avatar
Douwe Maan committed
22 23

      expose :web_url do |user, options|
24
        Gitlab::Routing.url_helpers.user_url(user)
Douwe Maan's avatar
Douwe Maan committed
25
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
26
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
27

28 29
    class User < UserBasic
      expose :created_at
30
      expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
31 32
    end

33 34
    class UserActivity < Grape::Entity
      expose :username
35 36
      expose :last_activity_on
      expose :last_activity_on, as: :last_activity_at # Back-compat
37 38
    end

39 40 41 42
    class Identity < Grape::Entity
      expose :provider, :extern_uid
    end

43
    class UserPublic < User
44 45
      expose :last_sign_in_at
      expose :confirmed_at
46
      expose :last_activity_on
47
      expose :email
48
      expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at
49
      expose :identities, using: Entities::Identity
50 51
      expose :can_create_group?, as: :can_create_group
      expose :can_create_project?, as: :can_create_project
52
      expose :two_factor_enabled?, as: :two_factor_enabled
53
      expose :external
54 55
    end

56
    class UserWithAdmin < UserPublic
57
      expose :admin?, as: :is_admin
58 59
    end

60 61 62 63
    class UserWithPrivateDetails < UserWithAdmin
      expose :private_token
    end

64 65 66 67
    class Email < Grape::Entity
      expose :id, :email
    end

miks's avatar
miks committed
68
    class Hook < Grape::Entity
69
      expose :id, :url, :created_at, :push_events, :tag_push_events, :repository_update_events
70
      expose :enable_ssl_verification
miks's avatar
miks committed
71 72
    end

73
    class ProjectHook < Hook
74
      expose :project_id, :issues_events, :merge_requests_events
75
      expose :note_events, :pipeline_events, :wiki_page_events
76
      expose :job_events
77 78
    end

79 80 81 82 83 84 85 86
    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

87 88 89 90 91
    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
92 93 94
      expose :avatar_url do |project, options|
        project.avatar_url(only_path: false)
      end
95 96 97 98
      expose :star_count, :forks_count
      expose :created_at, :last_activity_at
    end

99
    class Project < BasicProjectDetails
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
      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

132
      expose :archived?, as: :archived
133
      expose :visibility
134
      expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
135
      expose :resolve_outdated_diff_discussions
136 137 138
      expose :container_registry_enabled

      # Expose old field names with the new permissions methods to keep API compatible
139 140 141
      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]) }
142
      expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
143
      expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
144

145 146
      expose :shared_runners_enabled
      expose :lfs_enabled?, as: :lfs_enabled
147
      expose :creator_id
148
      expose :namespace, using: 'API::Entities::Namespace'
149
      expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda { |project, options| project.forked? }
150 151
      expose :import_status
      expose :import_error, if: lambda { |_project, options| options[:user_can_admin_project] }
152

153
      expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
154
      expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
155
      expose :public_builds, as: :public_jobs
156
      expose :ci_config_path
157 158 159
      expose :shared_with_groups do |project, options|
        SharedGroup.represent(project.project_group_links.all, options)
      end
160
      expose :only_allow_merge_if_pipeline_succeeds
161
      expose :request_access_enabled
162
      expose :only_allow_merge_if_all_discussions_are_resolved
163
      expose :printing_merge_request_link_enabled
164 165 166 167 168 169 170 171 172

      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
173
      expose :build_artifacts_size, as: :job_artifacts_size
Nihad Abbasov's avatar
Nihad Abbasov committed
174 175
    end

176
    class Member < UserBasic
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
177
      expose :access_level do |user, options|
178
        member = options[:member] || options[:source].members.find_by(user_id: user.id)
179 180
        member.access_level
      end
181
      expose :expires_at do |user, options|
182
        member = options[:member] || options[:source].members.find_by(user_id: user.id)
183 184
        member.expires_at
      end
185 186 187 188
    end

    class AccessRequester < UserBasic
      expose :requested_at do |user, options|
189
        access_requester = options[:access_requester] || options[:source].requesters.find_by(user_id: user.id)
190
        access_requester.requested_at
191
      end
miks's avatar
miks committed
192 193
    end

194
    class Group < Grape::Entity
195
      expose :id, :name, :path, :description, :visibility
196
      expose :lfs_enabled?, as: :lfs_enabled
197 198
      expose :avatar_url do |group, options|
        group.avatar_url(only_path: false)
199
      end
200
      expose :web_url
201
      expose :request_access_enabled
202
      expose :full_name, :full_path
203 204 205 206

      if ::Group.supports_nested_groups?
        expose :parent_id
      end
207 208 209 210 211 212

      expose :statistics, if: :statistics do
        with_options format_with: -> (value) { value.to_i } do
          expose :storage_size
          expose :repository_size
          expose :lfs_objects_size
213
          expose :build_artifacts_size, as: :job_artifacts_size
214 215
        end
      end
216
    end
Andrew8xx8's avatar
Andrew8xx8 committed
217

218
    class GroupDetail < Group
219
      expose :projects, using: Entities::Project
220
      expose :shared_projects, using: Entities::Project
221 222
    end

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    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
238
      expose :last_pipeline, using: 'API::Entities::PipelineBasic'
239 240
    end

241
    class RepoBranch < Grape::Entity
242 243
      expose :name

244
      expose :commit, using: Entities::RepoCommit do |repo_branch, options|
245
        options[:project].repository.commit(repo_branch.dereferenced_target)
246 247
      end

248
      expose :merged do |repo_branch, options|
249 250 251 252
        # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37442
        Gitlab::GitalyClient.allow_n_plus_1_calls do
          options[:project].repository.merged_to_root_ref?(repo_branch.name)
        end
253 254
      end

255
      expose :protected do |repo_branch, options|
256
        ::ProtectedBranch.protected?(options[:project], repo_branch.name)
257 258
      end

259
      expose :developers_can_push do |repo_branch, options|
260
        options[:project].protected_branches.developers_can?(:push, repo_branch.name)
261
      end
262

263
      expose :developers_can_merge do |repo_branch, options|
264
        options[:project].protected_branches.developers_can?(:merge, repo_branch.name)
265
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
266
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
267

268
    class RepoTreeObject < Grape::Entity
269
      expose :id, :name, :type, :path
270 271

      expose :mode do |obj, options|
272
        filemode = obj.mode
273 274 275 276 277
        filemode = "0" + filemode if filemode.length < 6
        filemode
      end
    end

Nihad Abbasov's avatar
Nihad Abbasov committed
278
    class ProjectSnippet < Grape::Entity
279
      expose :id, :title, :file_name, :description
280
      expose :author, using: Entities::UserBasic
281
      expose :updated_at, :created_at
282

283 284 285
      expose :web_url do |snippet, options|
        Gitlab::UrlBuilder.build(snippet)
      end
Nihad Abbasov's avatar
Nihad Abbasov committed
286
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
287

288
    class PersonalSnippet < Grape::Entity
289
      expose :id, :title, :file_name, :description
290 291 292 293 294 295 296 297 298 299 300
      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

301 302
    class ProjectEntity < Grape::Entity
      expose :id, :iid
Felipe Artur's avatar
Felipe Artur committed
303
      expose(:project_id) { |entity| entity&.project.try(:id) }
304 305
      expose :title, :description
      expose :state, :created_at, :updated_at
306 307
    end

308
    class RepoDiff < Grape::Entity
309
      expose :old_path, :new_path, :a_mode, :b_mode
310 311 312
      expose :new_file?, as: :new_file
      expose :renamed_file?, as: :renamed_file
      expose :deleted_file?, as: :deleted_file
313
      expose :json_safe_diff, as: :diff
314 315
    end

316 317 318 319 320 321 322 323 324 325 326 327 328
    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
329 330
    class Milestone < Grape::Entity
      expose :id, :iid
331 332
      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
333 334
      expose :title, :description
      expose :state, :created_at, :updated_at
335
      expose :due_date
336
      expose :start_date
Nihad Abbasov's avatar
Nihad Abbasov committed
337 338
    end

339
    class IssueBasic < ProjectEntity
340
      expose :closed_at
341 342 343 344
      expose :labels do |issue, options|
        # Avoids an N+1 query since labels are preloaded
        issue.labels.map(&:title).sort
      end
345
      expose :milestone, using: Entities::Milestone
346 347 348 349 350
      expose :assignees, :author, using: Entities::UserBasic

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

Z.J. van de Weg's avatar
Z.J. van de Weg committed
352
      expose :user_notes_count
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
      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
369
      expose :due_date
370
      expose :confidential
371 372 373 374

      expose :web_url do |issue, options|
        Gitlab::UrlBuilder.build(issue)
      end
375 376 377 378

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

381
    class Issue < IssueBasic
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
      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

402 403 404 405 406
      expose :subscribed do |issue, options|
        issue.subscribed?(options[:current_user], options[:project] || issue.project)
      end
    end

407
    class IssuableTimeStats < Grape::Entity
408 409 410 411
      format_with(:time_tracking_formatter) do |time_spent|
        Gitlab::TimeTrackingFormatter.output(time_spent)
      end

412 413 414
      expose :time_estimate
      expose :total_time_spent
      expose :human_time_estimate
415 416 417 418 419 420 421 422 423

      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
424 425
    end

426 427 428 429 430
    class ExternalIssue < Grape::Entity
      expose :title
      expose :id
    end

431 432 433 434 435 436 437
    class MergeRequestSimple < ProjectEntity
      expose :title
      expose :web_url do |merge_request, options|
        Gitlab::UrlBuilder.build(merge_request)
      end
    end

438
    class MergeRequestBasic < ProjectEntity
Valery Sizov's avatar
Valery Sizov committed
439
      expose :target_branch, :source_branch
440 441 442 443 444 445 446 447 448 449 450 451 452 453
      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
454 455
      expose :author, :assignee, using: Entities::UserBasic
      expose :source_project_id, :target_project_id
456 457 458 459
      expose :labels do |merge_request, options|
        # Avoids an N+1 query since labels are preloaded
        merge_request.labels.map(&:title).sort
      end
460
      expose :work_in_progress?, as: :work_in_progress
461
      expose :milestone, using: Entities::Milestone
462
      expose :merge_when_pipeline_succeeds
463
      expose :merge_status
464 465
      expose :diff_head_sha, as: :sha
      expose :merge_commit_sha
Z.J. van de Weg's avatar
Z.J. van de Weg committed
466
      expose :user_notes_count
467 468
      expose :should_remove_source_branch?, as: :should_remove_source_branch
      expose :force_remove_source_branch?, as: :force_remove_source_branch
469 470 471 472

      expose :web_url do |merge_request, options|
        Gitlab::UrlBuilder.build(merge_request)
      end
473 474 475 476

      expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |merge_request|
        merge_request
      end
Alex Denisov's avatar
Alex Denisov committed
477
    end
478

479 480 481 482 483 484
    class MergeRequest < MergeRequestBasic
      expose :subscribed do |merge_request, options|
        merge_request.subscribed?(options[:current_user], options[:project])
      end
    end

485 486
    class MergeRequestChanges < MergeRequest
      expose :diffs, as: :changes, using: Entities::RepoDiff do |compare, _|
Douwe Maan's avatar
Douwe Maan committed
487
        compare.raw_diffs(limits: false).to_a
488 489 490
      end
    end

491 492 493
    class MergeRequestDiff < Grape::Entity
      expose :id, :head_commit_sha, :base_commit_sha, :start_commit_sha,
        :created_at, :merge_request_id, :state, :real_size
494
    end
495

496
    class MergeRequestDiffFull < MergeRequestDiff
497 498 499
      expose :commits, using: Entities::RepoCommit

      expose :diffs, using: Entities::RepoDiff do |compare, _|
Douwe Maan's avatar
Douwe Maan committed
500
        compare.raw_diffs(limits: false).to_a
501 502 503
      end
    end

504
    class SSHKey < Grape::Entity
505
      expose :id, :title, :key, :created_at, :can_push
506
    end
507

508
    class SSHKeyWithUser < SSHKey
509
      expose :user, using: Entities::UserPublic
510 511
    end

512 513 514 515
    class GPGKey < Grape::Entity
      expose :id, :key, :created_at
    end

516
    class Note < Grape::Entity
sue445's avatar
sue445 committed
517 518 519
      # Only Issue and MergeRequest have iid
      NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze

520 521
      expose :id
      expose :note, as: :body
522
      expose :attachment_identifier, as: :attachment
523
      expose :author, using: Entities::UserBasic
524
      expose :created_at, :updated_at
525
      expose :system?, as: :system
526
      expose :noteable_id, :noteable_type
sue445's avatar
sue445 committed
527 528 529

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

532 533 534 535 536 537 538 539
    class AwardEmoji < Grape::Entity
      expose :id
      expose :name
      expose :user, using: Entities::UserBasic
      expose :created_at, :updated_at
      expose :awardable_id, :awardable_type
    end

540 541 542 543
    class MRNote < Grape::Entity
      expose :note
      expose :author, using: Entities::UserBasic
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
544

545 546
    class CommitNote < Grape::Entity
      expose :note
547 548 549
      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? }
550
      expose :author, using: Entities::UserBasic
551
      expose :created_at
552 553
    end

554 555
    class CommitStatus < Grape::Entity
      expose :id, :sha, :ref, :status, :name, :target_url, :description,
556
             :created_at, :started_at, :finished_at, :allow_failure, :coverage
Kamil Trzcinski's avatar
Kamil Trzcinski committed
557
      expose :author, using: Entities::UserBasic
558 559
    end

560 561 562 563 564
    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
565
    class Event < Grape::Entity
566
      expose :project_id, :action_name
sue445's avatar
sue445 committed
567
      expose :target_id, :target_iid, :target_type, :author_id
568
      expose :target_title
569
      expose :created_at
570 571
      expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
      expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
572

573 574 575 576 577
      expose :push_event_payload,
        as: :push_data,
        using: PushEventPayload,
        if: -> (event, _) { event.push? }

578
      expose :author_username do |event, options|
579
        event.author&.username
580
      end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
581
    end
582

583
    class ProjectGroupLink < Grape::Entity
584
      expose :id, :project_id, :group_id, :group_access, :expires_at
585 586
    end

Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
587 588 589 590
    class Todo < Grape::Entity
      expose :id
      expose :project, using: Entities::BasicProjectDetails
      expose :author, using: Entities::UserBasic
Robert Schilling's avatar
Robert Schilling committed
591
      expose :action_name
Douglas Barbosa Alexandre's avatar
Douglas Barbosa Alexandre committed
592
      expose :target_type
593 594

      expose :target do |todo, options|
595 596
        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
597 598 599 600 601
      end

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

604 605 606
        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
607 608 609 610 611 612 613
      end

      expose :body
      expose :state
      expose :created_at
    end

614
    class Namespace < Grape::Entity
615 616
      expose :id, :name, :path, :kind, :full_path, :parent_id

617 618 619 620 621 622
      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)
623
      end
624
    end
625

626
    class MemberAccess < Grape::Entity
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
627
      expose :access_level
628 629
      expose :notification_level do |member, options|
        if member.notification_setting
630
          ::NotificationSetting.levels[member.notification_setting.level]
631 632
        end
      end
633 634
    end

635
    class ProjectAccess < MemberAccess
636 637
    end

638
    class GroupAccess < MemberAccess
639 640
    end

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
    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

656 657
    class ProjectService < Grape::Entity
      expose :id, :title, :created_at, :updated_at, :active
658
      expose :push_events, :issues_events, :merge_requests_events
659
      expose :tag_push_events, :note_events, :pipeline_events
660
      expose :job_events
661 662
      # Expose serialized properties
      expose :properties do |service, options|
663 664 665
        field_names = service.fields
          .select { |field| options[:include_passwords] || field[:type] != 'password' }
          .map { |field| field[:name] }
666 667 668 669
        service.properties.slice(*field_names)
      end
    end

670 671 672
    class ProjectWithAccess < Project
      expose :permissions do
        expose :project_access, using: Entities::ProjectAccess do |project, options|
673 674 675 676 677
          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
678 679 680
        end

        expose :group_access, using: Entities::GroupAccess do |project, options|
681
          if project.group
682 683 684 685 686
            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
687
          end
688 689 690
        end
      end
    end
691

692
    class LabelBasic < Grape::Entity
Rares Sfirlogea's avatar
Rares Sfirlogea committed
693
      expose :id, :name, :color, :description
694 695 696
    end

    class Label < LabelBasic
697
      expose :open_issues_count do |label, options|
Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
698 699
        label.open_issues_count(options[:current_user])
      end
700

Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
701 702 703
      expose :closed_issues_count do |label, options|
        label.closed_issues_count(options[:current_user])
      end
704

Francesco Coda Zabetta's avatar
Francesco Coda Zabetta committed
705 706
      expose :open_merge_requests_count do |label, options|
        label.open_merge_requests_count(options[:current_user])
707 708
      end

709 710 711
      expose :priority do |label, options|
        label.priority(options[:project])
      end
712 713

      expose :subscribed do |label, options|
714
        label.subscribed?(options[:current_user], options[:project])
715
      end
716
    end
717

718 719 720 721 722 723 724 725 726 727 728 729 730
    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

731 732
    class Compare < Grape::Entity
      expose :commit, using: Entities::RepoCommit do |compare, options|
733
        Commit.decorate(compare.commits, nil).last
734
      end
735

736
      expose :commits, using: Entities::RepoCommit do |compare, options|
737
        Commit.decorate(compare.commits, nil)
738
      end
739

740
      expose :diffs, using: Entities::RepoDiff do |compare, options|
Douwe Maan's avatar
Douwe Maan committed
741
        compare.diffs(limits: false).to_a
742
      end
743 744

      expose :compare_timeout do |compare, options|
745
        compare.diffs.overflow?
746 747 748
      end

      expose :same, as: :compare_same_ref
749
    end
750 751 752 753

    class Contributor < Grape::Entity
      expose :name, :email, :commits, :additions, :deletions
    end
754 755 756 757

    class BroadcastMessage < Grape::Entity
      expose :message, :starts_at, :ends_at, :color, :font
    end
758 759 760

    class ApplicationSetting < Grape::Entity
      expose :id
761
      expose(*::ApplicationSettingsHelper.visible_attributes)
762 763 764 765 766 767
      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) }
768
      expose :password_authentication_enabled, as: :signin_enabled
769
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
770 771

    class Release < Grape::Entity
772 773
      expose :tag, as: :tag_name
      expose :description
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
774
    end
775 776

    class RepoTag < Grape::Entity
777
      expose :name, :message
778

779
      expose :commit, using: Entities::RepoCommit do |repo_tag, options|
780
        options[:project].repository.commit(repo_tag.dereferenced_target)
781 782
      end

783 784
      expose :release, using: Entities::Release do |repo_tag, options|
        options[:project].releases.find_by(tag: repo_tag.name)
785 786
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
787

788
    class Runner < Grape::Entity
789 790 791 792 793 794 795
      expose :id
      expose :description
      expose :active
      expose :is_shared
      expose :name
    end

796 797
    class RunnerDetails < Runner
      expose :tag_list
798
      expose :run_untagged
799
      expose :locked
Shinya Maeda's avatar
Shinya Maeda committed
800
      expose :access_level
801
      expose :version, :revision, :platform, :architecture
802
      expose :contacted_at
803
      expose :token, if: lambda { |runner, options| options[:current_user].admin? || !runner.is_shared? }
804
      expose :projects, with: Entities::BasicProjectDetails do |runner, options|
805
        if options[:current_user].admin?
806 807
          runner.projects
        else
808
          options[:current_user].authorized_projects.where(id: runner.projects)
809 810
        end
      end
811 812
    end

813 814 815 816
    class RunnerRegistrationDetails < Grape::Entity
      expose :id, :token
    end

817
    class JobArtifactFile < Grape::Entity
818 819 820
      expose :filename, :size
    end

821 822 823 824
    class PipelineBasic < Grape::Entity
      expose :id, :sha, :ref, :status
    end

825
    class Job < Grape::Entity
826
      expose :id, :status, :stage, :name, :ref, :tag, :coverage
827
      expose :created_at, :started_at, :finished_at
Tomasz Maczukin's avatar
Tomasz Maczukin committed
828
      expose :user, with: User
Z.J. van de Weg's avatar
Z.J. van de Weg committed
829
      expose :artifacts_file, using: JobArtifactFile, if: -> (job, opts) { job.artifacts? }
830
      expose :commit, with: RepoCommit
831
      expose :runner, with: Runner
832
      expose :pipeline, with: PipelineBasic
833
    end
834

835
    class Trigger < Grape::Entity
836
      expose :id
837 838 839
      expose :token, :description
      expose :created_at, :updated_at, :deleted_at, :last_used
      expose :owner, using: Entities::UserBasic
840
    end
841

842
    class Variable < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
843
      expose :key, :value
Shinya Maeda's avatar
Shinya Maeda committed
844
      expose :protected?, as: :protected, if: -> (entity, _) { entity.respond_to?(:protected?) }
845
    end
846

847 848
    class Pipeline < PipelineBasic
      expose :before_sha, :tag, :yaml_errors
849 850 851 852

      expose :user, with: Entities::UserBasic
      expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
      expose :duration
853
      expose :coverage
854 855
    end

856 857 858
    class PipelineSchedule < Grape::Entity
      expose :id
      expose :description, :ref, :cron, :cron_timezone, :next_run_at, :active
859
      expose :created_at, :updated_at
860 861 862
      expose :owner, using: Entities::UserBasic
    end

Shinya Maeda's avatar
Shinya Maeda committed
863 864
    class PipelineScheduleDetails < PipelineSchedule
      expose :last_pipeline, using: Entities::PipelineBasic
865
      expose :variables, using: Entities::Variable
Shinya Maeda's avatar
Shinya Maeda committed
866 867
    end

868
    class EnvironmentBasic < Grape::Entity
Nick Thomas's avatar
Nick Thomas committed
869
      expose :id, :name, :slug, :external_url
870 871
    end

872
    class Environment < EnvironmentBasic
873
      expose :project, using: Entities::BasicProjectDetails
Z.J. van de Weg's avatar
Z.J. van de Weg committed
874 875 876 877
    end

    class Deployment < Grape::Entity
      expose :id, :iid, :ref, :sha, :created_at
878 879
      expose :user,        using: Entities::UserBasic
      expose :environment, using: Entities::EnvironmentBasic
880
      expose :deployable,  using: Entities::Job
881 882
    end

883
    class RepoLicense < Grape::Entity
884 885
      expose :key, :name, :nickname
      expose :featured, as: :popular
886 887 888
      expose :url, as: :html_url
      expose(:source_url) { |license| license.meta['source'] }
      expose(:description) { |license| license.meta['description'] }
889 890 891
      expose(:conditions) { |license| license.meta['conditions'] }
      expose(:permissions) { |license| license.meta['permissions'] }
      expose(:limitations) { |license| license.meta['limitations'] }
892 893
      expose :content
    end
894

895
    class TemplatesList < Grape::Entity
896 897 898
      expose :name
    end

899
    class Template < Grape::Entity
900 901
      expose :name, :content
    end
902 903 904 905 906

    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
907

908
    class PersonalAccessToken < Grape::Entity
909 910 911 912 913 914 915
      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

916
    class PersonalAccessTokenWithToken < PersonalAccessToken
917 918
      expose :token
    end
919 920 921 922

    class ImpersonationToken < PersonalAccessTokenWithToken
      expose :impersonation
    end
923

924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945
    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

946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
    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
962

963 964 965
      class RunnerInfo < Grape::Entity
        expose :timeout
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
966

967
      class Step < Grape::Entity
Tomasz Maczukin's avatar
Tomasz Maczukin committed
968
        expose :name, :script, :timeout, :when, :allow_failure
969
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
970

971
      class Image < Grape::Entity
972 973 974
        expose :name, :entrypoint
      end

975
      class Service < Image
976
        expose :alias, :command
977
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
978

979 980
      class Artifacts < Grape::Entity
        expose :name, :untracked, :paths, :when, :expire_in
Tomasz Maczukin's avatar
Tomasz Maczukin committed
981 982
      end

983
      class Cache < Grape::Entity
984
        expose :key, :untracked, :paths, :policy
Tomasz Maczukin's avatar
Tomasz Maczukin committed
985 986
      end

987 988 989
      class Credentials < Grape::Entity
        expose :type, :url, :username, :password
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
990

991 992 993 994 995
      class ArtifactFile < Grape::Entity
        expose :filename, :size
      end

      class Dependency < Grape::Entity
996
        expose :id, :name, :token
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
        expose :artifacts_file, using: ArtifactFile, if: ->(job, _) { job.artifacts? }
      end

      class Response < Grape::Entity
        expose :id
        expose :token
        expose :allow_git_fetch

        expose :job_info, using: JobInfo do |model|
          model
        end

        expose :git_info, using: GitInfo do |model|
          model
        end

        expose :runner_info, using: RunnerInfo do |model|
          model
        end

        expose :variables
        expose :steps, using: Step
        expose :image, using: Image
1020
        expose :services, using: Service
1021 1022 1023
        expose :artifacts, using: Artifacts
        expose :cache, using: Cache
        expose :credentials, using: Credentials
1024
        expose :dependencies, using: Dependency
1025
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
1026
    end
1027 1028 1029 1030

    class UserAgentDetail < Grape::Entity
      expose :user_agent
      expose :ip_address
1031
      expose :submitted, as: :akismet_submitted
1032
    end
1033 1034 1035 1036 1037 1038

    class RepositoryStorageHealth < Grape::Entity
      expose :storage_name
      expose :failing_on_hosts
      expose :total_failures
    end
Nihad Abbasov's avatar
Nihad Abbasov committed
1039 1040
  end
end