Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
414fefc8c4
commit
9a1203dfe8
|
@ -1,3 +1,5 @@
|
|||
import initSearchSettings from '~/search_settings';
|
||||
import initWebhookForm from '~/webhooks';
|
||||
|
||||
initSearchSettings();
|
||||
initWebhookForm();
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<script>
|
||||
import { GlFormGroup, GlFormInput, GlFormRadio, GlFormRadioGroup } from '@gitlab/ui';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
import FormUrlMaskItem from './form_url_mask_item.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FormUrlMaskItem,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlFormRadio,
|
||||
GlFormRadioGroup,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
maskEnabled: false,
|
||||
url: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
maskedUrl() {
|
||||
return this.url;
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
radioFullUrlText: s__('Webhooks|Show full URL'),
|
||||
radioMaskUrlText: s__('Webhooks|Mask portions of URL'),
|
||||
radioMaskUrlHelp: s__('Webhooks|Do not show sensitive data such as tokens in the UI.'),
|
||||
urlDescription: s__(
|
||||
'Webhooks|URL must be percent-encoded if it contains one or more special characters.',
|
||||
),
|
||||
urlLabel: __('URL'),
|
||||
urlPlaceholder: 'http://example.com/trigger-ci.json',
|
||||
urlPreview: s__('Webhooks|URL preview'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.urlLabel"
|
||||
label-for="webhook-url"
|
||||
:description="$options.i18n.urlDescription"
|
||||
>
|
||||
<gl-form-input
|
||||
id="webhook-url"
|
||||
v-model="url"
|
||||
name="hook[url]"
|
||||
:placeholder="$options.i18n.urlPlaceholder"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<div class="gl-mt-5">
|
||||
<gl-form-radio-group v-model="maskEnabled">
|
||||
<gl-form-radio :value="false">{{ $options.i18n.radioFullUrlText }}</gl-form-radio>
|
||||
<gl-form-radio :value="true"
|
||||
>{{ $options.i18n.radioMaskUrlText }}
|
||||
<template #help>
|
||||
{{ $options.i18n.radioMaskUrlHelp }}
|
||||
</template>
|
||||
</gl-form-radio>
|
||||
</gl-form-radio-group>
|
||||
|
||||
<div v-if="maskEnabled" class="gl-ml-6" data-testid="url-mask-section">
|
||||
<form-url-mask-item :index="0" />
|
||||
<gl-form-group :label="$options.i18n.urlPreview" label-for="webhook-url-preview">
|
||||
<gl-form-input id="webhook-url-preview" :value="maskedUrl" readonly />
|
||||
</gl-form-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,61 @@
|
|||
<script>
|
||||
import { GlButton, GlFormGroup, GlFormInput } from '@gitlab/ui';
|
||||
import { s__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
},
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
keyInputId() {
|
||||
return this.inputId('key');
|
||||
},
|
||||
valueInputId() {
|
||||
return this.inputId('value');
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
inputId(type) {
|
||||
return `webhook-url-mask-item-${type}-${this.index}`;
|
||||
},
|
||||
inputName(type) {
|
||||
return `hook[url_variables][][${type}]`;
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
keyLabel: s__('Webhooks|How it looks in the UI'),
|
||||
valueLabel: s__('Webhooks|Sensitive portion of URL'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-display-flex gl-align-items-flex-end gl-gap-3 gl-mb-5">
|
||||
<gl-form-group
|
||||
:label="$options.i18n.valueLabel"
|
||||
:label-for="valueInputId"
|
||||
class="gl-flex-grow-1 gl-mb-0"
|
||||
data-testid="mask-item-value"
|
||||
>
|
||||
<gl-form-input :id="valueInputId" :name="inputName('value')" />
|
||||
</gl-form-group>
|
||||
<gl-form-group
|
||||
:label="$options.i18n.keyLabel"
|
||||
:label-for="keyInputId"
|
||||
class="gl-flex-grow-1 gl-mb-0"
|
||||
data-testid="mask-item-key"
|
||||
>
|
||||
<gl-form-input :id="keyInputId" :name="inputName('key')" />
|
||||
</gl-form-group>
|
||||
<gl-button icon="remove" />
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,18 @@
|
|||
import Vue from 'vue';
|
||||
import FormUrlApp from './components/form_url_app.vue';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-vue-webhook-form');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: 'WebhookFormRoot',
|
||||
render(createElement) {
|
||||
return createElement(FormUrlApp, {});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -22,6 +22,7 @@ class Note < ApplicationRecord
|
|||
include ThrottledTouch
|
||||
include FromUnion
|
||||
include Sortable
|
||||
include EachBatch
|
||||
|
||||
ISSUE_TASK_SYSTEM_NOTE_PATTERN = /\A.*marked\sthe\stask.+as\s(completed|incomplete).*\z/.freeze
|
||||
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
= form_errors(hook)
|
||||
|
||||
.form-group
|
||||
= form.label :url, s_('Webhooks|URL'), class: 'label-bold'
|
||||
= form.text_field :url, class: 'form-control gl-form-input', placeholder: 'http://example.com/trigger-ci.json'
|
||||
%p.form-text.text-muted
|
||||
= s_('Webhooks|URL must be percent-encoded if it contains one or more special characters.')
|
||||
- if Feature.enabled?(:webhook_form_mask_url)
|
||||
.js-vue-webhook-form
|
||||
- else
|
||||
.form-group
|
||||
= form.label :url, s_('Webhooks|URL'), class: 'label-bold'
|
||||
= form.text_field :url, class: 'form-control gl-form-input', placeholder: 'http://example.com/trigger-ci.json'
|
||||
%p.form-text.text-muted
|
||||
= s_('Webhooks|URL must be percent-encoded if it contains one or more special characters.')
|
||||
.form-group
|
||||
= form.label :token, s_('Webhooks|Secret token'), class: 'label-bold'
|
||||
= form.text_field :token, class: 'form-control gl-form-input', placeholder: ''
|
||||
|
|
|
@ -1020,6 +1020,24 @@
|
|||
:weight: 1
|
||||
:idempotent: false
|
||||
:tags: []
|
||||
- :name: github_importer:github_import_attachments_import_note
|
||||
:worker_name: Gitlab::GithubImport::Attachments::ImportNoteWorker
|
||||
:feature_category: :importers
|
||||
:has_external_dependencies: true
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: false
|
||||
:tags: []
|
||||
- :name: github_importer:github_import_attachments_import_release
|
||||
:worker_name: Gitlab::GithubImport::Attachments::ImportReleaseWorker
|
||||
:feature_category: :importers
|
||||
:has_external_dependencies: true
|
||||
:urgency: :low
|
||||
:resource_boundary: :unknown
|
||||
:weight: 1
|
||||
:idempotent: false
|
||||
:tags: []
|
||||
- :name: github_importer:github_import_import_diff_note
|
||||
:worker_name: Gitlab::GithubImport::ImportDiffNoteWorker
|
||||
:feature_category: :importers
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Attachments
|
||||
class ImportNoteWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ObjectImporter
|
||||
|
||||
def representation_class
|
||||
Representation::NoteText
|
||||
end
|
||||
|
||||
def importer_class
|
||||
Importer::NoteAttachmentsImporter
|
||||
end
|
||||
|
||||
def object_type
|
||||
:note_attachment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Attachments
|
||||
class ImportReleaseWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ObjectImporter
|
||||
|
||||
def representation_class
|
||||
Representation::NoteText
|
||||
end
|
||||
|
||||
def importer_class
|
||||
Importer::NoteAttachmentsImporter
|
||||
end
|
||||
|
||||
def object_type
|
||||
:release_attachment
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,16 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# TODO: remove in 16.X milestone
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/377059
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
class ImportReleaseAttachmentsWorker # rubocop:disable Scalability/IdempotentWorker
|
||||
include ObjectImporter
|
||||
|
||||
def representation_class
|
||||
Representation::ReleaseAttachments
|
||||
Representation::NoteText
|
||||
end
|
||||
|
||||
def importer_class
|
||||
Importer::ReleaseAttachmentsImporter
|
||||
Importer::NoteAttachmentsImporter
|
||||
end
|
||||
|
||||
def object_type
|
||||
|
|
|
@ -28,7 +28,10 @@ module Gitlab
|
|||
|
||||
# For future issue/mr/milestone/etc attachments importers
|
||||
def importers
|
||||
[::Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter]
|
||||
[
|
||||
::Gitlab::GithubImport::Importer::Attachments::ReleasesImporter,
|
||||
::Gitlab::GithubImport::Importer::Attachments::NotesImporter
|
||||
]
|
||||
end
|
||||
|
||||
def start_importer(project, importer, client)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: webhook_form_mask_url
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/99995
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/376106
|
||||
milestone: '15.5'
|
||||
type: development
|
||||
group: group::integrations
|
||||
default_enabled: false
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexReleasesProjectIdId < Gitlab::Database::Migration[2.0]
|
||||
INDEX_NAME = 'index_releases_on_project_id_id'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_index :releases, %i[project_id id], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :releases, name: INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
0f625bd9df16d035dd8cd73d5097e5b5f4d1b900183bd6ccf4b1489319535df6
|
|
@ -30160,6 +30160,8 @@ CREATE INDEX index_releases_on_author_id_id_created_at ON releases USING btree (
|
|||
|
||||
CREATE INDEX index_releases_on_project_id_and_released_at_and_id ON releases USING btree (project_id, released_at, id);
|
||||
|
||||
CREATE INDEX index_releases_on_project_id_id ON releases USING btree (project_id, id);
|
||||
|
||||
CREATE UNIQUE INDEX index_releases_on_project_tag_unique ON releases USING btree (project_id, tag);
|
||||
|
||||
CREATE INDEX index_releases_on_released_at ON releases USING btree (released_at);
|
||||
|
|
|
@ -127,13 +127,15 @@ comments.
|
|||
|
||||
### 10. Stage::ImportAttachmentsWorker
|
||||
|
||||
This worker imports release notes attachments that are linked inside Markdown.
|
||||
For every release of the project, we schedule a job of
|
||||
`Gitlab::GithubImport::ImportReleaseAttachmentsWorker` for every comment.
|
||||
This worker imports note attachments that are linked inside Markdown.
|
||||
For each entity with Markdown text in the project, we schedule a job of:
|
||||
|
||||
- `Gitlab::GithubImport::ImportReleaseAttachmentsWorker` for every release.
|
||||
- `Gitlab::GithubImport::ImportNoteAttachmentsWorker` for every note.
|
||||
|
||||
Each job:
|
||||
|
||||
1. Iterates over all attachment links inside of a specific release note.
|
||||
1. Iterates over all attachment links inside of a specific record.
|
||||
1. Downloads the attachment.
|
||||
1. Replaces the old link with a newly-generated link to GitLab.
|
||||
|
||||
|
|
|
@ -178,6 +178,11 @@ The following items of a project are imported:
|
|||
- Milestones.
|
||||
- Labels.
|
||||
- Release note descriptions.
|
||||
- Attachments for:
|
||||
- Release notes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15620) in GitLab 15.4.
|
||||
- Comments and notes. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18052) in GitLab 15.5.
|
||||
|
||||
NOTE: All attachment importers work under `github_importer_attachments_import` [feature flag](../../../administration/feature_flags.md) disabled by default.
|
||||
- Release note attachments. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15620) in GitLab 15.4 with `github_importer_attachments_import`
|
||||
[feature flag](../../../administration/feature_flags.md) disabled by default.
|
||||
- Pull request review comments.
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Exceptions
|
||||
# Sometimes it's not clear which of not implemented interfaces caused this error.
|
||||
# We need custom exception to be able to add text that gives extra context.
|
||||
NotImplementedError = Class.new(StandardError)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Importer
|
||||
module Attachments
|
||||
class BaseImporter
|
||||
include ParallelScheduling
|
||||
|
||||
BATCH_SIZE = 100
|
||||
|
||||
# The method that will be called for traversing through all the objects to
|
||||
# import, yielding them to the supplied block.
|
||||
def each_object_to_import
|
||||
collection.each_batch(of: BATCH_SIZE) do |batch|
|
||||
batch.each do |record|
|
||||
next if already_imported?(record)
|
||||
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
|
||||
|
||||
yield record
|
||||
|
||||
# We mark the object as imported immediately so we don't end up
|
||||
# scheduling it multiple times.
|
||||
mark_as_imported(record)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def representation_class
|
||||
Representation::NoteText
|
||||
end
|
||||
|
||||
def importer_class
|
||||
NoteAttachmentsImporter
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
raise Gitlab::GithubImport::Exceptions::NotImplementedError, '#collection'
|
||||
end
|
||||
|
||||
def object_representation(object)
|
||||
representation_class.from_db_record(object)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Importer
|
||||
module Attachments
|
||||
class NotesImporter < ::Gitlab::GithubImport::Importer::Attachments::BaseImporter
|
||||
def sidekiq_worker_class
|
||||
::Gitlab::GithubImport::Attachments::ImportNoteWorker
|
||||
end
|
||||
|
||||
def collection_method
|
||||
:note_attachments
|
||||
end
|
||||
|
||||
def object_type
|
||||
:note_attachment
|
||||
end
|
||||
|
||||
def id_for_already_imported_cache(note)
|
||||
note.id
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# TODO: exclude :system, :noteable_type from select after removing override Note#note method
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/369923
|
||||
def collection
|
||||
project.notes.user.select(:id, :note, :system, :noteable_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Importer
|
||||
module Attachments
|
||||
class ReleasesImporter < ::Gitlab::GithubImport::Importer::Attachments::BaseImporter
|
||||
def sidekiq_worker_class
|
||||
::Gitlab::GithubImport::Attachments::ImportReleaseWorker
|
||||
end
|
||||
|
||||
def collection_method
|
||||
:release_attachments
|
||||
end
|
||||
|
||||
def object_type
|
||||
:release_attachment
|
||||
end
|
||||
|
||||
def id_for_already_imported_cache(release)
|
||||
release.id
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collection
|
||||
project.releases.select(:id, :description)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,26 +3,27 @@
|
|||
module Gitlab
|
||||
module GithubImport
|
||||
module Importer
|
||||
class ReleaseAttachmentsImporter
|
||||
attr_reader :release_db_id, :release_description, :project
|
||||
class NoteAttachmentsImporter
|
||||
attr_reader :note_text, :project
|
||||
|
||||
# release - An instance of `ReleaseAttachments`.
|
||||
# note_text - An instance of `NoteText`.
|
||||
# project - An instance of `Project`.
|
||||
# client - An instance of `Gitlab::GithubImport::Client`.
|
||||
def initialize(release_attachments, project, _client = nil)
|
||||
@release_db_id = release_attachments.release_db_id
|
||||
@release_description = release_attachments.description
|
||||
def initialize(note_text, project, _client = nil)
|
||||
@note_text = note_text
|
||||
@project = project
|
||||
end
|
||||
|
||||
def execute
|
||||
attachment_urls = MarkdownText.fetch_attachment_urls(release_description)
|
||||
new_description = attachment_urls.reduce(release_description) do |description, url|
|
||||
attachment_urls = MarkdownText.fetch_attachment_urls(note_text.text)
|
||||
return if attachment_urls.blank?
|
||||
|
||||
new_text = attachment_urls.reduce(note_text.text) do |text, url|
|
||||
new_url = download_attachment(url)
|
||||
description.gsub(url, new_url)
|
||||
text.gsub(url, new_url)
|
||||
end
|
||||
|
||||
Release.find(release_db_id).update_column(:description, new_description)
|
||||
update_note_record(new_text)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -52,6 +53,15 @@ module Gitlab
|
|||
def extract_name_from_markdown(text)
|
||||
text.match(%r{^!?\[.*\]}).to_a[0]
|
||||
end
|
||||
|
||||
def update_note_record(text)
|
||||
case note_text.record_type
|
||||
when ::Release.name
|
||||
::Release.find(note_text.record_db_id).update_column(:description, text)
|
||||
when ::Note.name
|
||||
::Note.find(note_text.record_db_id).update_column(:note, text)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -36,6 +36,9 @@ module Gitlab
|
|||
# We're using bulk_insert here so we can bypass any validations and
|
||||
# callbacks. Running these would result in a lot of unnecessary SQL
|
||||
# queries being executed when importing large projects.
|
||||
# Note: if you're going to replace `legacy_bulk_insert` with something that trigger callback
|
||||
# to generate HTML version - you also need to regenerate it in
|
||||
# Gitlab::GithubImport::Importer::NoteAttachmentsImporter.
|
||||
ApplicationRecord.legacy_bulk_insert(Note.table_name, [attributes]) # rubocop:disable Gitlab/BulkInsert
|
||||
rescue ActiveRecord::InvalidForeignKey
|
||||
# It's possible the project and the issue have been deleted since
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Importer
|
||||
class ReleasesAttachmentsImporter
|
||||
include ParallelScheduling
|
||||
|
||||
BATCH_SIZE = 100
|
||||
|
||||
# The method that will be called for traversing through all the objects to
|
||||
# import, yielding them to the supplied block.
|
||||
def each_object_to_import
|
||||
project.releases.select(:id, :description).each_batch(of: BATCH_SIZE, column: :id) do |batch|
|
||||
batch.each do |release|
|
||||
next if already_imported?(release)
|
||||
|
||||
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
|
||||
|
||||
yield release
|
||||
|
||||
# We mark the object as imported immediately so we don't end up
|
||||
# scheduling it multiple times.
|
||||
mark_as_imported(release)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def representation_class
|
||||
Representation::ReleaseAttachments
|
||||
end
|
||||
|
||||
def importer_class
|
||||
ReleaseAttachmentsImporter
|
||||
end
|
||||
|
||||
def sidekiq_worker_class
|
||||
ImportReleaseAttachmentsWorker
|
||||
end
|
||||
|
||||
def collection_method
|
||||
:release_attachments
|
||||
end
|
||||
|
||||
def object_type
|
||||
:release_attachment
|
||||
end
|
||||
|
||||
def id_for_already_imported_cache(release)
|
||||
release.id
|
||||
end
|
||||
|
||||
def object_representation(object)
|
||||
representation_class.from_db_record(object)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,6 +12,9 @@ module Gitlab
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# Note: if you're going to replace `legacy_bulk_insert` with something that triggers callback
|
||||
# to generate HTML version - you also need to regenerate it in
|
||||
# Gitlab::GithubImport::Importer::NoteAttachmentsImporter.
|
||||
def execute
|
||||
bulk_insert(Release, build_releases)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This class only partly represents Release record from DB and
|
||||
# is used to connect ReleasesAttachmentsImporter, NotesAttachmentsImporter etc.
|
||||
# with NoteAttachmentsImporter without modifying ObjectImporter a lot.
|
||||
# Attachments are inside release's `description`.
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Representation
|
||||
class NoteText
|
||||
include ToHash
|
||||
include ExposeAttribute
|
||||
|
||||
MODELS_WHITELIST = [::Release, ::Note].freeze
|
||||
ModelNotSupported = Class.new(StandardError)
|
||||
|
||||
attr_reader :attributes
|
||||
|
||||
expose_attribute :record_db_id, :record_type, :text
|
||||
|
||||
class << self
|
||||
# Builds a note text representation from DB record of Note or Release.
|
||||
#
|
||||
# record - An instance of `Release` or `Note` model.
|
||||
def from_db_record(record)
|
||||
check_record_class!(record)
|
||||
|
||||
record_type = record.class.name
|
||||
text = record.is_a?(Release) ? record.description : record.note
|
||||
new(
|
||||
record_db_id: record.id,
|
||||
record_type: record_type,
|
||||
text: text
|
||||
)
|
||||
end
|
||||
|
||||
def from_json_hash(raw_hash)
|
||||
new Representation.symbolize_hash(raw_hash)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_record_class!(record)
|
||||
raise ModelNotSupported, record.class.name if MODELS_WHITELIST.exclude?(record.class)
|
||||
end
|
||||
end
|
||||
|
||||
# attributes - A Hash containing the event details. The keys of this
|
||||
# Hash (and any nested hashes) must be symbols.
|
||||
def initialize(attributes)
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
def github_identifiers
|
||||
{ db_id: record_db_id }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This class only partly represents Release record from DB and
|
||||
# is used to connect ReleasesAttachmentsImporter with ReleaseAttachmentsImporter
|
||||
# without modifying ObjectImporter a lot.
|
||||
# Attachments are inside release's `description`.
|
||||
module Gitlab
|
||||
module GithubImport
|
||||
module Representation
|
||||
class ReleaseAttachments
|
||||
include ToHash
|
||||
include ExposeAttribute
|
||||
|
||||
attr_reader :attributes
|
||||
|
||||
expose_attribute :release_db_id, :description
|
||||
|
||||
# Builds a event from a GitHub API response.
|
||||
#
|
||||
# release - An instance of `Release` model.
|
||||
def self.from_db_record(release)
|
||||
new(
|
||||
release_db_id: release.id,
|
||||
description: release.description
|
||||
)
|
||||
end
|
||||
|
||||
def self.from_json_hash(raw_hash)
|
||||
new Representation.symbolize_hash(raw_hash)
|
||||
end
|
||||
|
||||
# attributes - A Hash containing the event details. The keys of this
|
||||
# Hash (and any nested hashes) must be symbols.
|
||||
def initialize(attributes)
|
||||
@attributes = attributes
|
||||
end
|
||||
|
||||
def github_identifiers
|
||||
{ db_id: release_db_id }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -45011,6 +45011,9 @@ msgstr ""
|
|||
msgid "Webhooks|Deployment events"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Do not show sensitive data such as tokens in the UI."
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Enable SSL verification"
|
||||
msgstr ""
|
||||
|
||||
|
@ -45026,12 +45029,18 @@ msgstr ""
|
|||
msgid "Webhooks|Go to webhooks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|How it looks in the UI"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Issues events"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Job events"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Mask portions of URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Member events"
|
||||
msgstr ""
|
||||
|
||||
|
@ -45056,6 +45065,12 @@ msgstr ""
|
|||
msgid "Webhooks|Secret token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Sensitive portion of URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Show full URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Subgroup events"
|
||||
msgstr ""
|
||||
|
||||
|
@ -45080,6 +45095,9 @@ msgstr ""
|
|||
msgid "Webhooks|URL must be percent-encoded if it contains one or more special characters."
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|URL preview"
|
||||
msgstr ""
|
||||
|
||||
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -48,10 +48,10 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
|
|||
expect(page).to have_content('Releases events')
|
||||
end
|
||||
|
||||
it 'create webhook' do
|
||||
it 'create webhook', :js do
|
||||
visit webhooks_path
|
||||
|
||||
fill_in 'hook_url', with: url
|
||||
fill_in 'URL', with: url
|
||||
check 'Tag push events'
|
||||
fill_in 'hook_push_events_branch_filter', with: 'master'
|
||||
check 'Enable SSL verification'
|
||||
|
@ -66,12 +66,12 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
|
|||
expect(page).to have_content('Job events')
|
||||
end
|
||||
|
||||
it 'edit existing webhook' do
|
||||
it 'edit existing webhook', :js do
|
||||
hook
|
||||
visit webhooks_path
|
||||
|
||||
click_link 'Edit'
|
||||
fill_in 'hook_url', with: url
|
||||
fill_in 'URL', with: url
|
||||
check 'Enable SSL verification'
|
||||
click_button 'Save changes'
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
import { nextTick } from 'vue';
|
||||
import { GlFormRadio, GlFormRadioGroup } from '@gitlab/ui';
|
||||
|
||||
import FormUrlApp from '~/webhooks/components/form_url_app.vue';
|
||||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
describe('FormUrlApp', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(FormUrlApp);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findAllRadioButtons = () => wrapper.findAllComponents(GlFormRadio);
|
||||
const findRadioGroup = () => wrapper.findComponent(GlFormRadioGroup);
|
||||
const findUrlMaskDisable = () => findAllRadioButtons().at(0);
|
||||
const findUrlMaskEnable = () => findAllRadioButtons().at(1);
|
||||
const findUrlMaskSection = () => wrapper.findByTestId('url-mask-section');
|
||||
|
||||
describe('template', () => {
|
||||
it('renders radio buttons for URL masking', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findAllRadioButtons().length).toBe(2);
|
||||
expect(findUrlMaskDisable().text()).toBe(FormUrlApp.i18n.radioFullUrlText);
|
||||
expect(findUrlMaskEnable().text()).toBe(FormUrlApp.i18n.radioMaskUrlText);
|
||||
});
|
||||
|
||||
it('does not render mask section', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findUrlMaskSection().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('on radio select', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent();
|
||||
|
||||
findRadioGroup().vm.$emit('input', true);
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
it('renders mask section', () => {
|
||||
expect(findUrlMaskSection().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,51 @@
|
|||
import { GlButton, GlFormInput } from '@gitlab/ui';
|
||||
|
||||
import FormUrlMaskItem from '~/webhooks/components/form_url_mask_item.vue';
|
||||
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
describe('FormUrlMaskItem', () => {
|
||||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
index: 0,
|
||||
};
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(FormUrlMaskItem, {
|
||||
propsData: { ...defaultProps },
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findMaskItemKey = () => wrapper.findByTestId('mask-item-key');
|
||||
const findMaskItemValue = () => wrapper.findByTestId('mask-item-value');
|
||||
const findRemoveButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
describe('template', () => {
|
||||
it('renders input for key and value', () => {
|
||||
createComponent();
|
||||
|
||||
const keyInput = findMaskItemKey();
|
||||
expect(keyInput.attributes('label')).toBe(FormUrlMaskItem.i18n.keyLabel);
|
||||
expect(keyInput.findComponent(GlFormInput).attributes('name')).toBe(
|
||||
'hook[url_variables][][key]',
|
||||
);
|
||||
|
||||
const valueInput = findMaskItemValue();
|
||||
expect(valueInput.attributes('label')).toBe(FormUrlMaskItem.i18n.valueLabel);
|
||||
expect(valueInput.findComponent(GlFormInput).attributes('name')).toBe(
|
||||
'hook[url_variables][][value]',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders remove button', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findRemoveButton().props('icon')).toBe('remove');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::Attachments::BaseImporter do
|
||||
subject(:importer) { importer_class.new(project, client) }
|
||||
|
||||
let(:project) { instance_double(Project, id: 1) }
|
||||
let(:client) { instance_double(Gitlab::GithubImport::Client) }
|
||||
let(:importer_class) do
|
||||
Class.new(described_class) do
|
||||
private
|
||||
|
||||
def collection_method
|
||||
'test'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#each_object_to_import' do
|
||||
context 'with not implemented #collection interface' do
|
||||
it 'raises NotImplementedError' do
|
||||
expect { importer.each_object_to_import }
|
||||
.to raise_error(Gitlab::GithubImport::Exceptions::NotImplementedError, '#collection')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::Attachments::NotesImporter do
|
||||
subject(:importer) { described_class.new(project, client) }
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:client) { instance_double(Gitlab::GithubImport::Client) }
|
||||
|
||||
describe '#sequential_import', :clean_gitlab_redis_cache do
|
||||
let_it_be(:note_1) { create(:note, project: project) }
|
||||
let_it_be(:note_2) { create(:note, project: project) }
|
||||
let_it_be(:system_note) { create(:note, :system, project: project) }
|
||||
|
||||
let(:importer_stub) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
|
||||
let(:importer_attrs) { [instance_of(Gitlab::GithubImport::Representation::NoteText), project, client] }
|
||||
|
||||
it 'imports each project user note' do
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter).to receive(:new)
|
||||
.with(*importer_attrs).twice.and_return(importer_stub)
|
||||
expect(importer_stub).to receive(:execute).twice
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when note is already processed' do
|
||||
it "doesn't import this note" do
|
||||
importer.mark_as_imported(note_1)
|
||||
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter).to receive(:new)
|
||||
.with(*importer_attrs).once.and_return(importer_stub)
|
||||
expect(importer_stub).to receive(:execute).once
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#representation_class' do
|
||||
it { expect(importer.representation_class).to eq(Gitlab::GithubImport::Representation::NoteText) }
|
||||
end
|
||||
|
||||
describe '#importer_class' do
|
||||
it { expect(importer.importer_class).to eq(Gitlab::GithubImport::Importer::NoteAttachmentsImporter) }
|
||||
end
|
||||
|
||||
describe '#sidekiq_worker_class' do
|
||||
it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportNoteWorker) }
|
||||
end
|
||||
|
||||
describe '#collection_method' do
|
||||
it { expect(importer.collection_method).to eq(:note_attachments) }
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it { expect(importer.object_type).to eq(:note_attachment) }
|
||||
end
|
||||
|
||||
describe '#id_for_already_imported_cache' do
|
||||
let(:note) { build_stubbed(:note) }
|
||||
|
||||
it { expect(importer.id_for_already_imported_cache(note)).to eq(note.id) }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::Attachments::ReleasesImporter do
|
||||
subject(:importer) { described_class.new(project, client) }
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:client) { instance_double(Gitlab::GithubImport::Client) }
|
||||
|
||||
describe '#sequential_import', :clean_gitlab_redis_cache do
|
||||
let_it_be(:release_1) { create(:release, project: project) }
|
||||
let_it_be(:release_2) { create(:release, project: project) }
|
||||
|
||||
let(:importer_stub) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
|
||||
let(:importer_attrs) { [instance_of(Gitlab::GithubImport::Representation::NoteText), project, client] }
|
||||
|
||||
it 'imports each project release' do
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter).to receive(:new)
|
||||
.with(*importer_attrs).twice.and_return(importer_stub)
|
||||
expect(importer_stub).to receive(:execute).twice
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
|
||||
context 'when note is already processed' do
|
||||
it "doesn't import this release" do
|
||||
importer.mark_as_imported(release_1)
|
||||
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter).to receive(:new)
|
||||
.with(*importer_attrs).once.and_return(importer_stub)
|
||||
expect(importer_stub).to receive(:execute).once
|
||||
|
||||
importer.sequential_import
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#representation_class' do
|
||||
it { expect(importer.representation_class).to eq(Gitlab::GithubImport::Representation::NoteText) }
|
||||
end
|
||||
|
||||
describe '#importer_class' do
|
||||
it { expect(importer.importer_class).to eq(Gitlab::GithubImport::Importer::NoteAttachmentsImporter) }
|
||||
end
|
||||
|
||||
describe '#sidekiq_worker_class' do
|
||||
it { expect(importer.sidekiq_worker_class).to eq(Gitlab::GithubImport::Attachments::ImportReleaseWorker) }
|
||||
end
|
||||
|
||||
describe '#collection_method' do
|
||||
it { expect(importer.collection_method).to eq(:release_attachments) }
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it { expect(importer.object_type).to eq(:release_attachment) }
|
||||
end
|
||||
|
||||
describe '#id_for_already_imported_cache' do
|
||||
let(:release) { build_stubbed(:release) }
|
||||
|
||||
it { expect(importer.id_for_already_imported_cache(release)).to eq(release.id) }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::NoteAttachmentsImporter do
|
||||
subject(:importer) { described_class.new(note_text, project, client) }
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
|
||||
let(:doc_url) { 'https://github.com/nickname/public-test-repo/files/9020437/git-cheat-sheet.txt' }
|
||||
let(:image_url) { 'https://user-images.githubusercontent.com/6833842/0cf366b61ef2.jpeg' }
|
||||
let(:text) do
|
||||
<<-TEXT.strip
|
||||
Some text...
|
||||
|
||||
[special-doc](#{doc_url})
|
||||
![image.jpeg](#{image_url})
|
||||
TEXT
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let(:downloader_stub) { instance_double(Gitlab::GithubImport::AttachmentsDownloader) }
|
||||
let(:tmp_stub_doc) { Tempfile.create('attachment_download_test.txt') }
|
||||
let(:tmp_stub_image) { Tempfile.create('image.jpeg') }
|
||||
|
||||
before do
|
||||
allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(doc_url)
|
||||
.and_return(downloader_stub)
|
||||
allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(image_url)
|
||||
.and_return(downloader_stub)
|
||||
allow(downloader_stub).to receive(:perform).and_return(tmp_stub_doc, tmp_stub_image)
|
||||
allow(downloader_stub).to receive(:delete).twice
|
||||
|
||||
allow(UploadService).to receive(:new)
|
||||
.with(project, tmp_stub_doc, FileUploader).and_call_original
|
||||
allow(UploadService).to receive(:new)
|
||||
.with(project, tmp_stub_image, FileUploader).and_call_original
|
||||
end
|
||||
|
||||
context 'when importing release attachments' do
|
||||
let(:release) { create(:release, project: project, description: text) }
|
||||
let(:note_text) { Gitlab::GithubImport::Representation::NoteText.from_db_record(release) }
|
||||
|
||||
it 'updates release description with new attachment urls' do
|
||||
importer.execute
|
||||
|
||||
release.reload
|
||||
expect(release.description).to start_with("Some text...\n\n [special-doc](/uploads/")
|
||||
expect(release.description).to include('![image.jpeg](/uploads/')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when importing note attachments' do
|
||||
let(:note) { create(:note, project: project, note: text) }
|
||||
let(:note_text) { Gitlab::GithubImport::Representation::NoteText.from_db_record(note) }
|
||||
|
||||
it 'updates note text with new attachment urls' do
|
||||
importer.execute
|
||||
|
||||
note.reload
|
||||
expect(note.note).to start_with("Some text...\n\n [special-doc](/uploads/")
|
||||
expect(note.note).to include('![image.jpeg](/uploads/')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -160,6 +160,13 @@ RSpec.describe Gitlab::GithubImport::Importer::NoteImporter do
|
|||
|
||||
expect(project.notes.take).to be_valid
|
||||
end
|
||||
|
||||
# rubocop:disable RSpec/AnyInstanceOf
|
||||
it 'skips markdown field cache callback' do
|
||||
expect_any_instance_of(Note).not_to receive(:refresh_markdown_cache)
|
||||
importer.execute
|
||||
end
|
||||
# rubocop:enable RSpec/AnyInstanceOf
|
||||
end
|
||||
|
||||
describe '#find_noteable_id' do
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter do
|
||||
subject(:importer) { described_class.new(release_attachments, project, client) }
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
let(:release) { create(:release, project: project, description: description) }
|
||||
let(:release_attachments) do
|
||||
Gitlab::GithubImport::Representation::ReleaseAttachments
|
||||
.from_json_hash(release_db_id: release.id, description: release.description)
|
||||
end
|
||||
|
||||
let(:doc_url) { 'https://github.com/nickname/public-test-repo/files/9020437/git-cheat-sheet.txt' }
|
||||
let(:image_url) { 'https://user-images.githubusercontent.com/6833842/0cf366b61ef2.jpeg' }
|
||||
let(:description) do
|
||||
<<-TEXT.strip
|
||||
Some text...
|
||||
|
||||
[special-doc](#{doc_url})
|
||||
![image.jpeg](#{image_url})
|
||||
TEXT
|
||||
end
|
||||
|
||||
describe '#execute' do
|
||||
let(:downloader_stub) { instance_double(Gitlab::GithubImport::AttachmentsDownloader) }
|
||||
let(:tmp_stub_doc) { Tempfile.create('attachment_download_test.txt') }
|
||||
let(:tmp_stub_image) { Tempfile.create('image.jpeg') }
|
||||
|
||||
context 'when importing doc attachment' do
|
||||
before do
|
||||
allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(doc_url)
|
||||
.and_return(downloader_stub)
|
||||
allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(image_url)
|
||||
.and_return(downloader_stub)
|
||||
allow(downloader_stub).to receive(:perform).and_return(tmp_stub_doc, tmp_stub_image)
|
||||
allow(downloader_stub).to receive(:delete).twice
|
||||
|
||||
allow(UploadService).to receive(:new)
|
||||
.with(project, tmp_stub_doc, FileUploader).and_call_original
|
||||
allow(UploadService).to receive(:new)
|
||||
.with(project, tmp_stub_image, FileUploader).and_call_original
|
||||
end
|
||||
|
||||
it 'updates release description with new attachment url' do
|
||||
importer.execute
|
||||
|
||||
release.reload
|
||||
expect(release.description).to start_with("Some text...\n\n [special-doc](/uploads/")
|
||||
expect(release.description).to include('![image.jpeg](/uploads/')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter do
|
||||
subject { described_class.new(project, client) }
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:client) { instance_double(Gitlab::GithubImport::Client) }
|
||||
|
||||
describe '#each_object_to_import', :clean_gitlab_redis_cache do
|
||||
let!(:release_1) { create(:release, project: project) }
|
||||
let!(:release_2) { create(:release, project: project) }
|
||||
|
||||
it 'iterates each project release' do
|
||||
list = []
|
||||
subject.each_object_to_import do |object|
|
||||
list << object
|
||||
end
|
||||
expect(list).to contain_exactly(release_1, release_2)
|
||||
end
|
||||
|
||||
context 'when release is already processed' do
|
||||
it "doesn't process this release" do
|
||||
subject.mark_as_imported(release_1)
|
||||
|
||||
list = []
|
||||
subject.each_object_to_import do |object|
|
||||
list << object
|
||||
end
|
||||
expect(list).to contain_exactly(release_2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#representation_class' do
|
||||
it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::ReleaseAttachments) }
|
||||
end
|
||||
|
||||
describe '#importer_class' do
|
||||
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter) }
|
||||
end
|
||||
|
||||
describe '#sidekiq_worker_class' do
|
||||
it { expect(subject.sidekiq_worker_class).to eq(Gitlab::GithubImport::ImportReleaseAttachmentsWorker) }
|
||||
end
|
||||
|
||||
describe '#collection_method' do
|
||||
it { expect(subject.collection_method).to eq(:release_attachments) }
|
||||
end
|
||||
|
||||
describe '#object_type' do
|
||||
it { expect(subject.object_type).to eq(:release_attachment) }
|
||||
end
|
||||
|
||||
describe '#id_for_already_imported_cache' do
|
||||
let(:release) { build_stubbed(:release) }
|
||||
|
||||
it { expect(subject.id_for_already_imported_cache(release)).to eq(release.id) }
|
||||
end
|
||||
|
||||
describe '#object_representation' do
|
||||
let(:release) { build_stubbed(:release) }
|
||||
|
||||
it 'returns release attachments representation' do
|
||||
representation = subject.object_representation(release)
|
||||
|
||||
expect(representation.class).to eq subject.representation_class
|
||||
expect(representation.release_db_id).to eq release.id
|
||||
expect(representation.description).to eq release.description
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Representation::NoteText do
|
||||
shared_examples 'a Note text data' do |match_record_type|
|
||||
it 'returns an instance of ReleaseAttachments' do
|
||||
expect(representation).to be_an_instance_of(described_class)
|
||||
end
|
||||
|
||||
it 'includes record DB id' do
|
||||
expect(representation.record_db_id).to eq 42
|
||||
end
|
||||
|
||||
it 'includes record type' do
|
||||
expect(representation.record_type).to eq match_record_type
|
||||
end
|
||||
|
||||
it 'includes note text' do
|
||||
expect(representation.text).to eq 'Some text here..'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_db_record' do
|
||||
context 'with Release' do
|
||||
let(:record) { build_stubbed(:release, id: 42, description: 'Some text here..') }
|
||||
|
||||
it_behaves_like 'a Note text data', 'Release' do
|
||||
let(:representation) { described_class.from_db_record(record) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with Note' do
|
||||
let(:record) { build_stubbed(:note, id: 42, note: 'Some text here..') }
|
||||
|
||||
it_behaves_like 'a Note text data', 'Note' do
|
||||
let(:representation) { described_class.from_db_record(record) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_json_hash' do
|
||||
it_behaves_like 'a Note text data', 'Release' do
|
||||
let(:hash) do
|
||||
{
|
||||
'record_db_id' => 42,
|
||||
'record_type' => 'Release',
|
||||
'text' => 'Some text here..'
|
||||
}
|
||||
end
|
||||
|
||||
let(:representation) { described_class.from_json_hash(hash) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#github_identifiers' do
|
||||
it 'returns a hash with needed identifiers' do
|
||||
record_id = rand(100)
|
||||
representation = described_class.new(record_db_id: record_id, text: 'text')
|
||||
|
||||
expect(representation.github_identifiers).to eq({ db_id: record_id })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Representation::ReleaseAttachments do
|
||||
shared_examples 'a Release attachments data' do
|
||||
it 'returns an instance of ReleaseAttachments' do
|
||||
expect(representation).to be_an_instance_of(described_class)
|
||||
end
|
||||
|
||||
it 'includes release DB id' do
|
||||
expect(representation.release_db_id).to eq 42
|
||||
end
|
||||
|
||||
it 'includes release description' do
|
||||
expect(representation.description).to eq 'Some text here..'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_db_record' do
|
||||
let(:release) { build_stubbed(:release, id: 42, description: 'Some text here..') }
|
||||
|
||||
it_behaves_like 'a Release attachments data' do
|
||||
let(:representation) { described_class.from_db_record(release) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.from_json_hash' do
|
||||
it_behaves_like 'a Release attachments data' do
|
||||
let(:hash) do
|
||||
{
|
||||
'release_db_id' => 42,
|
||||
'description' => 'Some text here..'
|
||||
}
|
||||
end
|
||||
|
||||
let(:representation) { described_class.from_json_hash(hash) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#github_identifiers' do
|
||||
it 'returns a hash with needed identifiers' do
|
||||
release_id = rand(100)
|
||||
representation = described_class.new(release_db_id: release_id, description: 'text')
|
||||
|
||||
expect(representation.github_identifiers).to eq({ db_id: release_id })
|
||||
end
|
||||
end
|
||||
end
|
|
@ -258,6 +258,8 @@ RSpec.describe 'Every Sidekiq worker' do
|
|||
'GitGarbageCollectWorker' => false,
|
||||
'Gitlab::GithubImport::AdvanceStageWorker' => 3,
|
||||
'Gitlab::GithubImport::ImportReleaseAttachmentsWorker' => 5,
|
||||
'Gitlab::GithubImport::Attachments::ImportReleaseWorker' => 5,
|
||||
'Gitlab::GithubImport::Attachments::ImportNoteWorker' => 5,
|
||||
'Gitlab::GithubImport::ImportDiffNoteWorker' => 5,
|
||||
'Gitlab::GithubImport::ImportIssueWorker' => 5,
|
||||
'Gitlab::GithubImport::ImportIssueEventWorker' => 5,
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Attachments::ImportNoteWorker do
|
||||
subject(:worker) { described_class.new }
|
||||
|
||||
describe '#import' do
|
||||
let(:import_state) { create(:import_state, :started) }
|
||||
|
||||
let(:project) do
|
||||
instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state)
|
||||
end
|
||||
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
let(:importer) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
|
||||
|
||||
let(:note_hash) do
|
||||
{
|
||||
'record_db_id' => rand(100),
|
||||
'record_type' => 'Note',
|
||||
'text' => <<-TEXT
|
||||
Some text...
|
||||
|
||||
![special-image](https://user-images.githubusercontent.com...)
|
||||
TEXT
|
||||
}
|
||||
end
|
||||
|
||||
it 'imports an release attachments' do
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter)
|
||||
.to receive(:new)
|
||||
.with(
|
||||
an_instance_of(Gitlab::GithubImport::Representation::NoteText),
|
||||
project,
|
||||
client
|
||||
)
|
||||
.and_return(importer)
|
||||
|
||||
expect(importer).to receive(:execute)
|
||||
|
||||
expect(Gitlab::GithubImport::ObjectCounter)
|
||||
.to receive(:increment)
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, note_hash)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::Attachments::ImportReleaseWorker do
|
||||
subject(:worker) { described_class.new }
|
||||
|
||||
describe '#import' do
|
||||
let(:import_state) { create(:import_state, :started) }
|
||||
|
||||
let(:project) do
|
||||
instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state)
|
||||
end
|
||||
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
let(:importer) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
|
||||
|
||||
let(:note_hash) do
|
||||
{
|
||||
'record_db_id' => rand(100),
|
||||
'record_type' => 'Release',
|
||||
'text' => <<-TEXT
|
||||
Some text...
|
||||
|
||||
![special-image](https://user-images.githubusercontent.com...)
|
||||
TEXT
|
||||
}
|
||||
end
|
||||
|
||||
it 'imports an release attachments' do
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter)
|
||||
.to receive(:new)
|
||||
.with(
|
||||
an_instance_of(Gitlab::GithubImport::Representation::NoteText),
|
||||
project,
|
||||
client
|
||||
)
|
||||
.and_return(importer)
|
||||
|
||||
expect(importer).to receive(:execute)
|
||||
|
||||
expect(Gitlab::GithubImport::ObjectCounter)
|
||||
.to receive(:increment)
|
||||
.and_call_original
|
||||
|
||||
worker.import(project, client, note_hash)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ RSpec.describe Gitlab::GithubImport::ImportReleaseAttachmentsWorker do
|
|||
end
|
||||
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
let(:importer) { instance_double('Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter') }
|
||||
let(:importer) { instance_double('Gitlab::GithubImport::Importer::NoteAttachmentsImporter') }
|
||||
|
||||
let(:release_hash) do
|
||||
{
|
||||
|
@ -27,10 +27,10 @@ RSpec.describe Gitlab::GithubImport::ImportReleaseAttachmentsWorker do
|
|||
end
|
||||
|
||||
it 'imports an issue event' do
|
||||
expect(Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter)
|
||||
expect(Gitlab::GithubImport::Importer::NoteAttachmentsImporter)
|
||||
.to receive(:new)
|
||||
.with(
|
||||
an_instance_of(Gitlab::GithubImport::Representation::ReleaseAttachments),
|
||||
an_instance_of(Gitlab::GithubImport::Representation::NoteText),
|
||||
project,
|
||||
client
|
||||
)
|
||||
|
|
|
@ -10,26 +10,32 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
|
|||
let(:feature_flag_state) { [group] }
|
||||
|
||||
describe '#import' do
|
||||
let(:importer) { instance_double('Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter') }
|
||||
let(:releases_importer) { instance_double('Gitlab::GithubImport::Importer::Attachments::ReleasesImporter') }
|
||||
let(:notes_importer) { instance_double('Gitlab::GithubImport::Importer::Attachments::NotesImporter') }
|
||||
let(:client) { instance_double('Gitlab::GithubImport::Client') }
|
||||
let(:releases_waiter) { Gitlab::JobWaiter.new(2, '123') }
|
||||
let(:notes_waiter) { Gitlab::JobWaiter.new(3, '234') }
|
||||
|
||||
before do
|
||||
stub_feature_flags(github_importer_attachments_import: feature_flag_state)
|
||||
end
|
||||
|
||||
it 'imports release attachments' do
|
||||
waiter = Gitlab::JobWaiter.new(2, '123')
|
||||
|
||||
expect(Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter)
|
||||
expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter)
|
||||
.to receive(:new)
|
||||
.with(project, client)
|
||||
.and_return(importer)
|
||||
.and_return(releases_importer)
|
||||
expect(releases_importer).to receive(:execute).and_return(releases_waiter)
|
||||
|
||||
expect(importer).to receive(:execute).and_return(waiter)
|
||||
expect(Gitlab::GithubImport::Importer::Attachments::NotesImporter)
|
||||
.to receive(:new)
|
||||
.with(project, client)
|
||||
.and_return(notes_importer)
|
||||
expect(notes_importer).to receive(:execute).and_return(notes_waiter)
|
||||
|
||||
expect(Gitlab::GithubImport::AdvanceStageWorker)
|
||||
.to receive(:perform_async)
|
||||
.with(project.id, { '123' => 2 }, :protected_branches)
|
||||
.with(project.id, { '123' => 2, '234' => 3 }, :protected_branches)
|
||||
|
||||
worker.import(client, project)
|
||||
end
|
||||
|
@ -38,7 +44,8 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
|
|||
let(:feature_flag_state) { false }
|
||||
|
||||
it 'skips release attachments import and calls next stage' do
|
||||
expect(Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter).not_to receive(:new)
|
||||
expect(Gitlab::GithubImport::Importer::Attachments::ReleasesImporter).not_to receive(:new)
|
||||
expect(Gitlab::GithubImport::Importer::Attachments::NotesImporter).not_to receive(:new)
|
||||
expect(Gitlab::GithubImport::AdvanceStageWorker)
|
||||
.to receive(:perform_async).with(project.id, {}, :protected_branches)
|
||||
|
||||
|
|
Loading…
Reference in New Issue