Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b3db40398c
commit
17b91a3c6a
|
|
@ -1,4 +1,6 @@
|
||||||
import { join as joinPaths } from 'path';
|
const PATH_SEPARATOR = '/';
|
||||||
|
const PATH_SEPARATOR_LEADING_REGEX = new RegExp(`^${PATH_SEPARATOR}+`);
|
||||||
|
const PATH_SEPARATOR_ENDING_REGEX = new RegExp(`${PATH_SEPARATOR}+$`);
|
||||||
|
|
||||||
// Returns a decoded url parameter value
|
// Returns a decoded url parameter value
|
||||||
// - Treats '+' as '%20'
|
// - Treats '+' as '%20'
|
||||||
|
|
@ -6,6 +8,37 @@ function decodeUrlParameter(val) {
|
||||||
return decodeURIComponent(val.replace(/\+/g, '%20'));
|
return decodeURIComponent(val.replace(/\+/g, '%20'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cleanLeadingSeparator(path) {
|
||||||
|
return path.replace(PATH_SEPARATOR_LEADING_REGEX, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanEndingSeparator(path) {
|
||||||
|
return path.replace(PATH_SEPARATOR_ENDING_REGEX, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely joins the given paths which might both start and end with a `/`
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* - `joinPaths('abc/', '/def') === 'abc/def'`
|
||||||
|
* - `joinPaths(null, 'abc/def', 'zoo) === 'abc/def/zoo'`
|
||||||
|
*
|
||||||
|
* @param {...String} paths
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
export function joinPaths(...paths) {
|
||||||
|
return paths.reduce((acc, path) => {
|
||||||
|
if (!path) {
|
||||||
|
return acc;
|
||||||
|
}
|
||||||
|
if (!acc) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [cleanEndingSeparator(acc), PATH_SEPARATOR, cleanLeadingSeparator(path)].join('');
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
|
||||||
// Returns an array containing the value(s) of the
|
// Returns an array containing the value(s) of the
|
||||||
// of the key passed as an argument
|
// of the key passed as an argument
|
||||||
export function getParameterValues(sParam, url = window.location) {
|
export function getParameterValues(sParam, url = window.location) {
|
||||||
|
|
@ -212,5 +245,3 @@ export function objectToQuery(obj) {
|
||||||
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
|
.map(k => `${encodeURIComponent(k)}=${encodeURIComponent(obj[k])}`)
|
||||||
.join('&');
|
.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
export { joinPaths };
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class Projects::JobsController < Projects::ApplicationController
|
||||||
before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize]
|
before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize]
|
||||||
before_action :verify_api_request!, only: :terminal_websocket_authorize
|
before_action :verify_api_request!, only: :terminal_websocket_authorize
|
||||||
before_action only: [:show] do
|
before_action only: [:show] do
|
||||||
push_frontend_feature_flag(:job_log_json, project)
|
push_frontend_feature_flag(:job_log_json, project, default_enabled: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
layout 'project'
|
layout 'project'
|
||||||
|
|
@ -53,7 +53,7 @@ class Projects::JobsController < Projects::ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
# TODO: when the feature flag is removed we should not pass
|
# TODO: when the feature flag is removed we should not pass
|
||||||
# content_format to serialize method.
|
# content_format to serialize method.
|
||||||
content_format = Feature.enabled?(:job_log_json, @project) ? :json : :html
|
content_format = Feature.enabled?(:job_log_json, @project, default_enabled: true) ? :json : :html
|
||||||
|
|
||||||
build_trace = Ci::BuildTrace.new(
|
build_trace = Ci::BuildTrace.new(
|
||||||
build: @build,
|
build: @build,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module Snippets
|
||||||
|
class Base < BaseMutation
|
||||||
|
field :snippet,
|
||||||
|
Types::SnippetType,
|
||||||
|
null: true,
|
||||||
|
description: 'The snippet after mutation'
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_object(id:)
|
||||||
|
GitlabSchema.object_from_id(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized_resource?(snippet)
|
||||||
|
Ability.allowed?(context[:current_user], ability_for(snippet), snippet)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ability_for(snippet)
|
||||||
|
"#{ability_name}_#{snippet.to_ability_name}".to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def ability_name
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module Snippets
|
||||||
|
class Create < BaseMutation
|
||||||
|
include Mutations::ResolvesProject
|
||||||
|
|
||||||
|
graphql_name 'CreateSnippet'
|
||||||
|
|
||||||
|
field :snippet,
|
||||||
|
Types::SnippetType,
|
||||||
|
null: true,
|
||||||
|
description: 'The snippet after mutation'
|
||||||
|
|
||||||
|
argument :title, GraphQL::STRING_TYPE,
|
||||||
|
required: true,
|
||||||
|
description: 'Title of the snippet'
|
||||||
|
|
||||||
|
argument :file_name, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'File name of the snippet'
|
||||||
|
|
||||||
|
argument :content, GraphQL::STRING_TYPE,
|
||||||
|
required: true,
|
||||||
|
description: 'Content of the snippet'
|
||||||
|
|
||||||
|
argument :description, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'Description of the snippet'
|
||||||
|
|
||||||
|
argument :visibility_level, Types::VisibilityLevelsEnum,
|
||||||
|
description: 'The visibility level of the snippet',
|
||||||
|
required: true
|
||||||
|
|
||||||
|
argument :project_path, GraphQL::ID_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'The project full path the snippet is associated with'
|
||||||
|
|
||||||
|
def resolve(args)
|
||||||
|
project_path = args.delete(:project_path)
|
||||||
|
|
||||||
|
if project_path.present?
|
||||||
|
project = find_project!(project_path: project_path)
|
||||||
|
elsif !can_create_personal_snippet?
|
||||||
|
raise_resource_not_avaiable_error!
|
||||||
|
end
|
||||||
|
|
||||||
|
snippet = CreateSnippetService.new(project,
|
||||||
|
context[:current_user],
|
||||||
|
args).execute
|
||||||
|
|
||||||
|
{
|
||||||
|
snippet: snippet.valid? ? snippet : nil,
|
||||||
|
errors: errors_on_object(snippet)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_project!(project_path:)
|
||||||
|
authorized_find!(full_path: project_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_object(full_path:)
|
||||||
|
resolve_project(full_path: full_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorized_resource?(project)
|
||||||
|
Ability.allowed?(context[:current_user], :create_project_snippet, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_create_personal_snippet?
|
||||||
|
Ability.allowed?(context[:current_user], :create_personal_snippet)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module Snippets
|
||||||
|
class Destroy < Base
|
||||||
|
graphql_name 'DestroySnippet'
|
||||||
|
|
||||||
|
ERROR_MSG = 'Error deleting the snippet'
|
||||||
|
|
||||||
|
argument :id,
|
||||||
|
GraphQL::ID_TYPE,
|
||||||
|
required: true,
|
||||||
|
description: 'The global id of the snippet to destroy'
|
||||||
|
|
||||||
|
def resolve(id:)
|
||||||
|
snippet = authorized_find!(id: id)
|
||||||
|
|
||||||
|
result = snippet.destroy
|
||||||
|
errors = result ? [] : [ERROR_MSG]
|
||||||
|
|
||||||
|
{
|
||||||
|
errors: errors
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ability_name
|
||||||
|
"admin"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Mutations
|
||||||
|
module Snippets
|
||||||
|
class Update < Base
|
||||||
|
graphql_name 'UpdateSnippet'
|
||||||
|
|
||||||
|
argument :id,
|
||||||
|
GraphQL::ID_TYPE,
|
||||||
|
required: true,
|
||||||
|
description: 'The global id of the snippet to update'
|
||||||
|
|
||||||
|
argument :title, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'Title of the snippet'
|
||||||
|
|
||||||
|
argument :file_name, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'File name of the snippet'
|
||||||
|
|
||||||
|
argument :content, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'Content of the snippet'
|
||||||
|
|
||||||
|
argument :description, GraphQL::STRING_TYPE,
|
||||||
|
required: false,
|
||||||
|
description: 'Description of the snippet'
|
||||||
|
|
||||||
|
argument :visibility_level, Types::VisibilityLevelsEnum,
|
||||||
|
description: 'The visibility level of the snippet',
|
||||||
|
required: false
|
||||||
|
|
||||||
|
def resolve(args)
|
||||||
|
snippet = authorized_find!(id: args.delete(:id))
|
||||||
|
|
||||||
|
result = UpdateSnippetService.new(snippet.project,
|
||||||
|
context[:current_user],
|
||||||
|
snippet,
|
||||||
|
args).execute
|
||||||
|
|
||||||
|
{
|
||||||
|
snippet: result ? snippet : snippet.reset,
|
||||||
|
errors: errors_on_object(snippet)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ability_name
|
||||||
|
"update"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module Resolvers
|
module Resolvers
|
||||||
class BaseResolver < GraphQL::Schema::Resolver
|
class BaseResolver < GraphQL::Schema::Resolver
|
||||||
|
extend ::Gitlab::Utils::Override
|
||||||
|
|
||||||
def self.single
|
def self.single
|
||||||
@single ||= Class.new(self) do
|
@single ||= Class.new(self) do
|
||||||
def resolve(**args)
|
def resolve(**args)
|
||||||
|
|
@ -36,5 +38,13 @@ module Resolvers
|
||||||
# complexity difference is minimal in this case.
|
# complexity difference is minimal in this case.
|
||||||
[args[:iid], args[:iids]].any? ? 0 : 0.01
|
[args[:iid], args[:iids]].any? ? 0 : 0.01
|
||||||
end
|
end
|
||||||
|
|
||||||
|
override :object
|
||||||
|
def object
|
||||||
|
super.tap do |obj|
|
||||||
|
# If the field this resolver is used in is wrapped in a presenter, go back to it's subject
|
||||||
|
break obj.subject if obj.is_a?(Gitlab::View::Presenter::Base)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ module Types
|
||||||
mount_mutation Mutations::Todos::MarkDone
|
mount_mutation Mutations::Todos::MarkDone
|
||||||
mount_mutation Mutations::Todos::Restore
|
mount_mutation Mutations::Todos::Restore
|
||||||
mount_mutation Mutations::Todos::MarkAllDone
|
mount_mutation Mutations::Todos::MarkAllDone
|
||||||
|
mount_mutation Mutations::Snippets::Destroy
|
||||||
|
mount_mutation Mutations::Snippets::Update
|
||||||
|
mount_mutation Mutations::Snippets::Create
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ module Types
|
||||||
description: 'Description of the snippet',
|
description: 'Description of the snippet',
|
||||||
null: true
|
null: true
|
||||||
|
|
||||||
field :visibility, GraphQL::STRING_TYPE,
|
field :visibility_level, Types::VisibilityLevelsEnum,
|
||||||
description: 'Visibility of the snippet',
|
description: 'Visibility Level of the snippet',
|
||||||
null: false
|
null: false
|
||||||
|
|
||||||
field :created_at, Types::TimeType,
|
field :created_at, Types::TimeType,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Types
|
||||||
|
class VisibilityLevelsEnum < BaseEnum
|
||||||
|
Gitlab::VisibilityLevel.string_options.each do |name, int_value|
|
||||||
|
value name.downcase, value: int_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -30,6 +30,6 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
|
||||||
end
|
end
|
||||||
|
|
||||||
def ability_name(ability_prefix)
|
def ability_name(ability_prefix)
|
||||||
"#{ability_prefix}_#{snippet.class.underscore}".to_sym
|
"#{ability_prefix}_#{snippet.to_ability_name}".to_sym
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
%br
|
%br
|
||||||
|
|
||||||
%div
|
%div
|
||||||
%span= _('Each Runner can be in one of the following states:')
|
%span= _('Each Runner can be in one of the following states and/or belong to one of the following types:')
|
||||||
%ul
|
%ul
|
||||||
%li
|
%li
|
||||||
%span.badge.badge-success shared
|
%span.badge.badge-success shared
|
||||||
|
|
@ -120,7 +120,7 @@
|
||||||
.runners-content.content-list
|
.runners-content.content-list
|
||||||
.table-holder
|
.table-holder
|
||||||
.gl-responsive-table-row.table-row-header{ role: 'row' }
|
.gl-responsive-table-row.table-row-header{ role: 'row' }
|
||||||
.table-section.section-10{ role: 'rowheader' }= _('Type')
|
.table-section.section-10{ role: 'rowheader' }= _('Type/State')
|
||||||
.table-section.section-10{ role: 'rowheader' }= _('Runner token')
|
.table-section.section-10{ role: 'rowheader' }= _('Runner token')
|
||||||
.table-section.section-20{ role: 'rowheader' }= _('Description')
|
.table-section.section-20{ role: 'rowheader' }= _('Description')
|
||||||
.table-section.section-10{ role: 'rowheader' }= _('Version')
|
.table-section.section-10{ role: 'rowheader' }= _('Version')
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Fixes wording on runner admin
|
||||||
|
merge_request:
|
||||||
|
author:
|
||||||
|
type: changed
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Added Snippets GraphQL mutations
|
||||||
|
merge_request: 20956
|
||||||
|
author:
|
||||||
|
type: added
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Enable new job log by default
|
||||||
|
merge_request: 21543
|
||||||
|
author:
|
||||||
|
type: added
|
||||||
|
|
@ -442,6 +442,66 @@ type CreateNotePayload {
|
||||||
note: Note
|
note: Note
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of CreateSnippet
|
||||||
|
"""
|
||||||
|
input CreateSnippetInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Content of the snippet
|
||||||
|
"""
|
||||||
|
content: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Description of the snippet
|
||||||
|
"""
|
||||||
|
description: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
File name of the snippet
|
||||||
|
"""
|
||||||
|
fileName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
The project full path the snippet is associated with
|
||||||
|
"""
|
||||||
|
projectPath: ID
|
||||||
|
|
||||||
|
"""
|
||||||
|
Title of the snippet
|
||||||
|
"""
|
||||||
|
title: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The visibility level of the snippet
|
||||||
|
"""
|
||||||
|
visibilityLevel: VisibilityLevelsEnum!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of CreateSnippet
|
||||||
|
"""
|
||||||
|
type CreateSnippetPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reasons why the mutation failed.
|
||||||
|
"""
|
||||||
|
errors: [String!]!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The snippet after mutation
|
||||||
|
"""
|
||||||
|
snippet: Snippet
|
||||||
|
}
|
||||||
|
|
||||||
type Design implements Noteable {
|
type Design implements Noteable {
|
||||||
diffRefs: DiffRefs!
|
diffRefs: DiffRefs!
|
||||||
|
|
||||||
|
|
@ -861,6 +921,41 @@ type DestroyNotePayload {
|
||||||
note: Note
|
note: Note
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of DestroySnippet
|
||||||
|
"""
|
||||||
|
input DestroySnippetInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
The global id of the snippet to destroy
|
||||||
|
"""
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of DestroySnippet
|
||||||
|
"""
|
||||||
|
type DestroySnippetPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reasons why the mutation failed.
|
||||||
|
"""
|
||||||
|
errors: [String!]!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The snippet after mutation
|
||||||
|
"""
|
||||||
|
snippet: Snippet
|
||||||
|
}
|
||||||
|
|
||||||
type DetailedStatus {
|
type DetailedStatus {
|
||||||
detailsPath: String!
|
detailsPath: String!
|
||||||
favicon: String!
|
favicon: String!
|
||||||
|
|
@ -3737,9 +3832,11 @@ type Mutation {
|
||||||
createEpic(input: CreateEpicInput!): CreateEpicPayload
|
createEpic(input: CreateEpicInput!): CreateEpicPayload
|
||||||
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
|
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
|
||||||
createNote(input: CreateNoteInput!): CreateNotePayload
|
createNote(input: CreateNoteInput!): CreateNotePayload
|
||||||
|
createSnippet(input: CreateSnippetInput!): CreateSnippetPayload
|
||||||
designManagementDelete(input: DesignManagementDeleteInput!): DesignManagementDeletePayload
|
designManagementDelete(input: DesignManagementDeleteInput!): DesignManagementDeletePayload
|
||||||
designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload
|
designManagementUpload(input: DesignManagementUploadInput!): DesignManagementUploadPayload
|
||||||
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
|
destroyNote(input: DestroyNoteInput!): DestroyNotePayload
|
||||||
|
destroySnippet(input: DestroySnippetInput!): DestroySnippetPayload
|
||||||
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
|
epicSetSubscription(input: EpicSetSubscriptionInput!): EpicSetSubscriptionPayload
|
||||||
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
|
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
|
||||||
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
|
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
|
||||||
|
|
@ -3757,6 +3854,7 @@ type Mutation {
|
||||||
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload
|
toggleAwardEmoji(input: ToggleAwardEmojiInput!): ToggleAwardEmojiPayload
|
||||||
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
|
updateEpic(input: UpdateEpicInput!): UpdateEpicPayload
|
||||||
updateNote(input: UpdateNoteInput!): UpdateNotePayload
|
updateNote(input: UpdateNoteInput!): UpdateNotePayload
|
||||||
|
updateSnippet(input: UpdateSnippetInput!): UpdateSnippetPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -5396,9 +5494,9 @@ type Snippet implements Noteable {
|
||||||
userPermissions: SnippetPermissions!
|
userPermissions: SnippetPermissions!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Visibility of the snippet
|
Visibility Level of the snippet
|
||||||
"""
|
"""
|
||||||
visibility: String!
|
visibilityLevel: VisibilityLevelsEnum!
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Web URL of the snippet
|
Web URL of the snippet
|
||||||
|
|
@ -6120,6 +6218,66 @@ type UpdateNotePayload {
|
||||||
note: Note
|
note: Note
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated input type of UpdateSnippet
|
||||||
|
"""
|
||||||
|
input UpdateSnippetInput {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Content of the snippet
|
||||||
|
"""
|
||||||
|
content: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Description of the snippet
|
||||||
|
"""
|
||||||
|
description: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
File name of the snippet
|
||||||
|
"""
|
||||||
|
fileName: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
The global id of the snippet to update
|
||||||
|
"""
|
||||||
|
id: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Title of the snippet
|
||||||
|
"""
|
||||||
|
title: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
The visibility level of the snippet
|
||||||
|
"""
|
||||||
|
visibilityLevel: VisibilityLevelsEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
Autogenerated return type of UpdateSnippet
|
||||||
|
"""
|
||||||
|
type UpdateSnippetPayload {
|
||||||
|
"""
|
||||||
|
A unique identifier for the client performing the mutation.
|
||||||
|
"""
|
||||||
|
clientMutationId: String
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reasons why the mutation failed.
|
||||||
|
"""
|
||||||
|
errors: [String!]!
|
||||||
|
|
||||||
|
"""
|
||||||
|
The snippet after mutation
|
||||||
|
"""
|
||||||
|
snippet: Snippet
|
||||||
|
}
|
||||||
|
|
||||||
scalar Upload
|
scalar Upload
|
||||||
|
|
||||||
type User {
|
type User {
|
||||||
|
|
@ -6286,6 +6444,12 @@ type UserPermissions {
|
||||||
createSnippet: Boolean!
|
createSnippet: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum VisibilityLevelsEnum {
|
||||||
|
internal
|
||||||
|
private
|
||||||
|
public
|
||||||
|
}
|
||||||
|
|
||||||
enum VisibilityScopesEnum {
|
enum VisibilityScopesEnum {
|
||||||
internal
|
internal
|
||||||
private
|
private
|
||||||
|
|
|
||||||
|
|
@ -6442,8 +6442,8 @@
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "visibility",
|
"name": "visibilityLevel",
|
||||||
"description": "Visibility of the snippet",
|
"description": "Visibility Level of the snippet",
|
||||||
"args": [
|
"args": [
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
@ -6451,8 +6451,8 @@
|
||||||
"kind": "NON_NULL",
|
"kind": "NON_NULL",
|
||||||
"name": null,
|
"name": null,
|
||||||
"ofType": {
|
"ofType": {
|
||||||
"kind": "SCALAR",
|
"kind": "ENUM",
|
||||||
"name": "String",
|
"name": "VisibilityLevelsEnum",
|
||||||
"ofType": null
|
"ofType": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -6828,6 +6828,35 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "VisibilityLevelsEnum",
|
||||||
|
"description": null,
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": [
|
||||||
|
{
|
||||||
|
"name": "private",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "internal",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "public",
|
||||||
|
"description": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "VisibilityScopesEnum",
|
"name": "VisibilityScopesEnum",
|
||||||
|
|
@ -15804,6 +15833,33 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "createSnippet",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "CreateSnippetInput",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "CreateSnippetPayload",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "designManagementDelete",
|
"name": "designManagementDelete",
|
||||||
"description": null,
|
"description": null,
|
||||||
|
|
@ -15885,6 +15941,33 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "destroySnippet",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "DestroySnippetInput",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "DestroySnippetPayload",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "epicSetSubscription",
|
"name": "epicSetSubscription",
|
||||||
"description": null,
|
"description": null,
|
||||||
|
|
@ -16343,6 +16426,33 @@
|
||||||
},
|
},
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "updateSnippet",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "input",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "UpdateSnippetInput",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "UpdateSnippetPayload",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputFields": null,
|
"inputFields": null,
|
||||||
|
|
@ -19093,6 +19203,420 @@
|
||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "DestroySnippetPayload",
|
||||||
|
"description": "Autogenerated return type of DestroySnippet",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "errors",
|
||||||
|
"description": "Reasons why the mutation failed.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "snippet",
|
||||||
|
"description": "The snippet after mutation",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Snippet",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "DestroySnippetInput",
|
||||||
|
"description": "Autogenerated input type of DestroySnippet",
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"description": "The global id of the snippet to destroy",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "UpdateSnippetPayload",
|
||||||
|
"description": "Autogenerated return type of UpdateSnippet",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "errors",
|
||||||
|
"description": "Reasons why the mutation failed.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "snippet",
|
||||||
|
"description": "The snippet after mutation",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Snippet",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "UpdateSnippetInput",
|
||||||
|
"description": "Autogenerated input type of UpdateSnippet",
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"description": "The global id of the snippet to update",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"description": "Title of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fileName",
|
||||||
|
"description": "File name of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content",
|
||||||
|
"description": "Content of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"description": "Description of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "visibilityLevel",
|
||||||
|
"description": "The visibility level of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "VisibilityLevelsEnum",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "CreateSnippetPayload",
|
||||||
|
"description": "Autogenerated return type of CreateSnippet",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "errors",
|
||||||
|
"description": "Reasons why the mutation failed.",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "snippet",
|
||||||
|
"description": "The snippet after mutation",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "Snippet",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "INPUT_OBJECT",
|
||||||
|
"name": "CreateSnippetInput",
|
||||||
|
"description": "Autogenerated input type of CreateSnippet",
|
||||||
|
"fields": null,
|
||||||
|
"inputFields": [
|
||||||
|
{
|
||||||
|
"name": "title",
|
||||||
|
"description": "Title of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fileName",
|
||||||
|
"description": "File name of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "content",
|
||||||
|
"description": "Content of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"description": "Description of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "visibilityLevel",
|
||||||
|
"description": "The visibility level of the snippet",
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "VisibilityLevelsEnum",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "projectPath",
|
||||||
|
"description": "The project full path the snippet is associated with",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "ID",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clientMutationId",
|
||||||
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
"type": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interfaces": null,
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "DesignManagementUploadPayload",
|
"name": "DesignManagementUploadPayload",
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
||||||
| `errors` | String! => Array | Reasons why the mutation failed. |
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
| `note` | Note | The note after mutation |
|
| `note` | Note | The note after mutation |
|
||||||
|
|
||||||
|
### CreateSnippetPayload
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | ---- | ---------- |
|
||||||
|
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||||
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
|
| `snippet` | Snippet | The snippet after mutation |
|
||||||
|
|
||||||
### Design
|
### Design
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|
@ -145,6 +153,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
||||||
| `errors` | String! => Array | Reasons why the mutation failed. |
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
| `note` | Note | The note after mutation |
|
| `note` | Note | The note after mutation |
|
||||||
|
|
||||||
|
### DestroySnippetPayload
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | ---- | ---------- |
|
||||||
|
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||||
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
|
| `snippet` | Snippet | The snippet after mutation |
|
||||||
|
|
||||||
### DetailedStatus
|
### DetailedStatus
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|
@ -802,7 +818,7 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
||||||
| `fileName` | String | File Name of the snippet |
|
| `fileName` | String | File Name of the snippet |
|
||||||
| `content` | String! | Content of the snippet |
|
| `content` | String! | Content of the snippet |
|
||||||
| `description` | String | Description of the snippet |
|
| `description` | String | Description of the snippet |
|
||||||
| `visibility` | String! | Visibility of the snippet |
|
| `visibilityLevel` | VisibilityLevelsEnum! | Visibility Level of the snippet |
|
||||||
| `createdAt` | Time! | Timestamp this snippet was created |
|
| `createdAt` | Time! | Timestamp this snippet was created |
|
||||||
| `updatedAt` | Time! | Timestamp this snippet was updated |
|
| `updatedAt` | Time! | Timestamp this snippet was updated |
|
||||||
| `webUrl` | String! | Web URL of the snippet |
|
| `webUrl` | String! | Web URL of the snippet |
|
||||||
|
|
@ -929,6 +945,14 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
|
||||||
| `errors` | String! => Array | Reasons why the mutation failed. |
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
| `note` | Note | The note after mutation |
|
| `note` | Note | The note after mutation |
|
||||||
|
|
||||||
|
### UpdateSnippetPayload
|
||||||
|
|
||||||
|
| Name | Type | Description |
|
||||||
|
| --- | ---- | ---------- |
|
||||||
|
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||||
|
| `errors` | String! => Array | Reasons why the mutation failed. |
|
||||||
|
| `snippet` | Snippet | The snippet after mutation |
|
||||||
|
|
||||||
### User
|
### User
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 272 KiB |
|
|
@ -149,12 +149,13 @@ The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/14664) in GitLab
|
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/14664) in GitLab
|
||||||
> 12.0.
|
> 12.0.
|
||||||
|
|
||||||
Job logs are divided into sections that can be collapsed or expanded.
|
Job logs are divided into sections that can be collapsed or expanded. Each section will display
|
||||||
|
the duration.
|
||||||
|
|
||||||
In the following example:
|
In the following example:
|
||||||
|
|
||||||
- Two sections are expanded and can be collapsed.
|
- Two sections are collapsed and can be expanded.
|
||||||
- One section is collapsed and can be expanded.
|
- Three sections are expanded and can be collapsed.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 68 KiB |
|
|
@ -76,7 +76,7 @@ To the right of the filters, you should see a **Hide dismissed** toggle button.
|
||||||
NOTE: **Note:**
|
NOTE: **Note:**
|
||||||
The dashboard only shows projects with [security reports](#supported-reports) enabled in a group.
|
The dashboard only shows projects with [security reports](#supported-reports) enabled in a group.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Selecting one or more filters will filter the results in this page. Disabling the **Hide dismissed**
|
Selecting one or more filters will filter the results in this page. Disabling the **Hide dismissed**
|
||||||
toggle button will let you also see vulnerabilities that have been dismissed.
|
toggle button will let you also see vulnerabilities that have been dismissed.
|
||||||
|
|
@ -97,6 +97,17 @@ vulnerabilities your projects had at various points in time. You can filter amon
|
||||||
90 days, with the default being 90. Hover over the chart to get more details about
|
90 days, with the default being 90. Hover over the chart to get more details about
|
||||||
the open vulnerabilities at a specific time.
|
the open vulnerabilities at a specific time.
|
||||||
|
|
||||||
|
Below the timeline chart is a list of projects, grouped and sorted by the severity of the vulnerability found:
|
||||||
|
|
||||||
|
- F: 1 or more "critical"
|
||||||
|
- D: 1 or more "high" or "unknown"
|
||||||
|
- C: 1 or more "medium"
|
||||||
|
- B: 1 or more "low"
|
||||||
|
- A: 0 vulnerabilities
|
||||||
|
|
||||||
|
Projects with no vulnerability tests configured will not appear in the list. Additionally, dismissed
|
||||||
|
vulnerabilities are not included either.
|
||||||
|
|
||||||
Read more on how to [interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
|
Read more on how to [interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
|
||||||
|
|
||||||
## Keeping the dashboards up to date
|
## Keeping the dashboards up to date
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ module Gitlab
|
||||||
module AuthorizeResource
|
module AuthorizeResource
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
RESOURCE_ACCESS_ERROR = "The resource that you are attempting to access does not exist or you don't have permission to perform this action"
|
||||||
|
|
||||||
class_methods do
|
class_methods do
|
||||||
def required_permissions
|
def required_permissions
|
||||||
# If the `#authorize` call is used on multiple classes, we add the
|
# If the `#authorize` call is used on multiple classes, we add the
|
||||||
|
|
@ -38,8 +40,7 @@ module Gitlab
|
||||||
|
|
||||||
def authorize!(object)
|
def authorize!(object)
|
||||||
unless authorized_resource?(object)
|
unless authorized_resource?(object)
|
||||||
raise Gitlab::Graphql::Errors::ResourceNotAvailable,
|
raise_resource_not_avaiable_error!
|
||||||
"The resource that you are attempting to access does not exist or you don't have permission to perform this action"
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -61,6 +62,10 @@ module Gitlab
|
||||||
Ability.allowed?(current_user, ability, object, scope: :user)
|
Ability.allowed?(current_user, ability, object, scope: :user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def raise_resource_not_avaiable_error!
|
||||||
|
raise Gitlab::Graphql::Errors::ResourceNotAvailable, RESOURCE_ACCESS_ERROR
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,11 @@ msgid_plural "%d more comments"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "%d project"
|
||||||
|
msgid_plural "%d projects"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
msgid "%d request with warnings"
|
msgid "%d request with warnings"
|
||||||
msgid_plural "%d requests with warnings"
|
msgid_plural "%d requests with warnings"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
|
|
@ -5139,6 +5144,9 @@ msgstr ""
|
||||||
msgid "Creation date"
|
msgid "Creation date"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Critical vulnerabilities present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cron Timezone"
|
msgid "Cron Timezone"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -6225,6 +6233,9 @@ msgstr ""
|
||||||
msgid "Dynamic Application Security Testing (DAST)"
|
msgid "Dynamic Application Security Testing (DAST)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Each Runner can be in one of the following states and/or belong to one of the following types:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Each Runner can be in one of the following states:"
|
msgid "Each Runner can be in one of the following states:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -9205,6 +9216,9 @@ msgstr ""
|
||||||
msgid "Hiding all labels"
|
msgid "Hiding all labels"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "High or unknown vulnerabilities present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0."
|
msgid "Highest number of requests per minute for each raw path, default to 300. To disable throttling set to 0."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -10638,6 +10652,9 @@ msgstr ""
|
||||||
msgid "Logs|To see the pod logs, deploy your code to an environment."
|
msgid "Logs|To see the pod logs, deploy your code to an environment."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Low vulnerabilities present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "MB"
|
msgid "MB"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -10887,6 +10904,9 @@ msgstr ""
|
||||||
msgid "Median"
|
msgid "Median"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Medium vulnerabilities present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Member lock"
|
msgid "Member lock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -11818,6 +11838,9 @@ msgstr ""
|
||||||
msgid "No vulnerabilities found for this project"
|
msgid "No vulnerabilities found for this project"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No vulnerabilities present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "No, directly import the existing email addresses and usernames."
|
msgid "No, directly import the existing email addresses and usernames."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -13575,6 +13598,12 @@ msgstr ""
|
||||||
msgid "Project path"
|
msgid "Project path"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Project security status"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Project security status help page"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Project slug"
|
msgid "Project slug"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -13944,6 +13973,9 @@ msgstr ""
|
||||||
msgid "Projects Successfully Retrieved"
|
msgid "Projects Successfully Retrieved"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects are graded based on the highest severity vulnerability present"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Projects shared with %{group_name}"
|
msgid "Projects shared with %{group_name}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -13953,6 +13985,21 @@ msgstr ""
|
||||||
msgid "Projects to index"
|
msgid "Projects to index"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects with critical vulnerabilities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects with high or unknown vulnerabilities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects with low vulnerabilities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects with medium vulnerabilities"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Projects with no vulnerabilities and security scanning enabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Projects with write access"
|
msgid "Projects with write access"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -18870,6 +18917,9 @@ msgstr ""
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Type/State"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "U2F Devices (%{length})"
|
msgid "U2F Devices (%{length})"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -18903,6 +18953,9 @@ msgstr ""
|
||||||
msgid "Unable to connect to server: %{error}"
|
msgid "Unable to connect to server: %{error}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unable to fetch vulnerable projects"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Unable to generate new instance ID"
|
msgid "Unable to generate new instance ID"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -21756,6 +21809,9 @@ msgstr ""
|
||||||
msgid "severity|Medium"
|
msgid "severity|Medium"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "severity|None"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "severity|Undefined"
|
msgid "severity|Undefined"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(ci_enable_live_trace: true)
|
stub_feature_flags(ci_enable_live_trace: true)
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
stub_not_protect_default_branch
|
stub_not_protect_default_branch
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -527,7 +526,6 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
||||||
|
|
||||||
describe 'GET trace.json' do
|
describe 'GET trace.json' do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(job_log_json: true)
|
|
||||||
get_trace
|
get_trace
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -634,6 +632,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
|
||||||
|
|
||||||
describe 'GET legacy trace.json' do
|
describe 'GET legacy trace.json' do
|
||||||
before do
|
before do
|
||||||
|
stub_feature_flags(job_log_json: false)
|
||||||
get_trace
|
get_trace
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ describe 'Project Jobs Permissions' do
|
||||||
let!(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
|
let!(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(job_log_json: true)
|
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
|
|
||||||
project.enable_ci
|
project.enable_ci
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ describe 'User browses a job', :js do
|
||||||
let!(:build) { create(:ci_build, :success, :trace_artifact, :coverage, pipeline: pipeline) }
|
let!(:build) { create(:ci_build, :success, :trace_artifact, :coverage, pipeline: pipeline) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
|
|
||||||
project.add_maintainer(user)
|
project.add_maintainer(user)
|
||||||
project.enable_ci
|
project.enable_ci
|
||||||
|
|
||||||
|
|
@ -24,7 +22,7 @@ describe 'User browses a job', :js do
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
|
|
||||||
expect(page).to have_content("Job ##{build.id}")
|
expect(page).to have_content("Job ##{build.id}")
|
||||||
expect(page).to have_css('.js-build-trace')
|
expect(page).to have_css('.job-log')
|
||||||
|
|
||||||
# scroll to the top of the page first
|
# scroll to the top of the page first
|
||||||
execute_script "window.scrollTo(0,0)"
|
execute_script "window.scrollTo(0,0)"
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
||||||
before do
|
before do
|
||||||
project.add_role(user, user_access_level)
|
project.add_role(user, user_access_level)
|
||||||
sign_in(user)
|
sign_in(user)
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /:project/jobs" do
|
describe "GET /:project/jobs" do
|
||||||
|
|
@ -810,7 +809,7 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
|
||||||
|
|
||||||
it 'renders job log' do
|
it 'renders job log' do
|
||||||
wait_for_all_requests
|
wait_for_all_requests
|
||||||
expect(page).to have_selector('.js-build-trace')
|
expect(page).to have_selector('.job-log')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,6 @@ describe "Internal Project Access" do
|
||||||
|
|
||||||
set(:project) { create(:project, :internal, :repository) }
|
set(:project) { create(:project, :internal, :repository) }
|
||||||
|
|
||||||
before do
|
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Project should be internal" do
|
describe "Project should be internal" do
|
||||||
describe '#internal?' do
|
describe '#internal?' do
|
||||||
subject { project.internal? }
|
subject { project.internal? }
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,6 @@ describe "Private Project Access" do
|
||||||
|
|
||||||
set(:project) { create(:project, :private, :repository, public_builds: false) }
|
set(:project) { create(:project, :private, :repository, public_builds: false) }
|
||||||
|
|
||||||
before do
|
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Project should be private" do
|
describe "Project should be private" do
|
||||||
describe '#private?' do
|
describe '#private?' do
|
||||||
subject { project.private? }
|
subject { project.private? }
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,6 @@ describe "Public Project Access" do
|
||||||
|
|
||||||
set(:project) { create(:project, :public, :repository) }
|
set(:project) { create(:project, :public, :repository) }
|
||||||
|
|
||||||
before do
|
|
||||||
stub_feature_flags(job_log_json: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Project should be public" do
|
describe "Project should be public" do
|
||||||
describe '#public?' do
|
describe '#public?' do
|
||||||
subject { project.public? }
|
subject { project.public? }
|
||||||
|
|
|
||||||
|
|
@ -298,4 +298,28 @@ describe('URL utility', () => {
|
||||||
expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2');
|
expect(urlUtils.objectToQuery(searchQueryObject)).toEqual('one=1&two=2');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('joinPaths', () => {
|
||||||
|
it.each`
|
||||||
|
paths | expected
|
||||||
|
${['foo', 'bar']} | ${'foo/bar'}
|
||||||
|
${['foo/', 'bar']} | ${'foo/bar'}
|
||||||
|
${['foo//', 'bar']} | ${'foo/bar'}
|
||||||
|
${['abc/', '/def']} | ${'abc/def'}
|
||||||
|
${['foo', '/bar']} | ${'foo/bar'}
|
||||||
|
${['foo', '/bar/']} | ${'foo/bar/'}
|
||||||
|
${['foo', '//bar/']} | ${'foo/bar/'}
|
||||||
|
${['foo', '', '/bar']} | ${'foo/bar'}
|
||||||
|
${['foo', '/bar', '']} | ${'foo/bar'}
|
||||||
|
${['/', '', 'foo/bar/ ', '', '/ninja']} | ${'/foo/bar/ /ninja'}
|
||||||
|
${['', '/ninja', '/', ' ', '', 'bar', ' ']} | ${'/ninja/ /bar/ '}
|
||||||
|
${['http://something/bar/', 'foo']} | ${'http://something/bar/foo'}
|
||||||
|
${['foo/bar', null, 'ninja', null]} | ${'foo/bar/ninja'}
|
||||||
|
${[null, 'abc/def', 'zoo']} | ${'abc/def/zoo'}
|
||||||
|
${['', '', '']} | ${''}
|
||||||
|
${['///', '/', '//']} | ${'/'}
|
||||||
|
`('joins paths $paths => $expected', ({ paths, expected }) => {
|
||||||
|
expect(urlUtils.joinPaths(...paths)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,12 @@ describe Resolvers::BaseResolver do
|
||||||
let(:resolver) do
|
let(:resolver) do
|
||||||
Class.new(described_class) do
|
Class.new(described_class) do
|
||||||
def resolve(**args)
|
def resolve(**args)
|
||||||
|
process(object)
|
||||||
|
|
||||||
[args, args]
|
[args, args]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process(obj); end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -69,4 +73,26 @@ describe Resolvers::BaseResolver do
|
||||||
expect(field.to_graphql.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
|
expect(field.to_graphql.complexity.call({}, { sort: 'foo', iids: [1, 2, 3] }, 1)).to eq 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#object' do
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
|
it 'returns object' do
|
||||||
|
expect_next_instance_of(resolver) do |r|
|
||||||
|
expect(r).to receive(:process).with(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
resolve(resolver, obj: user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when object is a presenter' do
|
||||||
|
it 'returns presented object' do
|
||||||
|
expect_next_instance_of(resolver) do |r|
|
||||||
|
expect(r).to receive(:process).with(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
resolve(resolver, obj: UserPresenter.new(user))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ describe GitlabSchema.types['Snippet'] do
|
||||||
it 'has the correct fields' do
|
it 'has the correct fields' do
|
||||||
expected_fields = [:id, :title, :project, :author,
|
expected_fields = [:id, :title, :project, :author,
|
||||||
:file_name, :content, :description,
|
:file_name, :content, :description,
|
||||||
:visibility, :created_at, :updated_at,
|
:visibility_level, :created_at, :updated_at,
|
||||||
:web_url, :raw_url, :notes, :discussions,
|
:web_url, :raw_url, :notes, :discussions,
|
||||||
:user_permissions, :description_html]
|
:user_permissions, :description_html]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'Creating a Snippet' do
|
||||||
|
include GraphqlHelpers
|
||||||
|
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
let(:content) { 'Initial content' }
|
||||||
|
let(:description) { 'Initial description' }
|
||||||
|
let(:title) { 'Initial title' }
|
||||||
|
let(:file_name) { 'Initial file_name' }
|
||||||
|
let(:visibility_level) { 'public' }
|
||||||
|
let(:project_path) { nil }
|
||||||
|
|
||||||
|
let(:mutation) do
|
||||||
|
variables = {
|
||||||
|
content: content,
|
||||||
|
description: description,
|
||||||
|
visibility_level: visibility_level,
|
||||||
|
file_name: file_name,
|
||||||
|
title: title,
|
||||||
|
project_path: project_path
|
||||||
|
}
|
||||||
|
|
||||||
|
graphql_mutation(:create_snippet, variables)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutation_response
|
||||||
|
graphql_mutation_response(:create_snippet)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user does not have permission' do
|
||||||
|
let(:current_user) { nil }
|
||||||
|
|
||||||
|
it_behaves_like 'a mutation that returns top-level errors',
|
||||||
|
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||||
|
|
||||||
|
it 'does not create the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.not_to change { Snippet.count }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is not authorized in the project' do
|
||||||
|
let(:project_path) { project.full_path }
|
||||||
|
|
||||||
|
it 'does not create the snippet when the user is not authorized' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.not_to change { Snippet.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user has permission' do
|
||||||
|
let(:current_user) { user }
|
||||||
|
|
||||||
|
context 'with PersonalSnippet' do
|
||||||
|
it 'creates the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.to change { Snippet.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the created Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response['snippet']['content']).to eq(content)
|
||||||
|
expect(mutation_response['snippet']['title']).to eq(title)
|
||||||
|
expect(mutation_response['snippet']['description']).to eq(description)
|
||||||
|
expect(mutation_response['snippet']['fileName']).to eq(file_name)
|
||||||
|
expect(mutation_response['snippet']['visibilityLevel']).to eq(visibility_level)
|
||||||
|
expect(mutation_response['snippet']['project']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with ProjectSnippet' do
|
||||||
|
let(:project_path) { project.full_path }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_developer(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.to change { Snippet.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the created Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response['snippet']['content']).to eq(content)
|
||||||
|
expect(mutation_response['snippet']['title']).to eq(title)
|
||||||
|
expect(mutation_response['snippet']['description']).to eq(description)
|
||||||
|
expect(mutation_response['snippet']['fileName']).to eq(file_name)
|
||||||
|
expect(mutation_response['snippet']['visibilityLevel']).to eq(visibility_level)
|
||||||
|
expect(mutation_response['snippet']['project']['fullPath']).to eq(project_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the project path is invalid' do
|
||||||
|
let(:project_path) { 'foobar' }
|
||||||
|
|
||||||
|
it 'returns an an error' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the feature is disabled' do
|
||||||
|
it 'returns an an error' do
|
||||||
|
project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED)
|
||||||
|
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are ActiveRecord validation errors' do
|
||||||
|
let(:title) { '' }
|
||||||
|
|
||||||
|
it_behaves_like 'a mutation that returns errors in the response', errors: ["Title can't be blank"]
|
||||||
|
|
||||||
|
it 'does not create the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.not_to change { Snippet.count }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not return Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response['snippet']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'Destroying a Snippet' do
|
||||||
|
include GraphqlHelpers
|
||||||
|
|
||||||
|
let(:current_user) { snippet.author }
|
||||||
|
let(:mutation) do
|
||||||
|
variables = {
|
||||||
|
id: snippet.to_global_id.to_s
|
||||||
|
}
|
||||||
|
|
||||||
|
graphql_mutation(:destroy_snippet, variables)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutation_response
|
||||||
|
graphql_mutation_response(:destroy_snippet)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'graphql delete actions' do
|
||||||
|
context 'when the user does not have permission' do
|
||||||
|
let(:current_user) { create(:user) }
|
||||||
|
|
||||||
|
it_behaves_like 'a mutation that returns top-level errors',
|
||||||
|
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||||
|
|
||||||
|
it 'does not destroy the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.not_to change { Snippet.count }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user has permission' do
|
||||||
|
it 'destroys the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.to change { Snippet.count }.by(-1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response).to have_key('snippet')
|
||||||
|
expect(mutation_response['snippet']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PersonalSnippet' do
|
||||||
|
it_behaves_like 'graphql delete actions' do
|
||||||
|
let_it_be(:snippet) { create(:personal_snippet) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'ProjectSnippet' do
|
||||||
|
let_it_be(:project) { create(:project, :private) }
|
||||||
|
let_it_be(:snippet) { create(:project_snippet, :private, project: project, author: create(:user)) }
|
||||||
|
|
||||||
|
context 'when the author is not a member of the project' do
|
||||||
|
it 'returns an an error' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the author is a member of the project' do
|
||||||
|
before do
|
||||||
|
project.add_developer(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'graphql delete actions'
|
||||||
|
|
||||||
|
context 'when the snippet project feature is disabled' do
|
||||||
|
it 'returns an an error' do
|
||||||
|
project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED)
|
||||||
|
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'Updating a Snippet' do
|
||||||
|
include GraphqlHelpers
|
||||||
|
|
||||||
|
let_it_be(:original_content) { 'Initial content' }
|
||||||
|
let_it_be(:original_description) { 'Initial description' }
|
||||||
|
let_it_be(:original_title) { 'Initial title' }
|
||||||
|
let_it_be(:original_file_name) { 'Initial file_name' }
|
||||||
|
let(:updated_content) { 'Updated content' }
|
||||||
|
let(:updated_description) { 'Updated description' }
|
||||||
|
let(:updated_title) { 'Updated_title' }
|
||||||
|
let(:updated_file_name) { 'Updated file_name' }
|
||||||
|
let(:current_user) { snippet.author }
|
||||||
|
|
||||||
|
let(:mutation) do
|
||||||
|
variables = {
|
||||||
|
id: GitlabSchema.id_from_object(snippet).to_s,
|
||||||
|
content: updated_content,
|
||||||
|
description: updated_description,
|
||||||
|
visibility_level: 'public',
|
||||||
|
file_name: updated_file_name,
|
||||||
|
title: updated_title
|
||||||
|
}
|
||||||
|
|
||||||
|
graphql_mutation(:update_snippet, variables)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mutation_response
|
||||||
|
graphql_mutation_response(:update_snippet)
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'graphql update actions' do
|
||||||
|
context 'when the user does not have permission' do
|
||||||
|
let(:current_user) { create(:user) }
|
||||||
|
|
||||||
|
it_behaves_like 'a mutation that returns top-level errors',
|
||||||
|
errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
|
||||||
|
|
||||||
|
it 'does not update the Snippet' do
|
||||||
|
expect do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
end.not_to change { snippet.reload }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user has permission' do
|
||||||
|
it 'updates the Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(snippet.reload.title).to eq(updated_title)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the updated Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response['snippet']['content']).to eq(updated_content)
|
||||||
|
expect(mutation_response['snippet']['title']).to eq(updated_title)
|
||||||
|
expect(mutation_response['snippet']['description']).to eq(updated_description)
|
||||||
|
expect(mutation_response['snippet']['fileName']).to eq(updated_file_name)
|
||||||
|
expect(mutation_response['snippet']['visibilityLevel']).to eq('public')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there are ActiveRecord validation errors' do
|
||||||
|
let(:updated_title) { '' }
|
||||||
|
|
||||||
|
it_behaves_like 'a mutation that returns errors in the response', errors: ["Title can't be blank"]
|
||||||
|
|
||||||
|
it 'does not update the Snippet' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(snippet.reload.title).to eq(original_title)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the Snippet with its original values' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
|
||||||
|
expect(mutation_response['snippet']['content']).to eq(original_content)
|
||||||
|
expect(mutation_response['snippet']['title']).to eq(original_title)
|
||||||
|
expect(mutation_response['snippet']['description']).to eq(original_description)
|
||||||
|
expect(mutation_response['snippet']['fileName']).to eq(original_file_name)
|
||||||
|
expect(mutation_response['snippet']['visibilityLevel']).to eq('private')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'PersonalSnippet' do
|
||||||
|
it_behaves_like 'graphql update actions' do
|
||||||
|
let_it_be(:snippet) do
|
||||||
|
create(:personal_snippet,
|
||||||
|
:private,
|
||||||
|
file_name: original_file_name,
|
||||||
|
title: original_title,
|
||||||
|
content: original_content,
|
||||||
|
description: original_description)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'ProjectSnippet' do
|
||||||
|
let_it_be(:project) { create(:project, :private) }
|
||||||
|
let_it_be(:snippet) do
|
||||||
|
create(:project_snippet,
|
||||||
|
:private,
|
||||||
|
project: project,
|
||||||
|
author: create(:user),
|
||||||
|
file_name: original_file_name,
|
||||||
|
title: original_title,
|
||||||
|
content: original_content,
|
||||||
|
description: original_description)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the author is not a member of the project' do
|
||||||
|
it 'returns an an error' do
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the author is a member of the project' do
|
||||||
|
before do
|
||||||
|
project.add_developer(current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'graphql update actions'
|
||||||
|
|
||||||
|
context 'when the snippet project feature is disabled' do
|
||||||
|
it 'returns an an error' do
|
||||||
|
project.project_feature.update_attribute(:snippets_access_level, ProjectFeature::DISABLED)
|
||||||
|
|
||||||
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
|
errors = json_response['errors']
|
||||||
|
|
||||||
|
expect(errors.first['message']).to eq(Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue