Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-08-13 15:10:26 +00:00
parent d2bb9c16d9
commit 1be06f9dcb
77 changed files with 1987 additions and 395 deletions

View File

@ -137,7 +137,11 @@ export const config = {
}
// we want to concat next page of children work items within Hierarchy widget to the existing ones
if (incomingWidget?.type === WIDGET_TYPE_HIERARCHY && context.variables.endCursor) {
if (
incomingWidget?.type === WIDGET_TYPE_HIERARCHY &&
context.variables.endCursor &&
incomingWidget.children?.nodes
) {
// concatPagination won't work because we were placing new widget here so we have to do this manually
return {
...incomingWidget,

View File

@ -1,12 +1,12 @@
<script>
import { GlNavItem, GlTabs, GlTab } from '@gitlab/ui';
import { GlTabs, GlTab } from '@gitlab/ui';
import { s__ } from '~/locale';
import { settingsTabTitle } from '~/integrations/constants';
import { visitUrl } from '~/lib/utils/url_utility';
export default {
name: 'ExclusionsTabs',
components: {
GlNavItem,
GlTabs,
GlTab,
},
@ -19,21 +19,17 @@ export default {
settingsTabTitle,
projectsTabTitle: s__('Integrations|Exclusions'),
},
methods: {
goToSettings() {
visitUrl(this.editPath);
},
},
};
</script>
<template>
<gl-tabs content-class="gl-p-0">
<template #tabs-start>
<gl-nav-item role="presentation" link-classes="gl-tab-nav-item" :href="editPath">{{
$options.i18n.settingsTabTitle
}}</gl-nav-item>
</template>
<gl-tab active>
<template #title>
{{ $options.i18n.projectsTabTitle }}
</template>
</gl-tab>
<gl-tab :title="$options.i18n.settingsTabTitle" @click="goToSettings" />
<gl-tab :title="$options.i18n.projectsTabTitle" active />
</gl-tabs>
</template>

View File

@ -1,11 +1,11 @@
<script>
import { GlBadge, GlNavItem, GlTabs, GlTab } from '@gitlab/ui';
import { GlBadge, GlTabs, GlTab } from '@gitlab/ui';
import { settingsTabTitle, overridesTabTitle } from '~/integrations/constants';
import { visitUrl } from '~/lib/utils/url_utility';
export default {
components: {
GlBadge,
GlNavItem,
GlTabs,
GlTab,
},
@ -25,16 +25,17 @@ export default {
settingsTabTitle,
overridesTabTitle,
},
methods: {
goToSettings() {
visitUrl(this.editPath);
},
},
};
</script>
<template>
<gl-tabs>
<template #tabs-start>
<gl-nav-item role="presentation" link-classes="gl-tab-nav-item" :href="editPath">{{
$options.i18n.settingsTabTitle
}}</gl-nav-item>
</template>
<gl-tab :title="$options.i18n.settingsTabTitle" @click="goToSettings" />
<gl-tab active>
<template #title>

View File

@ -37,6 +37,7 @@ export const initMembersApp = (el, context, options) => {
canApproveAccessRequests,
namespaceUserLimit,
availableRoles,
reassignmentCsvPath,
...vuexStoreAttributes
} = parseDataAttributes(el);
@ -76,6 +77,7 @@ export const initMembersApp = (el, context, options) => {
namespaceUserLimit,
availableRoles,
context,
reassignmentCsvPath,
group: {
name: groupName,
path: groupPath,

View File

@ -12,7 +12,7 @@ export default {
GlAlert,
},
inject: {
reassignmentCsvDownloadPath: {
reassignmentCsvPath: {
default: '',
},
},
@ -56,7 +56,7 @@ export default {
<ol class="gl-ml-0 gl-mt-5">
<li>
<gl-button
:href="reassignmentCsvDownloadPath"
:href="reassignmentCsvPath"
variant="link"
icon="download"
data-testid="csv-download-button"

View File

@ -12,7 +12,6 @@ import {
} from '~/issues/show/utils';
import { getSortableDefaultOptions, isDragging } from '~/sortable/utils';
import SafeHtml from '~/vue_shared/directives/safe_html';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const FULL_OPACITY = 'gl-opacity-10';
const isCheckbox = (target) => target?.classList.contains('task-list-item-checkbox');
@ -25,7 +24,6 @@ export default {
components: {
GlButton,
},
mixins: [glFeatureFlagMixin()],
props: {
disableTruncation: {
type: Boolean,
@ -74,7 +72,7 @@ export default {
return this.descriptionHtml?.trim() === '';
},
isTruncated() {
return this.truncated && !this.disableTruncation && this.glFeatures.workItemsBeta;
return this.truncated && !this.disableTruncation;
},
},
watch: {

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
module Mutations
module Ml
module Models
class Edit < Base
graphql_name 'MlModelEdit'
include FindsProject
argument :model_id, GraphQL::Types::Int,
required: false,
description: 'Id of the model.'
argument :name, GraphQL::Types::String,
required: true,
description: 'Name of the model.'
argument :description, GraphQL::Types::String,
required: false,
description: 'Description of the model.'
def resolve(project_path:, name:, model_id:, description:)
project = authorized_find!(project_path)
model = ::Ml::FindModelService.new(project, name, model_id).execute
service_response = ::Ml::UpdateModelService.new(model, description).execute
if service_response.success?
{
model: service_response.payload,
errors: []
}
else
{
model: nil,
errors: service_response.errors
}
end
end
end
end
end
end

View File

@ -219,6 +219,7 @@ module Types
mount_mutation Mutations::WorkItems::Subscribe, alpha: { milestone: '16.3' }
mount_mutation Mutations::Admin::AbuseReportLabels::Create, alpha: { milestone: '16.4' }
mount_mutation Mutations::Ml::Models::Create, alpha: { milestone: '16.8' }
mount_mutation Mutations::Ml::Models::Edit, alpha: { milestone: '17.3' }
mount_mutation Mutations::Ml::Models::Destroy, alpha: { milestone: '16.10' }
mount_mutation Mutations::Ml::Models::Delete, alpha: { milestone: '17.0' }
mount_mutation Mutations::Ml::ModelVersions::Create, alpha: { milestone: '17.1' }

View File

@ -29,7 +29,8 @@ module Groups::GroupMembersHelper
group_path: group.full_path,
can_approve_access_requests: true, # true for CE, overridden in EE
placeholder: placeholder_users,
available_roles: available_group_roles(group)
available_roles: available_group_roles(group),
reassignment_csv_path: bulk_reassignment_file_group_group_members_path(group)
}
end
# rubocop:enable Metrics/ParameterLists

View File

@ -29,7 +29,8 @@ module Projects
model_id: model.id,
model_name: model.name,
max_allowed_file_size: max_allowed_file_size(project),
latest_version: model.latest_version&.version
latest_version: model.latest_version&.version,
markdown_preview_path: ::Gitlab::Routing.url_helpers.project_ml_preview_markdown_url(project)
}
to_json(data)

View File

@ -109,6 +109,10 @@ module Import
accepted_status? ? reassign_to_user : placeholder_user
end
def mapped_user_id
accepted_status? ? reassign_to_user_id : placeholder_user_id
end
def accepted_status?
STATUSES.slice(*ACCEPTED_STATUSES).value?(status)
end

View File

@ -34,6 +34,8 @@ module VirtualRegistries
after_validation :reset_credentials, if: -> { persisted? && url_changed? }
before_save :write_credentials
prevent_from_serialization(:username, :password) if respond_to?(:prevent_from_serialization)
def url_for(path)
full_url = File.join(url, path)
Addressable::URI.parse(full_url).to_s

View File

@ -2,12 +2,24 @@
module Ml
class FindModelService
def initialize(project, name)
def initialize(project, name = nil, model_id = nil)
@project = project
@name = name
@model_id = model_id
end
def execute
return find_by_model_id if @model_id
return find_by_project_and_name if @name
nil
end
def find_by_model_id
Ml::Model.by_project_id_and_id(@project.id, @model_id)
end
def find_by_project_and_name
Ml::Model.by_project_id_and_name(@project.id, @name)
end
end

View File

@ -8,9 +8,18 @@ module Ml
end
def execute
@model.update!(description: @description)
return error('Model not found') unless @model
@model
@model.update!(description: @description)
success(@model)
end
def success(model)
ServiceResponse.success(payload: model)
end
def error(reason)
ServiceResponse.error(message: reason)
end
end
end

View File

@ -1,7 +1,7 @@
- page_title _("Groups")
= render_dashboard_ultimate_trial(current_user)
%div{ data: { testid: 'groups-page' } }
= render_dashboard_ultimate_trial(current_user)
- if current_user.groups.exists?
= render 'dashboard/groups_head'
= render 'groups'

View File

@ -1,25 +1,16 @@
- if @related_branches.any?
- if @related_branches.any?
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, header_options: { class: 'gl-new-card-header' } , body_options: { class: 'gl-new-card-body' }) do |c|
- c.with_header do
.gl-new-card-title-wrapper
%h3.gl-new-card-title
= link_to "", "#related-branches", class: "gl-link anchor position-absolute gl-text-decoration-none", "aria-hidden": true
= _('Related branches')
.gl-new-card-count
= sprite_icon('branch', css_class: "gl-mr-2 gl-text-gray-500 gl-icon")
= @related_branches.size
- c.with_body do
.gl-new-card-content
%ul.related-merge-requests.content-list
- @related_branches.each do |branch|
%li.list-item{ class: "gl-py-0! gl-border-0!" }
.item-body.gl-display-flex.gl-align-items-center.gl-px-3.gl-pr-2.-gl-mx-2
.item-contents.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-flex-grow-1.gl-min-h-7
.item-title.gl-display-flex.mb-xl-0.gl-min-w-0.gl-align-items-center
- if branch[:pipeline_status].present?
%span.-gl-mt-2.-gl-mb-2.gl-mr-3
= render 'ci/status/icon', status: branch[:pipeline_status]
%span.related-branch-info
%strong
= link_to branch[:name], branch[:link], class: "ref-name"
= render ::Layouts::CrudComponent.new(_('Related branches'),
icon: 'branch',
count: @related_branches.size,
options: { class: 'gl-mt-5' },
body_options: { class: 'gl-p-3' }) do |c|
- c.with_body do
%ul.related-merge-requests.content-list
- @related_branches.each do |branch|
%li{ class: '!gl-p-0 !gl-border-b-0' }
.item-body.gl-p-3
.gl-flex.gl-items-center.gl-gap-3
- if branch[:pipeline_status].present?
%span.-gl-my-2
= render 'ci/status/icon', status: branch[:pipeline_status]
= link_to branch[:name], branch[:link], class: 'ref-name'

View File

@ -1,5 +1,9 @@
- if labels.any?
= render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c|
= render ::Layouts::CrudComponent.new(s_('Labels'),
icon: 'label',
count: labels.size,
options: { class: 'gl-mt-5' },
body_options: { class: '!gl-m-0' }) do |c|
- c.with_body do
%ul.manage-labels-list.gl-px-0.gl-mb-0
- labels.each do |label|

View File

@ -6,4 +6,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/153236
milestone: '17.3'
queued_migration_version: 20240717093514
finalize_after: '2024-08-01'
finalized_by:
finalized_by: 20240812010237

View File

@ -7,4 +7,4 @@ milestone: '17.3'
queued_migration_version: 20240708105034
# Replace with the approximate date you think it's best to ensure the completion of this BBM.
finalize_after: '2024-08-22'
finalized_by: # version of the migration that finalized this BBM
finalized_by: 20240812084505

View File

@ -7,4 +7,4 @@ milestone: '17.2'
queued_migration_version: 20240627122810
# Replace with the approximate date you think it's best to ensure the completion of this BBM.
finalize_after: '2024-07-22'
finalized_by: # version of the migration that finalized this BBM
finalized_by: 20240812062443

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
class RemoveCrmContactsWidgetFromWorkItemTypes < Gitlab::Database::Migration[2.2]
class WorkItemType < MigrationRecord
self.table_name = 'work_item_types'
end
class WidgetDefinition < MigrationRecord
self.table_name = 'work_item_widget_definitions'
end
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
milestone '17.4'
WIDGET_NAME = 'CrmContacts'
WIDGET_ENUM_VALUE = 24
WORK_ITEM_TYPES = %w[
Epic
Task
].freeze
def up
WORK_ITEM_TYPES.each do |type_name|
type = WorkItemType.find_by_name_and_namespace_id(type_name, nil)
next unless type
WidgetDefinition.where(name: WIDGET_NAME, work_item_type_id: type.id).delete_all
end
end
def down
widgets = []
WORK_ITEM_TYPES.each do |type_name|
type = WorkItemType.find_by_name_and_namespace_id(type_name, nil)
unless type
Gitlab::AppLogger.warn("type #{type_name} is missing, not adding widget")
next
end
widgets << {
work_item_type_id: type.id,
name: WIDGET_NAME,
widget_type: WIDGET_ENUM_VALUE
}
end
return if widgets.empty?
WidgetDefinition.upsert_all(
widgets,
unique_by: :index_work_item_widget_definitions_on_default_witype_and_name
)
end
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class FinalizeBackfillPartitionIdCiDailyBuildGroupReportResultAttempt2 < Gitlab::Database::Migration[2.2]
milestone '17.4'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
MIGRATION = 'BackfillPartitionIdCiDailyBuildGroupReportResult'
def up
ensure_batched_background_migration_is_finished(
job_class_name: MIGRATION,
table_name: :ci_daily_build_group_report_results,
column_name: :id,
job_arguments: [],
finalize: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class PartitionedFkToCiPipelinesFromCiDailyBuildGroupReportResults < Gitlab::Database::Migration[2.2]
milestone '17.4'
SOURCE_TABLE_NAME = :ci_daily_build_group_report_results
COLUMN = :last_pipeline_id
PARTITION_COLUMN = :partition_id
FK_NAME = :fk_rails_ee072d13b3_p
def up
validate_foreign_key(
SOURCE_TABLE_NAME, [PARTITION_COLUMN, COLUMN],
name: FK_NAME
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class FinalizeBackfillAutoCanceledByPartitionIdForPipelines < Gitlab::Database::Migration[2.2]
milestone '17.4'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
MIGRATION = 'QueueBackfillAutocancelPartitionIdOnCiPipelines'
TABLE_NAME = :ci_pipelines
COLUMN_NAME = :id
def up
ensure_batched_background_migration_is_finished(
job_class_name: MIGRATION,
table_name: TABLE_NAME,
column_name: COLUMN_NAME,
job_arguments: [],
finalize: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class ValidateFkAutoCanceledByIdForCiPipelines < Gitlab::Database::Migration[2.2]
milestone '17.4'
SOURCE_TABLE_NAME = :ci_pipelines
COLUMN = :auto_canceled_by_id
PARTITION_COLUMN = :auto_canceled_by_partition_id
FK_NAME = :fk_262d4c2d19_p
def up
validate_foreign_key(
SOURCE_TABLE_NAME, [PARTITION_COLUMN, COLUMN],
name: FK_NAME
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class FinalizeBackfillUpstreamPipelinePartitionIdForBuilds < Gitlab::Database::Migration[2.2]
milestone '17.4'
disable_ddl_transaction!
restrict_gitlab_migration gitlab_schema: :gitlab_ci
MIGRATION = 'BackfillUpstreamPipelinePartitionIdOnPCiBuilds'
def up
ensure_batched_background_migration_is_finished(
job_class_name: MIGRATION,
table_name: :p_ci_builds,
column_name: :upstream_pipeline_id,
job_arguments: [],
finalize: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,53 @@
# frozen_string_literal: true
class AddFkUpstreamPipelineIdForPCiBuilds < Gitlab::Database::Migration[2.2]
include Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers
milestone '17.4'
disable_ddl_transaction!
SOURCE_TABLE_NAME = :p_ci_builds
TARGET_TABLE_NAME = :ci_pipelines
COLUMN = :upstream_pipeline_id
PARTITION_COLUMN = :upstream_pipeline_partition_id
TARGET_COLUMN = :id
TARGET_PARTITION_COLUMN = :partition_id
FK_NAME = :fk_87f4cefcda_p
def up
add_concurrent_partitioned_foreign_key(
SOURCE_TABLE_NAME,
TARGET_TABLE_NAME,
column: [PARTITION_COLUMN, COLUMN],
target_column: [TARGET_PARTITION_COLUMN, TARGET_COLUMN],
validate: true,
reverse_lock_order: true,
on_update: :cascade,
on_delete: :cascade,
name: FK_NAME
)
end
def down
with_lock_retries do
remove_foreign_key_if_exists(
SOURCE_TABLE_NAME,
TARGET_TABLE_NAME,
name: FK_NAME,
reverse_lock_order: true
)
end
add_concurrent_partitioned_foreign_key(
SOURCE_TABLE_NAME,
TARGET_TABLE_NAME,
column: [PARTITION_COLUMN, COLUMN],
target_column: [TARGET_PARTITION_COLUMN, TARGET_COLUMN],
validate: false,
reverse_lock_order: true,
on_update: :cascade,
on_delete: :cascade,
name: FK_NAME
)
end
end

View File

@ -0,0 +1 @@
074508a89bf7e84826fe6c94093a8ba8c3022a88be8f583d9043e2208e1a934a

View File

@ -0,0 +1 @@
7eafb011d36e7a20e6ca47951f96beb512997abda4dda0a4de12b80bd406f451

View File

@ -0,0 +1 @@
4fe56834e4bcb4750cab7a95f9baa452761262721250617d15760e3e3d64f19e

View File

@ -0,0 +1 @@
ed4ecaa771a6b6f3a233d08a209d0e6749159c50f91c671c2cd6ec14a4826f39

View File

@ -0,0 +1 @@
10011db577bc89ecc877a22f7088038eeb564ea2d013a36d7522306aa1b04239

View File

@ -0,0 +1 @@
7725aa4f55c8e94d3b89f95b2a8760dc21e2d679e476c08a1117077ae58a88d2

View File

@ -0,0 +1 @@
9a962866caa0a9101bc5e9a992b5f28a7383599108464d009d7bff9ada44fc44

View File

@ -32630,7 +32630,7 @@ ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_262d4c2d19 FOREIGN KEY (auto_canceled_by_id) REFERENCES ci_pipelines(id) ON DELETE SET NULL;
ALTER TABLE ONLY ci_pipelines
ADD CONSTRAINT fk_262d4c2d19_p FOREIGN KEY (auto_canceled_by_partition_id, auto_canceled_by_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL NOT VALID;
ADD CONSTRAINT fk_262d4c2d19_p FOREIGN KEY (auto_canceled_by_partition_id, auto_canceled_by_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE SET NULL;
ALTER TABLE ONLY user_namespace_callouts
ADD CONSTRAINT fk_27a69fd1bd FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
@ -33154,8 +33154,8 @@ ALTER TABLE ONLY packages_package_files
ALTER TABLE p_ci_builds
ADD CONSTRAINT fk_87f4cefcda FOREIGN KEY (upstream_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_builds
ADD CONSTRAINT fk_87f4cefcda_p FOREIGN KEY (upstream_pipeline_partition_id, upstream_pipeline_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
ALTER TABLE p_ci_builds
ADD CONSTRAINT fk_87f4cefcda_p FOREIGN KEY (upstream_pipeline_partition_id, upstream_pipeline_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY approval_group_rules_users
ADD CONSTRAINT fk_888a0df3b7 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@ -35726,7 +35726,7 @@ ALTER TABLE ONLY ci_daily_build_group_report_results
ADD CONSTRAINT fk_rails_ee072d13b3 FOREIGN KEY (last_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_daily_build_group_report_results
ADD CONSTRAINT fk_rails_ee072d13b3_p FOREIGN KEY (partition_id, last_pipeline_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
ADD CONSTRAINT fk_rails_ee072d13b3_p FOREIGN KEY (partition_id, last_pipeline_id) REFERENCES ci_pipelines(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY import_source_users
ADD CONSTRAINT fk_rails_ee30e569be FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;

View File

@ -1033,3 +1033,19 @@ gitlab-ctl restart mailroom
```
::EndTabs
### Incoming emails are rejected by providers with email address limit
Your GitLab instance might not receive incoming emails, because some email providers impose a
64-character limit on the local part of the email address (before the `@`).
All emails from addresses that exceed this limit are rejected emails.
As a workaround, maintain a shorter path:
- Ensure that the local part configured before `%{key}` in `incoming_email_address` is as short as
possible, and not longer than 31 characters.
- Place the designated projects at a higher group hierarchy.
- Rename [groups](../user/group/manage.md#change-a-groups-path) and
[project](../user/project/working_with_projects.md#rename-a-repository) to shorter names.
Track this feature in [issue 460206](https://gitlab.com/gitlab-org/gitlab/-/issues/460206).

View File

@ -7048,6 +7048,32 @@ Input type: `MlModelDestroyInput`
| <a id="mutationmlmodeldestroymessage"></a>`message` | [`String`](#string) | Model deletion result message. |
| <a id="mutationmlmodeldestroymodel"></a>`model` | [`MlModel`](#mlmodel) | Model after mutation. |
### `Mutation.mlModelEdit`
DETAILS:
**Introduced** in GitLab 17.3.
**Status**: Experiment.
Input type: `MlModelEditInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmlmodeleditclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmlmodeleditdescription"></a>`description` | [`String`](#string) | Description of the model. |
| <a id="mutationmlmodeleditmodelid"></a>`modelId` | [`Int`](#int) | Id of the model. |
| <a id="mutationmlmodeleditname"></a>`name` | [`String!`](#string) | Name of the model. |
| <a id="mutationmlmodeleditprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the model to mutate is in. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmlmodeleditclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmlmodelediterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmlmodeleditmodel"></a>`model` | [`MlModel`](#mlmodel) | Model after mutation. |
### `Mutation.mlModelVersionCreate`
DETAILS:

View File

@ -161,7 +161,7 @@ curl --request PATCH \
## Delete a container registry protection rule
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/457518) in GitLab 17.3.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/457518) in GitLab 17.4.
Deletes a container registry protection rule from a project.

View File

@ -273,8 +273,8 @@ this limit. Repository limits apply to both public and private projects.
The [import sources](../project/import/index.md#supported-import-sources) that are available to you by default depend on
which GitLab you use:
- GitLab.com: all available import sources are enabled by default.
- GitLab self-managed: no import sources are enabled by default and must be
- GitLab.com: All available import sources are enabled by default.
- GitLab self-managed: No import sources are enabled by default and must be
[enabled](../../administration/settings/import_and_export_settings.md#configure-allowed-import-sources).
## IP range
@ -286,7 +286,7 @@ from those IPs and allow them.
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
For outgoing connections from CI/CD runners, we are not providing static IP addresses.
All GitLab.com instance runners are deployed into Google Cloud Platform (GCP) in `us-east1`.
Most GitLab.com instance runners are deployed into Google Cloud Platform (GCP) in `us-east1`, except _Linux GPU-enabled_ and _Linux Arm64_, hosted in `us-central1`.
Any IP-based firewall can be configured by looking up
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).

View File

@ -87,13 +87,13 @@ The following features are not found in standard Markdown:
The following features are extended from standard Markdown:
| Standard Markdown | Extended Markdown in GitLab |
|---------------------------------------|---------------------------------------------------------------------------------------|
| [Blockquotes](#blockquotes) | [Multiline blockquotes](#multiline-blockquote) |
| [Code blocks](#code-spans-and-blocks) | [Colored code and syntax highlighting](#syntax-highlighting) |
| [Headings](#headings) | [Linkable heading IDs](#heading-ids-and-links) |
| [Images](#images) | [Embedded videos](#videos) and [audio](#audio) |
| [Links](#links) | [Automatically linking URLs](#url-auto-linking) |
| Standard Markdown | Extended Markdown in GitLab |
|---------------------------------------|-----------------------------|
| [Blockquotes](#blockquotes) | [Multiline blockquotes](#multiline-blockquote) |
| [Code blocks](#code-spans-and-blocks) | [Colored code and syntax highlighting](#syntax-highlighting) |
| [Headings](#headings) | [Linkable heading IDs](#heading-ids-and-links) |
| [Images](#images) | [Embedded videos](#videos) and [audio](#audio) |
| [Links](#links) | [Automatically linking URLs](#url-auto-linking) |
## Markdown and accessibility
@ -1015,13 +1015,13 @@ The number of math blocks is also limited based on render time. If the limit is
GitLab renders the excess math instances as text. Wiki and repository files do not have
these limits.
Math written between dollar signs with backticks (``$`...`$``) or single dollar signs (`$...$`)
Math written between dollar signs with backticks (``` $`...`$ ```) or single dollar signs (`$...$`)
is rendered inline with the text.
Math written between double dollar signs (`$$...$$`) or in a [code block](#code-spans-and-blocks) with
the language declared as `math` is rendered on a separate line:
````markdown
`````markdown
This math is inline: $`a^2+b^2=c^2`$.
This math is on a separate line using a ```` ```math ```` block:
@ -1037,7 +1037,7 @@ This math is on a separate line using a `$$...$$` block:
$$
a^2+b^2=c^2
$$
````
`````
![Example of math in GitLab](img/markdown_math_v17_2.png)
@ -1049,16 +1049,12 @@ When creating tables:
- The first line contains the headers, separated by "pipes" (`|`).
- The second line separates the headers from the cells.
- The cells can contain only empty spaces, hyphens, and
(optionally) colons for horizontal alignment.
- Each cell must contain at least one hyphen, but adding more hyphens to a
cell does not change the cell's rendering.
- The cells can contain only empty spaces, hyphens, and (optionally) colons for horizontal alignment.
- Each cell must contain at least one hyphen, but adding more hyphens to a cell does not change the cell's rendering.
- Any content other than hyphens, whitespace, or colons is not allowed
- The third, and any following lines, contain the cell values.
- You **can't** have cells separated over many lines in the Markdown, they must be kept to single lines,
but they can be very long. You can also include HTML `<br>` tags to force newlines if needed.
- The cell sizes **don't** have to match each other. They are flexible, but must be separated
by pipes (`|`).
- You **can't** have cells separated over many lines in the Markdown, they must be kept to single lines, but they can be very long. You can also include HTML `<br>` tags to force newlines if needed.
- The cell sizes **don't** have to match each other. They are flexible, but must be separated by pipes (`|`).
- You **can** have blank cells.
- Column widths are calculated dynamically based on the content of the cells.
- To use the pipe character (`|`) in the text and not as table delimiter, you must [escape](#escape-characters) it with a backslash (`\|`).

View File

@ -85,10 +85,10 @@ difficult, but several tools exist including:
DETAILS:
**Status:** Experiment
> - [Introduced to migration by using direct transfer](https://gitlab.com/gitlab-org/gitlab/-/issues/443557) in GitLab 17.3 [with a flag](../../../administration/feature_flags.md) named `importer_user_mapping`. Disabled by default. This feature is an [experiment](../../../policy/experiment-beta-support.md).
> - [Introduced to migration by using direct transfer](https://gitlab.com/gitlab-org/gitlab/-/issues/443557) in GitLab 17.4 [with flags](../../../administration/feature_flags.md) named `importer_user_mapping` and `bulk_import_importer_user_mapping`. Disabled by default. This feature is an [experiment](../../../policy/experiment-beta-support.md).
FLAG:
The availability of this feature is controlled by a feature flag.
The availability of this feature is controlled by feature flags.
For more information, see the history.
This feature is available for internal testing only, it is not ready for production use.

View File

@ -263,6 +263,20 @@ it inherits the issue's milestone and labels.
For performance reasons, automatic issue closing is disabled for the very first
push from an existing repository.
#### User responsibility when merging
When you merge a merge request, it's your responsibility to check that it's appropriate for any targeted issues
to close. Users can include issue closing patterns in the merge request description, and also in the body
of a commit message. Closing messages in commit messages are easy to miss. In both cases, the merge request widget
shows information about the issue to close on merge:
![This merge request closes issue #2754.](../merge_requests/img/closing_pattern_v17_4.png)
When you merge a merge request, GitLab checks that you have permission to close the targeted issues.
In public repositories, this check is important, because external users can create both merge requests
and commits that contain closing patterns. When you are the user who merges, it's important
that you are aware of the effects the merge has on both the code and issues in your project.
#### Default closing pattern
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/465391) work item (task, objective, or key result) references in GitLab 17.3.

View File

@ -65,6 +65,10 @@ To do this from the GitLab user interface:
1. Scroll to the merge request reports section.
1. Optional. Select your desired merge options, such as **Delete source branch**,
**Squash commits**, or **Edit commit message**.
1. Review the contents of the merge request widget. If it contains an
[issue closing pattern](../issues/managing_issues.md#closing-issues-automatically), confirm
that the issue should close when this work merges:
![This merge request closes issue #2754.](img/closing_pattern_v17_4.png)
1. Select **Auto-merge**.
Commenting on a merge request after you select **Auto-merge**,

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -173,6 +173,13 @@ a merge request, or:
GitLab adds the merge request to the user's **Assigned merge requests** page.
## Merge a merge request
During the merge request [review process](reviews/index.md), reviewers provide feedback on your merge request.
When a reviewer decides that the contents of your merge request are acceptable, the reviewer can set
[auto-merge](auto_merge.md) on it, even if some merge checks are failing. When a merge request is set to auto-merge,
it merges after all merge checks pass, without more action from you.
## Close a merge request
If you decide to permanently stop work on a merge request, close it rather than

View File

@ -22,11 +22,6 @@ module API
}.freeze
included do
include ::API::Helpers::Authentication
feature_category :virtual_registry
urgency :low
helpers do
def require_non_web_browser!
browser = ::Browser.new(request.user_agent)
@ -79,12 +74,7 @@ module API
end
after_validation do
not_found! unless Feature.enabled?(:virtual_registry_maven, current_user)
require_non_web_browser!
require_dependency_proxy_enabled!
authenticate!
end
end
end

View File

@ -0,0 +1,151 @@
# frozen_string_literal: true
module API
module Concerns
module VirtualRegistries
module Packages
module Maven
module UpstreamEndpoints
extend ActiveSupport::Concern
included do
desc 'List all maven virtual registry upstreams' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success code: 200
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' }
]
tags %w[maven_virtual_registries]
hidden true
end
get do
authorize! :read_virtual_registry, registry
present [upstream].compact, with: Entities::VirtualRegistries::Packages::Maven::Upstream
end
desc 'Add a maven virtual registry upstream' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success code: 201
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' },
{ code: 409, message: 'Conflict' }
]
tags %w[maven_virtual_registries]
hidden true
end
params do
requires :url, type: String, desc: 'The URL of the maven virtual registry upstream', allow_blank: false
optional :username, type: String, desc: 'The username of the maven virtual registry upstream'
optional :password, type: String, desc: 'The password of the maven virtual registry upstream'
all_or_none_of :username, :password
end
post do
authorize! :create_virtual_registry, registry
conflict!(_('Upstream already exists')) if upstream
registry.build_upstream(declared_params.merge(group: group))
registry_upstream.group = group
ApplicationRecord.transaction do
render_validation_error!(upstream) unless upstream.save
render_validation_error!(registry_upstream) unless registry_upstream.save
end
created!
end
route_param :upstream_id, type: Integer, desc: 'The ID of the maven virtual registry upstream' do
desc 'Get a specific maven virtual registry upstream' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success code: 200
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' }
]
tags %w[maven_virtual_registries]
hidden true
end
get do
authorize! :read_virtual_registry, registry
not_found! if upstream&.id != params[:upstream_id]
present upstream, with: Entities::VirtualRegistries::Packages::Maven::Upstream
end
desc 'Update a maven virtual registry upstream' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success code: 200
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' }
]
tags %w[maven_virtual_registries]
hidden true
end
params do
optional :url, type: String, desc: 'The URL of the maven virtual registry upstream',
allow_blank: false
optional :username, type: String, desc: 'The username of the maven virtual registry upstream',
allow_blank: false
optional :password, type: String, desc: 'The password of the maven virtual registry upstream',
allow_blank: false
at_least_one_of :url, :username, :password
end
patch do
authorize! :update_virtual_registry, registry
render_validation_error!(upstream) unless upstream.update(declared_params(include_missing: false))
status :ok
end
desc 'Delete a maven virtual registry upstream' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success code: 204
failure [
{ code: 400, message: 'Bad Request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not found' }
]
tags %w[maven_virtual_registries]
hidden true
end
delete do
authorize! :destroy_virtual_registry, registry
not_found! if upstream&.id != params[:upstream_id]
destroy_conditionally!(upstream)
end
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
module API
module Entities
module VirtualRegistries
module Packages
module Maven
class Upstream < Grape::Entity
expose :id, :group_id, :url, :created_at, :updated_at
end
end
end
end
end
end

View File

@ -78,7 +78,9 @@ module API
desc: 'Optional description for registered model.'
end
patch 'update', urgency: :low do
present ::Ml::UpdateModelService.new(find_model(user_project, params[:name]), params[:description]).execute,
present ::Ml::UpdateModelService.new(
find_model(user_project, params[:name]), params[:description]
).execute.payload,
with: Entities::Ml::Mlflow::RegisteredModel, root: :registered_model
end

View File

@ -4,8 +4,12 @@ module API
module VirtualRegistries
module Packages
class Maven < ::API::Base
include ::API::Helpers::Authentication
include ::API::Concerns::VirtualRegistries::Packages::Endpoint
feature_category :virtual_registry
urgency :low
authenticate_with do |accept|
accept.token_types(:personal_access_token).sent_through(:http_private_token_header)
accept.token_types(:deploy_token).sent_through(:http_deploy_token_header)
@ -21,47 +25,71 @@ module API
helpers do
include ::Gitlab::Utils::StrongMemoize
delegate :group, :upstream, :registry_upstream, to: :registry
def require_dependency_proxy_enabled!
not_found! unless ::Gitlab.config.dependency_proxy.enabled
end
def registry
::VirtualRegistries::Packages::Maven::Registry.find(declared_params[:id])
::VirtualRegistries::Packages::Maven::Registry.find(params[:id])
end
strong_memoize_attr :registry
end
desc 'Download endpoint of the Maven virtual registry.' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success [
{ code: 200 }
]
failure [
{ code: 400, message: 'Bad request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not Found' }
]
tags %w[maven_virtual_registries]
hidden true
end
params do
requires :id,
type: Integer,
desc: 'The ID of the Maven virtual registry'
requires :path,
type: String,
file_path: true,
desc: 'Package path',
documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar' }
end
get 'virtual_registries/packages/maven/:id/*path', format: false do
service_response = ::VirtualRegistries::Packages::Maven::HandleFileRequestService.new(
registry: registry,
current_user: current_user,
params: { path: declared_params[:path] }
).execute
after_validation do
not_found! unless Feature.enabled?(:virtual_registry_maven, current_user)
send_error_response_from!(service_response: service_response) if service_response.error?
send_successful_response_from(service_response: service_response)
require_dependency_proxy_enabled!
authenticate!
end
namespace 'virtual_registries/packages/maven' do
namespace :registries do
route_param :id, type: Integer, desc: 'The ID of the maven virtual registry' do
namespace :upstreams do
include ::API::Concerns::VirtualRegistries::Packages::Maven::UpstreamEndpoints
end
end
end
desc 'Download endpoint of the Maven virtual registry.' do
detail 'This feature was introduced in GitLab 17.3. \
This feature is currently in experiment state. \
This feature behind the `virtual_registry_maven` feature flag.'
success [
{ code: 200 }
]
failure [
{ code: 400, message: 'Bad request' },
{ code: 401, message: 'Unauthorized' },
{ code: 403, message: 'Forbidden' },
{ code: 404, message: 'Not Found' }
]
tags %w[maven_virtual_registries]
hidden true
end
params do
requires :id,
type: Integer,
desc: 'The ID of the Maven virtual registry'
requires :path,
type: String,
file_path: true,
desc: 'Package path',
documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar' }
end
get ':id/*path', format: false do
service_response = ::VirtualRegistries::Packages::Maven::HandleFileRequestService.new(
registry: registry,
current_user: current_user,
params: { path: declared_params[:path] }
).execute
send_error_response_from!(service_response: service_response) if service_response.error?
send_successful_response_from(service_response: service_response)
end
end
end
end

View File

@ -30,6 +30,7 @@ module BulkImports
user_gid: id
public_email: publicEmail
username: username
name: name
}
}
}

View File

@ -10,7 +10,10 @@ module BulkImports
PROJECT_MEMBER_RELATIONS = %i[direct inherited invited_groups shared_into_ancestors].freeze
transformer Common::Transformers::ProhibitedAttributesTransformer
# The transformer is skipped when bulk_import_importer_user_mapping is enabled
transformer Common::Transformers::MemberAttributesTransformer
# The transformer is skipped when bulk_import_importer_user_mapping is disabled
transformer Import::BulkImports::Common::Transformers::SourceUserMemberAttributesTransformer
def extract(context)
graphql_extractor.extract(context)

View File

@ -5,6 +5,8 @@ module BulkImports
module Transformers
class MemberAttributesTransformer
def transform(context, data)
return data if context.importer_user_mapping_enabled?
user = find_user(data&.dig('user', 'public_email'))
access_level = data&.dig('access_level', 'integer_value')

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
module Import
module BulkImports
module Common
module Transformers
class SourceUserMemberAttributesTransformer
def transform(context, data)
return data if !context.importer_user_mapping_enabled? || data.nil?
# Create source_user and placeholder user if they do not exists so
# they can be mapped to contributions in subsequent pipelines
source_user = find_or_create_source_user(context, data)
# For now, members are only created on subsequent imports for users
# that have accepted the reassignment.
# https://gitlab.com/gitlab-org/gitlab/-/issues/477845 will handle
# membership for source_users mapped to placeholder users
return unless source_user.accepted_status?
access_level = data.dig('access_level', 'integer_value')
return unless valid_access_level?(access_level)
{
user_id: source_user.mapped_user_id,
access_level: access_level,
created_at: data['created_at'],
updated_at: data['updated_at'],
expires_at: data['expires_at'],
created_by_id: context.current_user.id
}
end
private
def valid_access_level?(access_level)
Gitlab::Access.options_with_owner.value?(access_level)
end
def find_or_create_source_user(context, data)
gid = data.dig('user', 'user_gid')
source_user_id = GlobalID.parse(gid).model_id
source_name = data.dig('user', 'name')
source_username = data.dig('user', 'username')
context.source_user_mapper.find_or_create_source_user(
source_user_identifier: source_user_id,
source_name: source_name,
source_username: source_username
)
end
end
end
end
end
end

View File

@ -1025,9 +1025,6 @@ msgstr ""
msgid "%{linkStart}Advanced search%{linkEnd} is enabled."
msgstr ""
msgid "%{link_start}Add a start date and due date%{link_end} to view a burndown chart."
msgstr ""
msgid "%{listToShow}, and %{awardsListLength} more"
msgstr ""
@ -3119,6 +3116,9 @@ msgstr ""
msgid "Add Zoom meeting"
msgstr ""
msgid "Add a %{link_start}start date and due date%{link_end} to view a burndown chart."
msgstr ""
msgid "Add a GCP region"
msgstr ""
@ -19725,6 +19725,27 @@ msgstr ""
msgid "DuoCodeReview|I have encountered some issues while I was reviewing. Please try again later."
msgstr ""
msgid "DuoEnterpriseTrial|Congratulations, your free GitLab Duo Enterprise trial is activated and will expire on %{exp_date}. The new license might take a minute to show on the page. To give members access to new GitLab Duo Enterprise features, %{assign_link_start}assign them%{assign_link_end} to GitLab Duo Enterprise seats."
msgstr ""
msgid "DuoEnterpriseTrial|Enhance security and remediate vulnerabilities efficiently"
msgstr ""
msgid "DuoEnterpriseTrial|Gain deeper insights into GitLab Duo usage patterns"
msgstr ""
msgid "DuoEnterpriseTrial|GitLab Duo Enterprise is is your end-to-end AI partner for faster, more secure software development."
msgstr ""
msgid "DuoEnterpriseTrial|GitLab Duo Enterprise is only available for purchase for Ultimate customers."
msgstr ""
msgid "DuoEnterpriseTrial|Maintain control and keep your data safe"
msgstr ""
msgid "DuoEnterpriseTrial|Quickly remedy broken pipelines to deliver products faster"
msgstr ""
msgid "DuoEnterpriseTrial|Start your free Duo Enterprise trial"
msgstr ""
@ -19734,6 +19755,9 @@ msgstr ""
msgid "DuoEnterpriseTrial|Start your free GitLab Duo Enterprise trial on %{group_name}"
msgstr ""
msgid "DuoEnterpriseTrial|Stay on top of regulatory requirements with self-hosted model deployment"
msgstr ""
msgid "DuoEnterpriseTrial|We just need some additional information to activate your trial."
msgstr ""
@ -57232,6 +57256,9 @@ msgstr ""
msgid "Upstream Gitaly has been exhausted. Try again later"
msgstr ""
msgid "Upstream already exists"
msgstr ""
msgid "Upvotes"
msgstr ""

View File

@ -84,41 +84,41 @@
"@snowplow/browser-plugin-timezone": "^3.24.2",
"@snowplow/browser-tracker": "^3.24.2",
"@sourcegraph/code-host-integration": "0.0.95",
"@tiptap/core": "^2.5.9",
"@tiptap/extension-blockquote": "^2.5.9",
"@tiptap/extension-bold": "^2.5.9",
"@tiptap/extension-bubble-menu": "^2.5.9",
"@tiptap/extension-bullet-list": "^2.5.9",
"@tiptap/extension-code": "^2.5.9",
"@tiptap/extension-code-block": "^2.5.9",
"@tiptap/extension-code-block-lowlight": "^2.5.9",
"@tiptap/extension-document": "^2.5.9",
"@tiptap/extension-dropcursor": "^2.5.9",
"@tiptap/extension-gapcursor": "^2.5.9",
"@tiptap/extension-hard-break": "^2.5.9",
"@tiptap/extension-heading": "^2.5.9",
"@tiptap/extension-highlight": "^2.5.9",
"@tiptap/extension-history": "^2.5.9",
"@tiptap/extension-horizontal-rule": "^2.5.9",
"@tiptap/extension-image": "^2.5.9",
"@tiptap/extension-italic": "^2.5.9",
"@tiptap/extension-link": "^2.5.9",
"@tiptap/extension-list-item": "^2.5.9",
"@tiptap/extension-ordered-list": "^2.5.9",
"@tiptap/extension-paragraph": "^2.5.9",
"@tiptap/extension-strike": "^2.5.9",
"@tiptap/extension-subscript": "^2.5.9",
"@tiptap/extension-superscript": "^2.5.9",
"@tiptap/extension-table": "^2.5.9",
"@tiptap/extension-table-cell": "^2.5.9",
"@tiptap/extension-table-header": "^2.5.9",
"@tiptap/extension-table-row": "^2.5.9",
"@tiptap/extension-task-item": "^2.5.9",
"@tiptap/extension-task-list": "^2.5.9",
"@tiptap/extension-text": "^2.5.9",
"@tiptap/pm": "^2.5.9",
"@tiptap/suggestion": "^2.5.9",
"@tiptap/vue-2": "^2.5.9",
"@tiptap/core": "^2.6.0",
"@tiptap/extension-blockquote": "^2.6.0",
"@tiptap/extension-bold": "^2.6.0",
"@tiptap/extension-bubble-menu": "^2.6.0",
"@tiptap/extension-bullet-list": "^2.6.0",
"@tiptap/extension-code": "^2.6.0",
"@tiptap/extension-code-block": "^2.6.0",
"@tiptap/extension-code-block-lowlight": "^2.6.0",
"@tiptap/extension-document": "^2.6.0",
"@tiptap/extension-dropcursor": "^2.6.0",
"@tiptap/extension-gapcursor": "^2.6.0",
"@tiptap/extension-hard-break": "^2.6.0",
"@tiptap/extension-heading": "^2.6.0",
"@tiptap/extension-highlight": "^2.6.0",
"@tiptap/extension-history": "^2.6.0",
"@tiptap/extension-horizontal-rule": "^2.6.0",
"@tiptap/extension-image": "^2.6.0",
"@tiptap/extension-italic": "^2.6.0",
"@tiptap/extension-link": "^2.6.0",
"@tiptap/extension-list-item": "^2.6.0",
"@tiptap/extension-ordered-list": "^2.6.0",
"@tiptap/extension-paragraph": "^2.6.0",
"@tiptap/extension-strike": "^2.6.0",
"@tiptap/extension-subscript": "^2.6.0",
"@tiptap/extension-superscript": "^2.6.0",
"@tiptap/extension-table": "^2.6.0",
"@tiptap/extension-table-cell": "^2.6.0",
"@tiptap/extension-table-header": "^2.6.0",
"@tiptap/extension-table-row": "^2.6.0",
"@tiptap/extension-task-item": "^2.6.0",
"@tiptap/extension-task-list": "^2.6.0",
"@tiptap/extension-text": "^2.6.0",
"@tiptap/pm": "^2.6.0",
"@tiptap/suggestion": "^2.6.0",
"@tiptap/vue-2": "^2.6.0",
"@vue/apollo-components": "^4.0.0-beta.4",
"@vue/apollo-option": "^4.0.0-beta.4",
"apollo-upload-client": "15.0.0",

View File

@ -58,7 +58,8 @@ RSpec.describe 'Work item children', :js, feature_category: :team_planning do
end
end
it 'adds a new child task', :aggregate_failures do
it 'adds a new child task', :aggregate_failures,
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/461666' do
allow(Gitlab::QueryLimiting::Transaction).to receive(:threshold).and_return(108)
within_testid('work-item-links') do

View File

@ -1,14 +1,20 @@
import { GlNavItem, GlTabs, GlTab } from '@gitlab/ui';
import { GlTabs, GlTab } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { visitUrl } from '~/lib/utils/url_utility';
import ExclusionsTabs from '~/integrations/beyond_identity/components/exclusions_tabs.vue';
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(),
}));
describe('ExclusionsTabs component', () => {
let wrapper;
const editPath = 'path/to/edit';
const findTabs = () => wrapper.findComponent(GlTabs);
const findNavItem = () => wrapper.findComponent(GlNavItem);
const findTab = () => wrapper.findComponent(GlTab);
const findAllTabs = () => wrapper.findAllComponents(GlTab);
const createComponent = () =>
shallowMountExtended(ExclusionsTabs, {
@ -28,12 +34,21 @@ describe('ExclusionsTabs component', () => {
});
it('renders a nav item for Settings', () => {
expect(findNavItem().text()).toBe('Settings');
expect(findNavItem().attributes('href')).toBe(editPath);
const tab = findAllTabs().at(0);
expect(tab.attributes('title')).toBe('Settings');
});
it('renders a tab for Exclusions', () => {
expect(findTab().text()).toBe('Exclusions');
const tab = findAllTabs().at(1);
expect(tab.attributes('title')).toBe('Exclusions');
});
it('redirects to editPath when the settings tab is clicked', async () => {
const tab = findAllTabs().at(0);
await tab.vm.$emit('click');
expect(visitUrl).toHaveBeenCalledWith(editPath);
});
});
});

View File

@ -1,16 +1,22 @@
import { mount, shallowMount } from '@vue/test-utils';
import { shallowMount } from '@vue/test-utils';
import { GlBadge, GlTab } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import IntegrationTabs from '~/integrations/overrides/components/integration_tabs.vue';
import { settingsTabTitle, overridesTabTitle } from '~/integrations/constants';
jest.mock('~/lib/utils/url_utility', () => ({
...jest.requireActual('~/lib/utils/url_utility'),
visitUrl: jest.fn(),
}));
describe('IntegrationTabs', () => {
let wrapper;
const editPath = 'mock/edit';
const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
wrapper = mountFn(IntegrationTabs, {
const createComponent = (props = {}) => {
wrapper = shallowMount(IntegrationTabs, {
propsData: props,
provide: {
editPath,
@ -22,28 +28,37 @@ describe('IntegrationTabs', () => {
};
const findGlBadge = () => wrapper.findComponent(GlBadge);
const findGlTab = () => wrapper.findComponent(GlTab);
const findSettingsLink = () => wrapper.find('a');
const findAllTabs = () => wrapper.findAllComponents(GlTab);
describe('template', () => {
it('renders "Settings" tab as a link', () => {
createComponent({ mountFn: mount });
it('renders "Settings" tab', () => {
createComponent();
expect(findSettingsLink().text()).toMatchInterpolatedText(settingsTabTitle);
expect(findSettingsLink().attributes('href')).toBe(editPath);
const tab = findAllTabs().at(0);
expect(tab.exists()).toBe(true);
expect(tab.attributes('title')).toBe(settingsTabTitle);
});
it('redirects to editPath when the settings tab is clicked', async () => {
createComponent();
const tab = findAllTabs().at(0);
await tab.vm.$emit('click');
expect(visitUrl).toHaveBeenCalledWith(editPath);
});
it('renders "Projects using custom settings" tab as active', () => {
const projectOverridesCount = '1';
createComponent({
props: { projectOverridesCount },
});
createComponent({ projectOverridesCount });
expect(findGlTab().exists()).toBe(true);
expect(findGlTab().text()).toMatchInterpolatedText(
`${overridesTabTitle} ${projectOverridesCount}`,
);
const tab = findAllTabs().at(1);
expect(tab.exists()).toBe(true);
expect(tab.text()).toMatchInterpolatedText(`${overridesTabTitle} ${projectOverridesCount}`);
expect(findGlBadge().text()).toBe(projectOverridesCount);
});
@ -51,8 +66,10 @@ describe('IntegrationTabs', () => {
it('renders "Projects using custom settings" tab without count', () => {
createComponent();
expect(findGlTab().exists()).toBe(true);
expect(findGlTab().text()).toMatchInterpolatedText(overridesTabTitle);
const tab = findAllTabs().at(1);
expect(tab.exists()).toBe(true);
expect(tab.text()).toMatchInterpolatedText(overridesTabTitle);
expect(findGlBadge().exists()).toBe(false);
});
});

View File

@ -59,7 +59,7 @@ describe('PlaceholdersTabApp', () => {
apolloProvider: mockApollo,
store,
provide: {
reassignmentCsvDownloadPath: 'foo/bar',
reassignmentCsvPath: 'foo/bar',
group: mockGroup,
...provide,
},

View File

@ -5,7 +5,7 @@ describe('CsvUploadModal', () => {
let wrapper;
const defaultInjectedAttributes = {
reassignmentCsvDownloadPath: 'foo/bar',
reassignmentCsvPath: 'foo/bar',
};
const findDownloadLink = () => wrapper.findByTestId('csv-download-button');
@ -29,8 +29,6 @@ describe('CsvUploadModal', () => {
const downloadLink = findDownloadLink();
expect(downloadLink.exists()).toBe(true);
expect(downloadLink.attributes('href')).toBe(
defaultInjectedAttributes.reassignmentCsvDownloadPath,
);
expect(downloadLink.attributes('href')).toBe(defaultInjectedAttributes.reassignmentCsvPath);
});
});

View File

@ -56,7 +56,6 @@ describe('WorkItemDescriptionRendered', () => {
return true;
},
},
hasWorkItemsBeta: true,
});
expect(wrapper.find('[data-test-id="description-read-more"]').exists()).toBe(true);
@ -75,7 +74,6 @@ describe('WorkItemDescriptionRendered', () => {
return false;
},
},
hasWorkItemsBeta: true,
});
expect(wrapper.find('[data-test-id="description-read-more"]').exists()).toBe(false);

View File

@ -57,7 +57,8 @@ RSpec.describe Projects::Ml::ModelRegistryHelper, feature_category: :mlops do
'mlflowTrackingUrl' => "http://localhost/api/v4/projects/#{project.id}/ml/mlflow/",
'modelId' => model.id,
'modelName' => 'cool_model',
'latestVersion' => model.latest_version.version
'latestVersion' => model.latest_version.version,
"markdownPreviewPath" => "http://localhost/#{project.full_path}/-/ml/preview_markdown"
})
end

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Entities::VirtualRegistries::Packages::Maven::Upstream, feature_category: :virtual_registry do
let(:upstream) { build_stubbed(:virtual_registries_packages_maven_upstream) }
subject { described_class.new(upstream).as_json }
it { is_expected.to include(:id, :group_id, :url, :created_at, :updated_at) }
end

View File

@ -3,8 +3,9 @@
require 'spec_helper'
RSpec.describe BulkImports::Common::Pipelines::MembersPipeline, feature_category: :importers do
let_it_be(:default_organization) { create(:organization, :default) }
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:bulk_import) { create(:bulk_import, :with_configuration, user: user) }
let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
let_it_be(:member_data) do
@ -29,7 +30,7 @@ RSpec.describe BulkImports::Common::Pipelines::MembersPipeline, feature_category
allow(pipeline).to receive(:set_source_objects_counter)
end
def extracted_data(email:, has_next_page: false)
def extracted_data(email: '', id: 1, has_next_page: false)
data = {
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-02T00:00:00Z',
@ -38,7 +39,10 @@ RSpec.describe BulkImports::Common::Pipelines::MembersPipeline, feature_category
'integer_value' => 30
},
'user' => {
'public_email' => email
'user_gid' => "gid://gitlab/User/#{id}",
'public_email' => email,
'name' => 'source_name',
'username' => 'source_username'
}
}
@ -71,6 +75,56 @@ RSpec.describe BulkImports::Common::Pipelines::MembersPipeline, feature_category
{ user_id: member_user2.id, access_level: 30 }
)
end
context 'when importer_user_mapping is enabled' do
let!(:import_source_user) do
create(:import_source_user,
namespace: context.portable.root_ancestor,
source_hostname: bulk_import.configuration.source_hostname,
import_type: Import::SOURCE_DIRECT_TRANSFER,
source_user_identifier: '101'
)
end
let!(:reassigned_import_source_user) do
create(:import_source_user,
:completed,
namespace: context.portable.root_ancestor,
source_hostname: bulk_import.configuration.source_hostname,
import_type: Import::SOURCE_DIRECT_TRANSFER,
source_user_identifier: '102'
)
end
before do
allow(context).to receive(:importer_user_mapping_enabled?).and_return(true)
first_page = extracted_data(id: import_source_user.source_user_identifier, has_next_page: true)
second_page = extracted_data(id: reassigned_import_source_user.source_user_identifier, has_next_page: true)
last_page = extracted_data(id: 103)
allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
allow(extractor).to receive(:extract).and_return(first_page, second_page, last_page)
end
end
it 'finds and creates source users and creates membership for the reassigned users' do
expect { pipeline.run }.to change { portable.members.count }.by(1).and(
change { Import::SourceUser.count }.by(1)
)
expect(Import::SourceUser.last).to have_attributes(
source_user_identifier: '103',
namespace: context.portable.root_ancestor,
source_hostname: bulk_import.configuration.source_hostname,
import_type: Import::SOURCE_DIRECT_TRANSFER.to_s
)
expect(members).to contain_exactly(
{ user_id: reassigned_import_source_user.reassign_to_user.id, access_level: 30 }
)
end
end
end
describe '#load' do

View File

@ -140,6 +140,16 @@ RSpec.describe BulkImports::Common::Transformers::MemberAttributesTransformer, f
end
end
end
context 'when importer_user_mapping is enabled' do
before do
allow(context).to receive(:importer_user_mapping_enabled?).and_return(true)
end
it 'does not transform the data' do
expect(subject.transform(context, { 'id' => 1 })).to eq({ 'id' => 1 })
end
end
end
context 'with a project' do

View File

@ -0,0 +1,159 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Import::BulkImports::Common::Transformers::SourceUserMemberAttributesTransformer,
:with_current_organization, feature_category: :importers do
let_it_be(:default_organization) { create(:organization, :default) }
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import) { create(:bulk_import, :with_configuration, user: user) }
shared_examples 'import source user members attribute transformer' do
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
let_it_be(:import_source_user) do
create(:import_source_user,
namespace: context.portable.root_ancestor,
source_hostname: bulk_import.configuration.source_hostname,
import_type: Import::SOURCE_DIRECT_TRANSFER,
source_user_identifier: '101'
)
end
let_it_be(:reassigned_import_source_user) do
create(:import_source_user, :completed,
namespace: context.portable.root_ancestor,
source_hostname: bulk_import.configuration.source_hostname,
import_type: Import::SOURCE_DIRECT_TRANSFER,
source_user_identifier: '102'
)
end
let(:importer_user_mapping_enabled) { true }
before do
allow(context).to receive(:importer_user_mapping_enabled?).and_return(importer_user_mapping_enabled)
end
context 'when an import source user exists and is mapped to a user' do
let(:data) { member_data(source_user_id: reassigned_import_source_user.source_user_identifier) }
it 'does not create an import source user' do
expect { subject.transform(context, data) }.not_to change { Import::SourceUser.count }
end
it 'returns member hash with the reassigned_to_user_id' do
expect(subject.transform(context, data)).to eq(
access_level: 30,
user_id: reassigned_import_source_user.reassign_to_user_id,
created_by_id: user.id,
created_at: '2020-01-01T00:00:00Z',
updated_at: '2020-01-01T00:00:00Z',
expires_at: nil
)
end
context 'when access level is invalid' do
let(:data) do
member_data(access_level: 999, source_user_id: reassigned_import_source_user.source_user_identifier)
end
it 'ignores record' do
expect(subject.transform(context, data)).to eq(nil)
end
end
context 'when importer_user_mapping is disabled' do
let(:importer_user_mapping_enabled) { false }
it 'does not create an import source user' do
expect { subject.transform(context, data) }.not_to change { Import::SourceUser.count }
end
it 'does not transform the data' do
expect(subject.transform(context, { id: 1 })).to eq({ id: 1 })
end
end
end
context 'when an import source user does not exist' do
let(:data) { member_data(source_user_id: 999) }
it 'creates an import source user' do
expect { subject.transform(context, data) }.to change { Import::SourceUser.count }.by(1)
expect(Import::SourceUser.last).to have_attributes(
source_user_identifier: '999',
source_username: 'source_username',
source_name: 'source_name',
import_type: Import::SOURCE_DIRECT_TRANSFER.to_s
)
end
it 'retuns nil' do
expect(subject.transform(context, data)).to eq(nil)
end
context 'when importer_user_mapping is disabled' do
let(:importer_user_mapping_enabled) { false }
it 'does not create an import source user' do
expect { subject.transform(context, data) }.not_to change { Import::SourceUser.count }
end
it 'does not transform the data' do
expect(subject.transform(context, { id: 1 })).to eq({ id: 1 })
end
end
end
context 'when an import source user exists and is mapped to placeholder user' do
let(:data) { member_data(source_user_id: import_source_user.source_user_identifier) }
it 'does not create an import source user' do
expect { subject.transform(context, data) }.not_to change { Import::SourceUser.count }
end
it 'retuns nil' do
expect(subject.transform(context, data)).to eq(nil)
end
end
context 'when data is nil' do
it 'returns nil' do
expect(subject.transform(context, nil)).to eq(nil)
end
end
end
context 'with a project' do
let_it_be(:project) { create(:project) }
let_it_be(:entity) { create(:bulk_import_entity, :project_entity, bulk_import: bulk_import, project: project) }
include_examples 'import source user members attribute transformer'
end
context 'with a group' do
let_it_be(:group) { create(:group) }
let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import, group: group) }
include_examples 'import source user members attribute transformer'
end
def member_data(source_user_id:, access_level: 30)
{
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-01T00:00:00Z',
'expires_at' => nil,
'access_level' => {
'integer_value' => access_level
},
'user' => {
'user_gid' => "gid://gitlab/User/#{source_user_id}",
'username' => 'source_username',
'name' => 'source_name'
}
}
end
end

View File

@ -0,0 +1,67 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe RemoveCrmContactsWidgetFromWorkItemTypes, :migration, feature_category: :team_planning do
let(:widget_name) { described_class::WIDGET_NAME }
let(:work_item_types) { described_class::WORK_ITEM_TYPES }
let(:migration) { described_class.new }
let(:work_item_definitions) { table(:work_item_widget_definitions) }
let(:work_item_type_count) { work_item_types.size }
let(:find_method_name) { :find_by_name_and_namespace_id }
describe '#up', :migration_with_transaction do
it "removes definitions for widget" do
migration.down
expect { migrate! }.to change { work_item_definitions.count }.by(-work_item_type_count)
end
end
describe '#down', :migration_with_transaction do
before do
migrate!
end
it "creates widget definition in all types" do
work_item_definitions.where(name: widget_name).delete_all
expect { migration.down }.to change { work_item_definitions.count }.by(work_item_type_count)
expect(work_item_definitions.all.pluck(:name)).to include(widget_name)
end
it 'logs a warning if the type is missing' do
type_name = work_item_types.first
allow(described_class::WorkItemType).to receive(find_method_name).and_call_original
allow(described_class::WorkItemType).to receive(find_method_name)
.with(type_name, nil).and_return(nil)
expect(Gitlab::AppLogger).to receive(:warn).with("type #{type_name} is missing, not adding widget")
migration.down
end
context 'when the widget already exists' do
let(:work_item_types_table) { table(:work_item_types) }
before do
work_item_types.each do |type_name|
type = work_item_types_table.find_by_name(type_name)
work_item_definitions.create!(
name: widget_name,
work_item_type_id: type.id,
widget_type: described_class::WIDGET_ENUM_VALUE
)
end
end
it 'upserts the widget definitions and raises no error' do
expect { migration.down }.to not_change {
work_item_definitions.where(name: widget_name).count
}
end
end
end
end

View File

@ -253,6 +253,28 @@ RSpec.describe Import::SourceUser, type: :model, feature_category: :importers do
end
end
describe '#mapped_user_id' do
let_it_be(:source_user) { build(:import_source_user, :with_reassign_to_user) }
subject(:mapped_user_id) { source_user.mapped_user_id }
before do
allow(source_user).to receive(:accepted_status?).and_return(accepted)
end
context 'when accepted' do
let(:accepted) { true }
it { is_expected.to eq(source_user.reassign_to_user_id) }
end
context 'when not accepted' do
let(:accepted) { false }
it { is_expected.to eq(source_user.placeholder_user_id) }
end
end
describe '#reassignable_status?' do
reassignable_statuses = [:pending_reassignment, :rejected]
all_states = described_class.state_machines[:status].states

View File

@ -174,4 +174,10 @@ RSpec.describe VirtualRegistries::Packages::Maven::Upstream, type: :model, featu
it { is_expected.to eq(expected_headers) }
end
end
describe '#as_json' do
subject { upstream.as_json }
it { is_expected.not_to include('username', 'password') }
end
end

View File

@ -0,0 +1,83 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Editing of a machine learning model', feature_category: :mlops do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:current_user) { project.owner }
let_it_be(:guest) { create(:user) }
let(:name) { 'some_name' }
let(:description) { 'A description' }
let(:input) { { project_path: project.full_path, name: name, description: description } }
let(:model) { create(:ml_models, project: project, name: name, description: description) }
let(:new_description) { 'A new description' }
let(:edit_input) { { project_path: project.full_path, name: name, description: new_description, model_id: model.id } }
let(:mutation) { graphql_mutation(:ml_model_edit, edit_input, nil, ['version']) }
let(:mutation_response) { graphql_mutation_response(:ml_model_edit) }
context 'when user is not allowed write changes' do
before do
allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?)
.with(current_user, :write_model_registry, project)
.and_return(false)
end
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when the user is not part of the project' do
it 'returns an error' do
post_graphql_mutation(mutation, current_user: guest)
expect { mutation }.to not_change { ::Ml::Model.count }
expect(mutation_response).to be_nil
end
end
context 'when the user is authenticated' do
context 'when the model does not exist' do
it 'returns an error' do
edit_input[:model_id] = 0
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).not_to be_empty
expect(mutation_response['errors']).to match_array(['Model not found'])
end
end
context 'when the model exists' do
before do
model
end
it 'updates the model description' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).to be_empty
model.reload
expect(model.name).to eq(name)
expect(model.description).to eq(new_description)
end
end
context 'when the model is not part of the project' do
let(:model) { create(:ml_models, name: name, description: description) }
it 'returns an error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).not_to be_empty
expect(mutation_response['errors']).to match_array(['Model not found'])
end
end
end
end

View File

@ -7,11 +7,480 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
include WorkhorseHelpers
include HttpBasicAuthHelpers
let_it_be(:registry) { create(:virtual_registries_packages_maven_registry, :with_upstream) }
let_it_be(:project) { create(:project, namespace: registry.group) }
let_it_be_with_reload(:registry) { create(:virtual_registries_packages_maven_registry) }
let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream, registry: registry) }
let_it_be(:group) { registry.group }
let_it_be(:project) { create(:project, namespace: group) }
let_it_be(:user) { project.creator }
let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
let_it_be(:job) { create(:ci_build, :running, user: user, project: project) }
let_it_be(:deploy_token) do
create(:deploy_token, :group, groups: [group], read_virtual_registry: true)
end
let(:upstream) { registry.upstream }
let_it_be(:headers) { user_basic_auth_header(user, personal_access_token) }
shared_examples 'disabled feature flag' do
before do
stub_feature_flags(virtual_registry_maven: false)
end
it_behaves_like 'returning response status', :not_found
end
shared_examples 'disabled dependency proxy' do
before do
stub_config(dependency_proxy: { enabled: false })
end
it_behaves_like 'returning response status', :not_found
end
shared_examples 'not authenticated user' do
let(:headers) { {} }
it_behaves_like 'returning response status', :unauthorized
end
before do
stub_config(dependency_proxy: { enabled: true })
end
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to contain_exactly(registry.upstream.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled feature flag'
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
context 'with valid registry' do
it_behaves_like 'successful response'
end
context 'with invalid registry' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :bad_request
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'with a non member user' do
let_it_be(:user) { create(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :ok
'INTERNAL' | :ok
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
if params[:status] == :ok
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'POST /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do
let(:registry_id) { registry.id }
let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" }
let(:params) { { url: 'http://example.com' } }
subject(:api_request) { post api(url), headers: headers, params: params }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Upstream.count }.by(1)
.and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(1)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled feature flag'
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
context 'with valid params' do
where(:user_role, :status) do
:owner | :created
:maintainer | :created
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
registry.upstream&.destroy!
group.send(:"add_#{user_role}", user)
end
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with invalid registry' do
where(:registry_id, :status) do
non_existing_record_id | :not_found
'foo' | :bad_request
'' | :not_found
end
with_them do
it_behaves_like 'returning response status', params[:status]
end
end
context 'for params' do
where(:params, :status) do
{ url: 'http://example.com', username: 'test', password: 'test' } | :created
{ url: '', username: 'test', password: 'test' } | :bad_request
{ url: 'http://example.com', username: 'test' } | :bad_request
{} | :bad_request
end
before do
registry.upstream&.destroy!
end
before_all do
group.add_maintainer(user)
end
with_them do
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'with existing upstream' do
before_all do
group.add_maintainer(user)
create(:virtual_registries_packages_maven_upstream, registry: registry)
end
it_behaves_like 'returning response status', :conflict
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
before do
registry.upstream&.destroy!
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :created
:personal_access_token | :basic_auth | :created
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :created
:job_token | :basic_auth | :created
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
if params[:status] == :created
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
end
describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { get api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
api_request
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to eq(registry.upstream.as_json)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled feature flag'
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
context 'with valid params' do
it_behaves_like 'successful response'
end
context 'with a non member user' do
let_it_be(:user) { build_stubbed(:user) }
where(:group_access_level, :status) do
'PUBLIC' | :ok
'INTERNAL' | :ok
'PRIVATE' | :forbidden
end
with_them do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false))
end
if params[:status] == :ok
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for authentication' do
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :ok
:deploy_token | :basic_auth | :ok
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'PATCH /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { patch api(url), params: params, headers: headers }
context 'with valid params' do
let(:params) { { url: 'http://example.com', username: 'test', password: 'test' } }
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled feature flag'
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
where(:user_role, :status) do
:owner | :ok
:maintainer | :ok
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
it_behaves_like 'returning response status', params[:status]
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :ok
:personal_access_token | :basic_auth | :ok
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :ok
:job_token | :basic_auth | :ok
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for params' do
before_all do
group.add_maintainer(user)
end
where(:param_url, :username, :password, :status) do
nil | 'test' | 'test' | :ok
'http://example.com' | nil | 'test' | :ok
'http://example.com' | 'test' | nil | :ok
'' | 'test' | 'test' | :bad_request
'http://example.com' | '' | 'test' | :bad_request
'http://example.com' | 'test' | '' | :bad_request
nil | nil | nil | :bad_request
end
with_them do
let(:params) { { url: param_url, username: username, password: password }.compact }
it_behaves_like 'returning response status', params[:status]
end
end
end
describe 'DELETE /api/v4/virtual_registries/packages/maven/registries/:id/upstreams/:upstream_id' do
let(:url) { "/virtual_registries/packages/maven/registries/#{registry.id}/upstreams/#{upstream.id}" }
subject(:api_request) { delete api(url), headers: headers }
shared_examples 'successful response' do
it 'returns a successful response' do
expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Upstream.count }.by(-1)
.and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(-1)
end
end
it { is_expected.to have_request_urgency(:low) }
it_behaves_like 'disabled feature flag'
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
context 'for different user roles' do
where(:user_role, :status) do
:owner | :no_content
:maintainer | :no_content
:developer | :forbidden
:reporter | :forbidden
:guest | :forbidden
end
with_them do
before do
group.send(:"add_#{user_role}", user)
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
context 'for authentication' do
before_all do
group.add_maintainer(user)
end
where(:token, :sent_as, :status) do
:personal_access_token | :header | :no_content
:personal_access_token | :basic_auth | :no_content
:deploy_token | :header | :forbidden
:deploy_token | :basic_auth | :forbidden
:job_token | :header | :no_content
:job_token | :basic_auth | :no_content
end
with_them do
let(:headers) do
case sent_as
when :header
token_header(token)
when :basic_auth
token_basic_auth(token)
end
end
if params[:status] == :no_content
it_behaves_like 'successful response'
else
it_behaves_like 'returning response status', params[:status]
end
end
end
end
describe 'GET /api/v4/virtual_registries/packages/maven/:id/*path' do
let(:path) { 'com/test/package/1.2.3/package-1.2.3.pom' }
@ -32,7 +501,6 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
.to receive(:new)
.with(registry: registry, current_user: user, params: { path: path })
.and_return(service_double)
stub_config(dependency_proxy: { enabled: true }) # not enabled by default
end
subject(:request) do
@ -152,13 +620,7 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
end
end
context 'with feature flag virtual_registry_maven disabled' do
before do
stub_feature_flags(virtual_registry_maven: false)
end
it_behaves_like 'returning response status', :not_found
end
it_behaves_like 'disabled feature flag'
context 'with a web browser' do
described_class::MAJOR_BROWSERS.each do |browser|
@ -179,19 +641,30 @@ RSpec.describe API::VirtualRegistries::Packages::Maven, feature_category: :virtu
end
end
context 'with the dependency proxy disabled' do
before do
stub_config(dependency_proxy: { enabled: false })
end
it_behaves_like 'disabled dependency proxy'
it_behaves_like 'not authenticated user'
end
end
it_behaves_like 'returning response status', :not_found
end
def token_header(token)
case token
when :personal_access_token
{ 'PRIVATE-TOKEN' => personal_access_token.token }
when :deploy_token
{ 'Deploy-Token' => deploy_token.token }
when :job_token
{ 'Job-Token' => job.token }
end
end
context 'as anonymous' do
let(:headers) { {} }
it_behaves_like 'returning response status', :unauthorized
end
def token_basic_auth(token)
case token
when :personal_access_token
user_basic_auth_header(user, personal_access_token)
when :deploy_token
deploy_token_basic_auth_header(deploy_token)
when :job_token
job_basic_auth_header(job)
end
end
end

View File

@ -5,24 +5,54 @@ require 'spec_helper'
RSpec.describe ::Ml::FindModelService, feature_category: :mlops do
let_it_be(:user) { create(:user) }
let_it_be(:existing_model) { create(:ml_models) }
let(:finder) { described_class.new(project, name) }
let(:finder) { described_class.new(project, name, model_id) }
let(:model_id) { nil }
describe '#execute' do
context 'when model name does not exist in the project' do
let(:name) { 'new_model' }
context 'when model_id is provided' do
let(:project) { existing_model.project }
let(:name) { nil }
let(:model_id) { existing_model.id }
it 'reutrns nil' do
expect(finder.execute).to be nil
it 'returns the model with the given model_id' do
expect(finder.execute).to eq(existing_model)
end
context 'when model_id does not exist' do
let(:model_id) { non_existing_record_id }
it 'returns nil' do
expect(finder.execute).to be_nil
end
end
end
context 'when model with name exists' do
let(:name) { existing_model.name }
context 'when neither model_id nor name is provided' do
let(:project) { existing_model.project }
let(:name) { nil }
it 'returns the existing model' do
expect(finder.execute).to eq(existing_model)
it 'returns nil' do
expect(finder.execute).to be_nil
end
end
context 'when model_id is not provided' do
context 'when model name does not exist in the project' do
let(:name) { 'new_model' }
let(:project) { existing_model.project }
it 'returns nil' do
expect(finder.execute).to be_nil
end
end
context 'when model with name exists' do
let(:name) { existing_model.name }
let(:project) { existing_model.project }
it 'returns the existing model' do
expect(finder.execute).to eq(existing_model)
end
end
end
end

View File

@ -4,23 +4,27 @@ require 'spec_helper'
RSpec.describe ::Ml::UpdateModelService, feature_category: :mlops do
let_it_be(:model) { create(:ml_models) }
let_it_be(:description) { 'updated model description' }
let(:service) { described_class.new(model, description) }
let_it_be(:new_description) { 'updated model description' }
let(:service) { described_class.new(model, new_description) }
describe '#execute' do
subject(:service_result) { service.execute }
context 'when supplied with a non-model object' do
let(:model) { nil }
it 'returns nil' do
expect { service.execute }.to raise_error(NoMethodError)
it 'returns an error' do
expect(service_result).to be_error
end
end
context 'with an existing model' do
it 'description is initially blank' do
expect(model.description).to eq(nil)
end
it 'updates the description' do
updated = service.execute
expect(updated.class).to be(Ml::Model)
expect(updated.description).to eq(description)
expect(service_result.payload.description).to eq(new_description)
end
end
end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
module HttpBasicAuthHelpers
def user_basic_auth_header(user)
access_token = create(:personal_access_token, user: user)
def user_basic_auth_header(user, access_token = nil)
access_token ||= create(:personal_access_token, user: user)
basic_auth_header(user.username, access_token.token)
end
@ -11,6 +11,10 @@ module HttpBasicAuthHelpers
basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token)
end
def deploy_token_basic_auth_header(deploy_token)
basic_auth_header(deploy_token.username, deploy_token.token)
end
def client_basic_auth_header(client)
basic_auth_header(client.uid, client.secret)
end

View File

@ -26,6 +26,6 @@ RSpec.describe 'projects/issues/_related_branches' do
expect(rendered).to have_link(href: 'link-to-feature')
expect(rendered).to have_link(href: 'link-to-other')
expect(rendered).to have_css('[data-testid="ci-icon"]')
expect(rendered).to have_css('.related-branch-info')
expect(rendered).to have_css('.ref-name')
end
end

292
yarn.lock
View File

@ -2619,181 +2619,181 @@
dom-accessibility-api "^0.5.1"
pretty-format "^26.4.2"
"@tiptap/core@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.5.9.tgz#1deb0b7c748e24ec32613263e0af8d55a3b3c2ca"
integrity sha512-PPUR+0tbr+wX2G8RG4FEps4qhbnAPEeXK1FUtirLXSRh8vm+TDgafu3sms7wBc4fAyw9zTO/KNNZ90GBe04guA==
"@tiptap/core@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.6.0.tgz#c222f53043abfa69d8d41a081bb3dfc1181fc841"
integrity sha512-MG2OXwpMVaiacGuipqZ3VXi36gMvtV3i3ncrtXufsMducWvniENMi8fEonj/Q7nkeVi59OcoW8vD6kqdKkh2Gw==
"@tiptap/extension-blockquote@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.5.9.tgz#d873a8496fcf572c69aaac2a7a341e035fdbae22"
integrity sha512-LhGyigmd/v1OjYPeoVK8UvFHbH6ffh175ZuNvseZY4PsBd7kZhrSUiuMG8xYdNX8FxamsxAzr2YpsYnOzu3W7A==
"@tiptap/extension-blockquote@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.6.0.tgz#3f0a80ebd82e1f5d174c24f9794e22ac3184c072"
integrity sha512-/rYTYnn7fk/LBixLv7R1fEee3JDrDo3VHEJ+gBGuyLXcNaYtzWuRfqdeIraNSOuhzHffWQ5dg1oxpVFrVrd6AQ==
"@tiptap/extension-bold@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.5.9.tgz#00c9b7b5211048b1e1c5d67e355935b9c92e3532"
integrity sha512-XUJdzFb31t0+bwiRquJf0btBpqOB3axQNHTKM9XADuL4S+Z6OBPj0I5rYINeElw/Q7muvdWrHWHh/ovNJA1/5A==
"@tiptap/extension-bold@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.6.0.tgz#eea678778fce751d6f771b6a18c15513f1bbd8ff"
integrity sha512-supo1j/lZFXkQ7aQxYTduncSF4aKiDomceJbqQN2HnsRmf1b1SimsGugjhcqT0g10bYb0zuscNPnk15xG8gVTA==
"@tiptap/extension-bubble-menu@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.5.9.tgz#d600bbcaa1d98a99f32b3b8b8c3d35752161200c"
integrity sha512-NddZ8Qn5dgPPa1W4yk0jdhF4tDBh0FwzBpbnDu2Xz/0TUHrA36ugB2CvR5xS1we4zUKckgpVqOqgdelrmqqFVg==
"@tiptap/extension-bubble-menu@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.6.0.tgz#91fb19abd94a4dcbd8816c89285d8b3a0dd6c098"
integrity sha512-d8LiaWVRCa3jc26OOCGTGLtJhbTlylokAJvKK1U0IGoX/jjzwWIypg6FZawYB2Fj0JANdTD100e7xDfLTOLLKg==
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-bullet-list@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.5.9.tgz#2852aba9a1dacbf2c673cda6a4994b1f3c33cd5c"
integrity sha512-hJTv1x4omFgaID4LMRT5tOZb/VKmi8Kc6jsf4JNq4Grxd2sANmr9qpmKtBZvviK+XD5PpTXHvL+1c8C1SQtuHQ==
"@tiptap/extension-bullet-list@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.6.0.tgz#2a0458701c46295ee2e600807312ac0f76c9ecdb"
integrity sha512-zf46le3It6OO/wrLk/FTckLRh+EPEBCLKiff4n0N/YwIpSGj0VNEjzPjN5X8KSSiDfP50hWsLVFU0h8A2c8keQ==
"@tiptap/extension-code-block-lowlight@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.5.9.tgz#ccd6569422d98b11813df3e0dbd09b1dcf957def"
integrity sha512-taIXxXQ/Lka9CegHFHQS+nx6cX9i9Ws63ZFMPbrXLMSJRhXk8+m4UAoGZQJH9CGGb5/Rv0p3Z8I59AGiyUHLEw==
"@tiptap/extension-code-block-lowlight@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.6.0.tgz#680ce93869639fb06faccb689622149ff58d3830"
integrity sha512-d1JFRmR75HjHeARP4B+ay4wUdeSBDe1Q5+hGLXMvNIrB1pGsuOUERrfL4QPxC51vi2EUbtxsUJh+h2V+ecGd+A==
"@tiptap/extension-code-block@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.5.9.tgz#8cd99515b286fc62ad1215a411aea5da9a7d9701"
integrity sha512-+MUwp0VFFv2aFiZ/qN6q10vfIc6VhLoFFpfuETX10eIRks0Xuj2nGiqCDj7ca0/M44bRg2VvW8+tg/ZEHFNl9g==
"@tiptap/extension-code-block@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.6.0.tgz#62a7327d0d96adfd91ca10d921abc2d16eede8ba"
integrity sha512-sDQhuxetwgCAqbD5hAjz6bqpvnPbS8cuv0kWmkHd29filzTn3CiwIRkXIz715POadN/jkZv7CDw5WsJcHiUjng==
"@tiptap/extension-code@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.5.9.tgz#93c4433eca8b2aa239ea7f408b90f152b7fc4603"
integrity sha512-Q1PL3DUXiEe5eYUwOug1haRjSaB0doAKwx7KFVI+kSGbDwCV6BdkKAeYf3us/O2pMP9D0im8RWX4dbSnatgwBA==
"@tiptap/extension-code@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.6.0.tgz#6fac1eafcfbbb7ab6c8d2ce88864305a1cd10a8f"
integrity sha512-xs7VhgnpIncVORP/a5K8Ad7ASOQ0P/JaF2nKnK4Sy6mFU2f8PgwOXbgbAaLtUfv2e5nLrqS3hGegePQ3y3R8+Q==
"@tiptap/extension-document@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.5.9.tgz#13a22b2d3bdc1463844872b1f1c926633df431a8"
integrity sha512-VdNZYDyCzC3W430UdeRXR9IZzPeODSbi5Xz/JEdV93THVp8AC9CrZR7/qjqdBTgbTB54VP8Yr6bKfCoIAF0BeQ==
"@tiptap/extension-document@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.6.0.tgz#5c21fcb40dcbabdfedc54bd17f964ec2a28164c8"
integrity sha512-aKyKCaErlZSOav2mttktvbyYhpgNfQcEvrupkDuRYRsdy1CH5h66Of1Vc2F9z/NgZvkyJLWWv2T7fwyAYnMnYw==
"@tiptap/extension-dropcursor@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.5.9.tgz#648f683f929056a0526620f530f73e6b052c1481"
integrity sha512-nEOb37UryG6bsU9JAs/HojE6Jg43LupNTAMISbnuB1CPAeAqNsFMwORd9eEPkyEwnQT7MkhsMOSJM44GoPGIFA==
"@tiptap/extension-dropcursor@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.6.0.tgz#0f2ed1ecb6eade94a629689ff4fd20300c5ac86f"
integrity sha512-l0u+T5EDICm7KKC1HBCKYyPKfoSINsG01QXAYBcogbFBsJRQJk0m9e0Bsf3kczbndV8+Sn/SOb+S1wbBoXcq7Q==
"@tiptap/extension-floating-menu@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.5.9.tgz#b970905f3c1af49a916dcbd477a4302086187974"
integrity sha512-MWJIQQT6e5MgqHny8neeH2Dx926nVPF7sv4p84nX4E0dnkRbEYUP8mCsWYhSUvxxIif6e+yY+4654f2Q9qTx1w==
"@tiptap/extension-floating-menu@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.6.0.tgz#6dd656ce00e80d5312dacc7fe472d3233ccddad7"
integrity sha512-eFmDaJJgGC+PBdRwfWSTzNfJR5Z0udPjHUf4pJosz2PcIg6wzeAxJyELzprk8qWNKbrxDm0CeJ0nz4uXxECdVw==
dependencies:
tippy.js "^6.3.7"
"@tiptap/extension-gapcursor@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.5.9.tgz#68b9e227cd7876aac353a8ac029995b4c092a763"
integrity sha512-yW7V2ebezsa7mWEDWCg4A1ZGsmSV5bEHKse9wzHCDkb7TutSVhLZxGo72U6hNN9PnAksv+FJQk03NuZNYvNyRQ==
"@tiptap/extension-gapcursor@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.6.0.tgz#a38d426f6f7691e3cb8bb038542557733bf8ec8b"
integrity sha512-mfkoMQuqLlnjamFJobWgXQb3g8S9P0Y8XuuyxK7MLTUdDgQdmw9/c2mF0LiaoJ825xjRRHELNuHZIPnyRWoP9Q==
"@tiptap/extension-hard-break@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.5.9.tgz#4f38f06dbeb5fb3e58ff7fc0c48b9db9c4ee4ecd"
integrity sha512-8hQ63SgZRG4BqHOeSfeaowG2eMr2beced018pOGbpHbE3XSYoISkMVuFz4Z8UEVR3W9dTbKo4wxNufSTducocQ==
"@tiptap/extension-hard-break@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.6.0.tgz#6a475ca5422a53c2739c0b54eaf488098972e6cc"
integrity sha512-RX5Xmj2svS7T3QkDs0X8aTekkcDxpnJ3hqHarzR1gK0GdVsrpowVKYhuEAc1zAAUirVa/IBimKXAIkXjDvuvNA==
"@tiptap/extension-heading@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.5.9.tgz#b9ec3b3b48dea939606d06eff56c4bdc7bed0662"
integrity sha512-HHowAlGUbFn1qvmY02ydM7qiPPMTGhAJn2A46enDRjNHW5UoqeMfkMpTEYaioOexyguRFSfDT3gpK68IHkQORQ==
"@tiptap/extension-heading@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.6.0.tgz#347e99128de6f368b6942d07f8b1ba99fdb0da17"
integrity sha512-dq3o9DoiqqzITKIeFrJSRuExrukrP1fNEwPEsrGx8vcwz3c0HIvQ9h/0jKzmjVD9UT+qhM/6DOiYlFnqDlTzqg==
"@tiptap/extension-highlight@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.5.9.tgz#290426538abcbb2299809d3e1274ba5af1ba9f68"
integrity sha512-tRaSIIbCI7aBlvlmgUgBI5lVBqnMy49lc++UVAx1Pjey1j2KW031vUyvZfEwf6wk8Y7W3kVSkN0mW9IYCcOAOQ==
"@tiptap/extension-highlight@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.6.0.tgz#082f01e3e66d069f9c49e25a9d9e9c410ff9c52e"
integrity sha512-k25Krl4KwqY3kaldFm3rp9evm5tJZyJmNQeHVfcjn20TqWP+g1OTAFYvai/Gwy5UtPpk03i6SyzvVNl6hwLxrQ==
"@tiptap/extension-history@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.5.9.tgz#f48f64ff95407f0ce27bcdd020762e49d0dd60d1"
integrity sha512-hGPtJgoZSwnVVqi/xipC2ET/9X2G2UI/Y+M3IYV1ZlM0tCYsv4spNi3uXlZqnXRwYcBXLk5u6e/dmsy5QFbL8g==
"@tiptap/extension-history@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.6.0.tgz#a52ec849834ec86035805fcbead9d13f2b4edd02"
integrity sha512-uFQU5Eo5pKboJmI8paqZLe+c3eV9RQ0RLvQ8PsJsKRTJnlq69l6phl6ATvNJC9GmVD2OwxHoroByPB5jS0o+HA==
"@tiptap/extension-horizontal-rule@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.5.9.tgz#9f91a17b80700670e53e241fcee40365c57aa994"
integrity sha512-/ES5NdxCndBmZAgIXSpCJH8YzENcpxR0S8w34coSWyv+iW0Sq7rW/mksQw8ZIVsj8a7ntpoY5OoRFpSlqcvyGw==
"@tiptap/extension-horizontal-rule@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.6.0.tgz#00039d78f8e44844de7234279ab76bd85af79c6d"
integrity sha512-CJ2ma8Wxww+E0SdSp8t/V2Gvv7q99fPplSSEWSgDWXnMDfcfV3vg637kvnLZpGX+qiRmgYAvjRsyNRQxTvJGgQ==
"@tiptap/extension-image@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.5.9.tgz#2f2be205d889f6688ef85c76071d2199baab03e4"
integrity sha512-v4WZISCvbriac6HE6v7kYYY7KX+v9rJaIZC3gbYGtqnBWfaAwZiVVu8Z03xSrqYhoc+KHuI+oQ4VubcvZ/i7OQ==
"@tiptap/extension-image@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.6.0.tgz#0515bd949e58efe499546ee9d5965fd0ae4520cd"
integrity sha512-j50e6P+L344iDxaPVV9svERBJv8MF+N3bKYJWPIu5GhHpoT1yO2L46rnXB8AHz2k5NHs4ZOj9SnSivMKDf/o7w==
"@tiptap/extension-italic@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.5.9.tgz#8ea0e19e650f0f1d6fc30425ec28291511143dda"
integrity sha512-Bw+P139L4cy+B56zpUiRjP8BZSaAUl3JFMnr/FO+FG55QhCxFMXIc6XrC3vslNy5ef3B3zv4gCttS3ee8ByMiw==
"@tiptap/extension-italic@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.6.0.tgz#00768b7c8916cb0098c2b85be6647e5a4289d8f5"
integrity sha512-ZILwDaTbFv5GVuxAga72GDmx8VHYqFOK8rkg/GCtW6OfAQVrlC17pzo2uRphzHpYTmOvNW5e63PC0fRbA1qXsg==
"@tiptap/extension-link@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.5.9.tgz#6cb323d36b82700963ad2b9d189a7d07c81c7d6e"
integrity sha512-7v9yRsX7NuiY8DPslIsPIlFqcD8aGBMLqfEGXltJDvuG6kykdr+khEZeWcJ8ihHIL4yWR3/MAgeT2W72Z/nxiQ==
"@tiptap/extension-link@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.6.0.tgz#2a3f65742fcb2776973541e9f2cea3169ce4a204"
integrity sha512-16ckFDnwqt0tYtQRgnVhooAUTLSk7liaqcGgc3NJ35gqWZho8hSnaTOl+72dlyJqUjqwt8D24NHFJsSqB9PSxA==
dependencies:
linkifyjs "^4.1.0"
"@tiptap/extension-list-item@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.5.9.tgz#0805b7216371b8b54649abe5ab29bd2c2155f05f"
integrity sha512-d9Eo+vBz74SMxP0r25aqiErV256C+lGz+VWMjOoqJa6xWLM1keYy12JtGQWJi8UDVZrDskJKCHq81A0uLt27WA==
"@tiptap/extension-list-item@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.6.0.tgz#5b960a8c6bceea1fe5f7c730cb3547b920fe0706"
integrity sha512-vgrLi3lYe1YHBPCjEFLBfUyeS2DueU/w/L8uNuM0SP38k0wuGZRbYr2mc5oZmVRrCsBdoY9abvjkJHLDVvzwTA==
"@tiptap/extension-ordered-list@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.5.9.tgz#44aab6ec3e19429a8e3b73e42c04156f2b0bc730"
integrity sha512-9MsWpvVvzILuEOd/GdroF7RI7uDuE1M6at9rzsaVGvCPVHZBvu1XR3MSVK5OdiJbbJuPGttlzEFLaN/rQdCGFg==
"@tiptap/extension-ordered-list@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.6.0.tgz#ca163a9237dad834b258bf2e159cb78a794f333a"
integrity sha512-eRYux5eRQFCUApOc0L4+R8uAa2tdiCCYqboe/Hadm35u5Z9VsO9V828BWeVVqtkUwSYJY3dz3tXplBkZ+ot+7A==
"@tiptap/extension-paragraph@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.5.9.tgz#05210b6e7a9940b1acc09fdd4ec769fc6406da2b"
integrity sha512-HDXGiHTJ/V85dbDMjcFj4XfqyTQZqry6V21ucMzgBZYX60X3gIn7VpQTQnnRjvULSgtfOASSJP6BELc5TyiK0w==
"@tiptap/extension-paragraph@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.6.0.tgz#721921477e3785c0a0abf132ea109e67bcea9fae"
integrity sha512-ztmgfCSaCGFCR4VudA91WJDEQrpXH2FirQrJSaoOOwk3OfrNSSvcbqAhTmxJHl8e7aFwuR5eH8wbIxKw9In3Kg==
"@tiptap/extension-strike@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.5.9.tgz#f2d54161d24ee37dc8a41b5077c553048ed69f99"
integrity sha512-QezkOZpczpl09S8lp5JL7sRkwREoPY16Y/lTvBcFKm3TZbVzYZZ/KwS0zpwK9HXTfXr8os4L9AGjQf0tHonX+w==
"@tiptap/extension-strike@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.6.0.tgz#1bff3ab3b91f7cdbb39bd7a860ad30e60e1750bd"
integrity sha512-+gt750H4ioTpwIQXYV5K4BScvAMBuWHvW/f4197LYE/fy6uNqUf62XRz6Ew12z13rEbWGzMHRdpYIguTHcqvzA==
"@tiptap/extension-subscript@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-subscript/-/extension-subscript-2.5.9.tgz#b6201139faf9cf4b8be013a21161806af660c666"
integrity sha512-SY1VCf/zlsBLowZaayXGl7dkIGPMNieCO0P1luFBjsiEXCRff0WYVpxi24wzgMeWE6q28SXmd3eD5BsGBudx9g==
"@tiptap/extension-subscript@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-subscript/-/extension-subscript-2.6.0.tgz#124ea99a960c9af399d65388d2b6b81ab5478d13"
integrity sha512-FSkUVNg2kIzJAP4sgoEulLk/7T1qhaDAFCMB3i6/ppftlpEtr+HaWUx3F3qaBCeHmRpMGOF/nje72Y9+SrKZMg==
"@tiptap/extension-superscript@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-superscript/-/extension-superscript-2.5.9.tgz#da9549e767d7686c2ca716d5eeb46d26814950f7"
integrity sha512-ggpVx/oJk6tBDq7mNxrJNXhitHVqkJVbeOKaac5FACCoA3ZzHgZ75JooxZqn5wRoCaqShmbljLb8ZqefdX/PRg==
"@tiptap/extension-superscript@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-superscript/-/extension-superscript-2.6.0.tgz#15f74fbc522c7cbc69f54c4f4cc1549e07acc219"
integrity sha512-zs3C1xt0qXAfE20HbgKq49P7t2q+3w9YzEUXy3ky/d2pNCHrrZU1nddpn8+cPf3KnG7kLarYb6cv/1JZmDsrEQ==
"@tiptap/extension-table-cell@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.5.9.tgz#dffcd23cb11d3ca1239c7290297c9caf0d3b8434"
integrity sha512-83zg+d8iY7FLZDC2qJVvHFEIwqB9e/zGPyfRMglYH7YxHeJSycG2K969DQhwkSq+z0PhHEO2XHDcD9aFnXRQNQ==
"@tiptap/extension-table-cell@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.6.0.tgz#8b6c01ddafed7a914f06349cd72cc2b316e9673f"
integrity sha512-+zOR3c9fzSr9TRFl7p2KxY1WOLit/+0kgIpbsidCAvVH1rigV7x/l/Lo3zOVP+lCzUoAVvaJX293PY9y61lDQQ==
"@tiptap/extension-table-header@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.5.9.tgz#405da76163ec4899b1d6b81f2fbbdb7c0f71bf59"
integrity sha512-+FKfxpEO8RnsHUhcWFeSpsI3ZRaDtgcX2c4kBBXZGJBMWHxw71VK9gkRM+JtxCl70hNyZR13qpOw1RmByf2kdw==
"@tiptap/extension-table-header@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.6.0.tgz#790d51971b19c1ab5a1042a1a6d389fd62dbcf43"
integrity sha512-mkh9RPA2iueMDsQjbs7YwExxS02EY+fYy/mA2bIqhu/4eZXLgHuZADmbW6VfPjkEZ0nRoToQCyDvP8NHS5WpWA==
"@tiptap/extension-table-row@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.5.9.tgz#62e5e329b38daf6142b39d7c17de045dfc6918be"
integrity sha512-VPmkzraT9m7g2/88qsTF9EQdFvkcwhvOD+WWZTflN92LMsRHddJt3peJ/fpuf0QKnwwn5qIB3fXWja4VcZe3wQ==
"@tiptap/extension-table-row@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.6.0.tgz#f1629be948c58d8f9fc0bee01b52ca05b92f7331"
integrity sha512-2rJCjK6cHPPA81YtrBew+hfSAUclvaz1nSV2WBvoWiY8UQHdaRhESVmmpkEhUSuX7JaevN93mZhpQ4Tj6Q7B6w==
"@tiptap/extension-table@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.5.9.tgz#a7b80a668a683c3abe59725812091123b2c12b88"
integrity sha512-kLZdYBO0Ug4sNjzyDfa3W29qL4HdRK/IaMxcmcEbyKSt42qiMJlIelbGzVENzxe9AbcNTeiWje70Nhk4dbb8Ag==
"@tiptap/extension-table@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.6.0.tgz#9866f5dfff14f9038d57e3f5e5615bd2bcc43bc0"
integrity sha512-dTOvgX7a9EnzqfqUZMlk6MUolb0nFN3JQG8foppxVPnO04H5Apwyhq39n87JHWQorFR5+dbbPg0FQk+3qNzP5g==
"@tiptap/extension-task-item@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.5.9.tgz#623590d549aa0e21ccd1d34396ebbeeea45886f5"
integrity sha512-g4HK3r3yNE0RcXQOkJHs94Ws/fhhTqa1L5iAy4gwYKNNFFnIQl8BpE6nn9d5h33kWDN9jjY+PZmq+0PvxCLODQ==
"@tiptap/extension-task-item@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.6.0.tgz#93a064cd7d3b83a33d885092a964acf6f22ca6dd"
integrity sha512-IWJgtyKPU1N+Vb3YpwU2k7m8APl/YVnH2OMFV6PohV9eAA0BoH4bGC8fLaPO4Kq7Jl7er4darHDI8touTSQyVg==
"@tiptap/extension-task-list@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.5.9.tgz#9934b3ae84dbfc27804d0d54e64aeb9e8fcaf418"
integrity sha512-OylVo5cAh0117PzhyM8MGaUIrCskGiF7v7x6/zAHMFIqVdcbKsq+hMueVPnABfOyLcIH5Zojo3NzNOJeKeblCg==
"@tiptap/extension-task-list@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.6.0.tgz#7f720a398cd8ad0163841481dfcff676d00b3350"
integrity sha512-bH0yQN4U3RhW/jlnXExfsZYPFHUjU1YxhbM2UvKY6w1IrqEDxIN+zjvaQ/anu9+4Z4zuzL6qNaJw7O5CdzqkqA==
"@tiptap/extension-text@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.5.9.tgz#a5bef0b9c5324511dbc2804a3a5ac8b9b5d5dc4c"
integrity sha512-W0pfiQUPsMkwaV5Y/wKW4cFsyXAIkyOFt7uN5u6LrZ/iW9KZ/IsDODPJDikWp0aeQnXzT9NNQULTpCjbHzzS6g==
"@tiptap/extension-text@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.6.0.tgz#395cd44456059c11fb5f3e2215093a5c32ba59a3"
integrity sha512-Bg/FXGzj6WGvqgCVvFKzTWOpgg8Nl4X2a/bUT7nEmBafpUNAiN6enomMWnniylGTFEqZrywr5KRcvbgl8ubeDQ==
"@tiptap/pm@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.5.9.tgz#f97889210374993a1ce78e9ecb23461d0e4644bf"
integrity sha512-YSUaEQVtvZnGzGjif2Tl2o9utE+6tR2Djhz0EqFUcAUEVhOMk7UYUO+r/aPfcCRraIoKKuDQzyCpjKmJicjCUA==
"@tiptap/pm@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.6.0.tgz#b2ef8aba2f5a04ac3f5051e3d891ffe26f37967b"
integrity sha512-Oj912Jfowuq/K+oEkqx+pADjuVwBCXroC2hdrRMo4w18bb2gI2Nhse6St1KQ9kWWGF36O81RpDbpLe7L1YQhzQ==
dependencies:
prosemirror-changeset "^2.2.1"
prosemirror-collab "^1.3.1"
@ -2814,18 +2814,18 @@
prosemirror-transform "^1.9.0"
prosemirror-view "^1.33.9"
"@tiptap/suggestion@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.5.9.tgz#af54bd41c2146454619fcadf15129a499bb6d05d"
integrity sha512-s7UU0j2IRreVXrMMxsFvsNjJnZeTS1SAwsjLkN2YX+/ZQss92s0BLP3HsxEr2oFHlFye8E0qR9xjWZ4vSc9asw==
"@tiptap/suggestion@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.6.0.tgz#457ae6a7f50ec47cf8fa7c2a3d1c88a8dfb0ed6d"
integrity sha512-NAsXzAbHa3epnvnK4mqzRYhvjBYndu/lnC4SVOFvgBbCqQ/V698DwYDZ8iKLBeEzckzd7G6RON223AyvCF+9lw==
"@tiptap/vue-2@^2.5.9":
version "2.5.9"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.5.9.tgz#a8bd3854ffff25ce09b1843d4f0b663d643b84b6"
integrity sha512-gqwwUfRy1WT9uHaZEpTWjEavLrsDfpjwzSQ/w1NjX/broyDMKc9WR/G2qlBCruYLBuypJSX9Z3nCMABD7Zhyaw==
"@tiptap/vue-2@^2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.6.0.tgz#df7113e33b773a5faaf99f056430ff2ad257c831"
integrity sha512-o6yw06XACSzFFDBdKTl8o0B/R2HVcfRKyTPQTakKcCwlHMKpsgdWNNf5F4BV8n9nSry4g7jSIhrD0WS5Eu7klg==
dependencies:
"@tiptap/extension-bubble-menu" "^2.5.9"
"@tiptap/extension-floating-menu" "^2.5.9"
"@tiptap/extension-bubble-menu" "^2.6.0"
"@tiptap/extension-floating-menu" "^2.6.0"
vue-ts-types "^1.6.0"
"@tootallnate/once@2":