summaryrefslogtreecommitdiff
path: root/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb
blob: fea74cbfb0b1df254e6872de7d1baeb1657ce744 (plain)
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
# frozen_string_literal: true

module Resolvers
  module DesignManagement
    module Version
      # Resolver for a DesignAtVersion object given an implicit version context
      class DesignAtVersionResolver < BaseResolver
        DesignAtVersionID = ::Types::GlobalIDType[::DesignManagement::DesignAtVersion]
        DesignID = ::Types::GlobalIDType[::DesignManagement::Design]

        include Gitlab::Graphql::Authorize::AuthorizeResource

        type Types::DesignManagement::DesignAtVersionType, null: true

        requires_argument!

        authorize :read_design

        argument :id, DesignAtVersionID,
                 required: false,
                 as: :design_at_version_id,
                 description: 'The ID of the DesignAtVersion.'
        argument :design_id, DesignID,
                 required: false,
                 description: 'The ID of a specific design.'
        argument :filename, GraphQL::STRING_TYPE,
                 required: false,
                 description: 'The filename of a specific design.'

        def self.single
          self
        end

        def resolve(design_id: nil, filename: nil, design_at_version_id: nil)
          validate_arguments(design_id, filename, design_at_version_id)

          # TODO: remove this when the compatibility layer is removed
          # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883
          design_id &&= DesignID.coerce_isolated_input(design_id)
          design_at_version_id &&= DesignAtVersionID.coerce_isolated_input(design_at_version_id)

          return unless Ability.allowed?(current_user, :read_design, issue)
          return specific_design_at_version(design_at_version_id) if design_at_version_id

          find(design_id, filename).map { |d| make(d) }.first
        end

        private

        def validate_arguments(design_id, filename, design_at_version_id)
          args = { filename: filename, id: design_at_version_id, design_id: design_id }
          passed = args.compact.keys

          return if passed.size == 1

          msg = "Exactly one of #{args.keys.join(', ')} expected, got #{passed}"

          raise Gitlab::Graphql::Errors::ArgumentError, msg
        end

        def specific_design_at_version(id)
          dav = GitlabSchema.find_by_gid(id)
          return unless consistent?(dav)

          dav
        end

        # Test that the DAV found by ID actually belongs on this version, and
        # that it is visible at this version.
        def consistent?(dav)
          return false unless dav.present?

          dav.design.issue_id == issue.id &&
            dav.version.id == version.id &&
            dav.design.visible_in?(version)
        end

        def find(gid, filename)
          ids = [gid.model_id] if gid
          filenames = [filename] if filename

          ::DesignManagement::DesignsFinder
            .new(issue, current_user, ids: ids, filenames: filenames, visible_at_version: version)
            .execute
        end

        def issue
          version.issue
        end

        def version
          object
        end

        def make(design)
          ::DesignManagement::DesignAtVersion.new(design: design, version: version)
        end
      end
    end
  end
end