diff options
Diffstat (limited to 'app/graphql/resolvers/design_management')
8 files changed, 454 insertions, 0 deletions
diff --git a/app/graphql/resolvers/design_management/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/design_at_version_resolver.rb new file mode 100644 index 00000000000..fd9b349f974 --- /dev/null +++ b/app/graphql/resolvers/design_management/design_at_version_resolver.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class DesignAtVersionResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::DesignManagement::DesignAtVersionType, null: false + + authorize :read_design + + argument :id, GraphQL::ID_TYPE, + required: true, + description: 'The Global ID of the design at this version' + + def resolve(id:) + authorized_find!(id: id) + end + + def find_object(id:) + dav = GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion) + return unless consistent?(dav) + + dav + end + + def self.single + self + end + + private + + # If this resolver is mounted on something that has an issue + # (such as design collection for instance), then we should check + # that the DesignAtVersion as found by its ID does in fact belong + # to this issue. + def consistent?(dav) + issue.nil? || (dav&.design&.issue_id == issue.id) + end + + def issue + object&.issue + end + end + end +end diff --git a/app/graphql/resolvers/design_management/design_resolver.rb b/app/graphql/resolvers/design_management/design_resolver.rb new file mode 100644 index 00000000000..05bdbbbe407 --- /dev/null +++ b/app/graphql/resolvers/design_management/design_resolver.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class DesignResolver < BaseResolver + argument :id, GraphQL::ID_TYPE, + required: false, + description: 'Find a design by its ID' + + argument :filename, GraphQL::STRING_TYPE, + required: false, + description: 'Find a design by its filename' + + def resolve(filename: nil, id: nil) + params = parse_args(filename, id) + + build_finder(params).execute.first + end + + def self.single + self + end + + private + + def issue + object.issue + end + + def build_finder(params) + ::DesignManagement::DesignsFinder.new(issue, current_user, params) + end + + def error(msg) + raise ::Gitlab::Graphql::Errors::ArgumentError, msg + end + + def parse_args(filename, id) + provided = [filename, id].map(&:present?) + + if provided.none? + error('one of id or filename must be passed') + elsif provided.all? + error('only one of id or filename may be passed') + elsif filename.present? + { filenames: [filename] } + else + { ids: [parse_gid(id)] } + end + end + + def parse_gid(gid) + GitlabSchema.parse_gid(gid, expected_type: ::DesignManagement::Design).model_id + end + end + end +end diff --git a/app/graphql/resolvers/design_management/designs_resolver.rb b/app/graphql/resolvers/design_management/designs_resolver.rb new file mode 100644 index 00000000000..81f94d5cb30 --- /dev/null +++ b/app/graphql/resolvers/design_management/designs_resolver.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class DesignsResolver < BaseResolver + argument :ids, + [GraphQL::ID_TYPE], + required: false, + description: 'Filters designs by their ID' + argument :filenames, + [GraphQL::STRING_TYPE], + required: false, + description: 'Filters designs by their filename' + argument :at_version, + GraphQL::ID_TYPE, + required: false, + description: 'Filters designs to only those that existed at the version. ' \ + 'If argument is omitted or nil then all designs will reflect the latest version' + + def self.single + ::Resolvers::DesignManagement::DesignResolver + end + + def resolve(ids: nil, filenames: nil, at_version: nil) + ::DesignManagement::DesignsFinder.new( + issue, + current_user, + ids: design_ids(ids), + filenames: filenames, + visible_at_version: version(at_version), + order: :id + ).execute + end + + private + + def version(at_version) + GitlabSchema.object_from_id(at_version)&.sync if at_version + end + + def design_ids(ids) + ids&.map { |id| GlobalID.parse(id).model_id } + end + + def issue + object.issue + end + end + end +end diff --git a/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb new file mode 100644 index 00000000000..03f7908780c --- /dev/null +++ b/app/graphql/resolvers/design_management/version/design_at_version_resolver.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + module Version + # Resolver for a DesignAtVersion object given an implicit version context + class DesignAtVersionResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::DesignManagement::DesignAtVersionType, null: true + + authorize :read_design + + argument :id, GraphQL::ID_TYPE, + required: false, + as: :design_at_version_id, + description: 'The ID of the DesignAtVersion' + argument :design_id, GraphQL::ID_TYPE, + 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) + + 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.object_from_id(id, expected_type: ::DesignManagement::DesignAtVersion) + 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(id, filename) + ids = [parse_design_id(id).model_id] if id + filenames = [filename] if filename + + ::DesignManagement::DesignsFinder + .new(issue, current_user, ids: ids, filenames: filenames, visible_at_version: version) + .execute + end + + def parse_design_id(id) + GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design) + 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 diff --git a/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb b/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb new file mode 100644 index 00000000000..5ccb2f3e311 --- /dev/null +++ b/app/graphql/resolvers/design_management/version/designs_at_version_resolver.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + module Version + # Resolver for DesignAtVersion objects given an implicit version context + class DesignsAtVersionResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::DesignManagement::DesignAtVersionType, null: true + + authorize :read_design + + argument :ids, + [GraphQL::ID_TYPE], + required: false, + description: 'Filters designs by their ID' + argument :filenames, + [GraphQL::STRING_TYPE], + required: false, + description: 'Filters designs by their filename' + + def self.single + ::Resolvers::DesignManagement::Version::DesignAtVersionResolver + end + + def resolve(ids: nil, filenames: nil) + find(ids, filenames).execute.map { |d| make(d) } + end + + private + + def find(ids, filenames) + ids = ids&.map { |id| parse_design_id(id).model_id } + + ::DesignManagement::DesignsFinder.new(issue, current_user, + ids: ids, + filenames: filenames, + visible_at_version: version) + end + + def parse_design_id(id) + GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Design) + 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 diff --git a/app/graphql/resolvers/design_management/version_in_collection_resolver.rb b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb new file mode 100644 index 00000000000..9e729172881 --- /dev/null +++ b/app/graphql/resolvers/design_management/version_in_collection_resolver.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class VersionInCollectionResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::DesignManagement::VersionType, null: true + + authorize :read_design + + alias_method :collection, :object + + argument :sha, GraphQL::STRING_TYPE, + required: false, + description: "The SHA256 of a specific version" + argument :id, GraphQL::ID_TYPE, + required: false, + description: 'The Global ID of the version' + + def resolve(id: nil, sha: nil) + check_args(id, sha) + + gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id + + ::DesignManagement::VersionsFinder + .new(collection, current_user, sha: sha, version_id: gid&.model_id) + .execute + .first + end + + def self.single + self + end + + private + + def check_args(id, sha) + return if id.present? || sha.present? + + raise ::Gitlab::Graphql::Errors::ArgumentError, 'one of id or sha is required' + end + end + end +end diff --git a/app/graphql/resolvers/design_management/version_resolver.rb b/app/graphql/resolvers/design_management/version_resolver.rb new file mode 100644 index 00000000000..b0e0843e6c8 --- /dev/null +++ b/app/graphql/resolvers/design_management/version_resolver.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class VersionResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::DesignManagement::VersionType, null: true + + authorize :read_design + + argument :id, GraphQL::ID_TYPE, + required: true, + description: 'The Global ID of the version' + + def resolve(id:) + authorized_find!(id: id) + end + + def find_object(id:) + GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version) + end + end + end +end diff --git a/app/graphql/resolvers/design_management/versions_resolver.rb b/app/graphql/resolvers/design_management/versions_resolver.rb new file mode 100644 index 00000000000..a62258dad5c --- /dev/null +++ b/app/graphql/resolvers/design_management/versions_resolver.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Resolvers + module DesignManagement + class VersionsResolver < BaseResolver + type Types::DesignManagement::VersionType.connection_type, null: false + + alias_method :design_or_collection, :object + + argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE, + as: :sha, + required: false, + description: 'The SHA256 of the most recent acceptable version' + + argument :earlier_or_equal_to_id, GraphQL::ID_TYPE, + as: :id, + required: false, + description: 'The Global ID of the most recent acceptable version' + + # This resolver has a custom singular resolver + def self.single + ::Resolvers::DesignManagement::VersionInCollectionResolver + end + + def resolve(parent: nil, id: nil, sha: nil) + version = cutoff(parent, id, sha) + + raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, 'cutoff not found' unless version.present? + + if version == :unconstrained + find + else + find(earlier_or_equal_to: version) + end + end + + private + + # Find the most recent version that the client will accept + def cutoff(parent, id, sha) + if sha.present? || id.present? + specific_version(id, sha) + elsif at_version = at_version_arg(parent) + by_id(at_version) + else + :unconstrained + end + end + + def specific_version(id, sha) + gid = GitlabSchema.parse_gid(id, expected_type: ::DesignManagement::Version) if id + find(sha: sha, version_id: gid&.model_id).first + end + + def find(**params) + ::DesignManagement::VersionsFinder + .new(design_or_collection, current_user, params) + .execute + end + + def by_id(id) + GitlabSchema.object_from_id(id, expected_type: ::DesignManagement::Version).sync + end + + # Find an `at_version` argument passed to a parent node. + # + # If one is found, then a design collection further up the AST + # has been filtered to reflect designs at that version, and so + # for consistency we should only present versions up to the given + # version here. + def at_version_arg(parent) + ::Gitlab::Graphql::FindArgumentInParent.find(parent, :at_version, limit_depth: 4) + end + end + end +end |