Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e9ae9c5f2e
commit
f7e83434e3
23 changed files with 123 additions and 89 deletions
|
@ -39,6 +39,7 @@ export default {
|
|||
assigneeUsername,
|
||||
search,
|
||||
milestoneTitle,
|
||||
iterationId,
|
||||
types,
|
||||
weight,
|
||||
epicId,
|
||||
|
@ -83,6 +84,13 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
if (iterationId) {
|
||||
filteredSearchValue.push({
|
||||
type: 'iteration',
|
||||
value: { data: iterationId, operator: '=' },
|
||||
});
|
||||
}
|
||||
|
||||
if (weight) {
|
||||
filteredSearchValue.push({
|
||||
type: 'weight',
|
||||
|
@ -118,6 +126,13 @@ export default {
|
|||
});
|
||||
}
|
||||
|
||||
if (this.filterParams['not[iteration_id]']) {
|
||||
filteredSearchValue.push({
|
||||
type: 'iteration_id',
|
||||
value: { data: this.filterParams['not[iteration_id]'], operator: '!=' },
|
||||
});
|
||||
}
|
||||
|
||||
if (this.filterParams['not[weight]']) {
|
||||
filteredSearchValue.push({
|
||||
type: 'weight',
|
||||
|
@ -179,8 +194,8 @@ export default {
|
|||
weight,
|
||||
epicId,
|
||||
myReactionEmoji,
|
||||
iterationId,
|
||||
} = this.filterParams;
|
||||
|
||||
let notParams = {};
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
|
||||
|
@ -194,6 +209,7 @@ export default {
|
|||
'not[weight]': this.filterParams.not.weight,
|
||||
'not[epic_id]': this.filterParams.not.epicId,
|
||||
'not[my_reaction_emoji]': this.filterParams.not.myReactionEmoji,
|
||||
'not[iteration_id]': this.filterParams.not.iterationId,
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
|
@ -205,6 +221,7 @@ export default {
|
|||
'label_name[]': labelName,
|
||||
assignee_username: assigneeUsername,
|
||||
milestone_title: milestoneTitle,
|
||||
iteration_id: iterationId,
|
||||
search,
|
||||
types,
|
||||
weight,
|
||||
|
@ -261,6 +278,9 @@ export default {
|
|||
case 'milestone_title':
|
||||
filterParams.milestoneTitle = filter.value.data;
|
||||
break;
|
||||
case 'iteration':
|
||||
filterParams.iterationId = filter.value.data;
|
||||
break;
|
||||
case 'weight':
|
||||
filterParams.weight = filter.value.data;
|
||||
break;
|
||||
|
|
|
@ -12,6 +12,7 @@ import { __ } from '~/locale';
|
|||
import {
|
||||
DEFAULT_MILESTONES_GRAPHQL,
|
||||
TOKEN_TITLE_MY_REACTION,
|
||||
OPERATOR_IS_AND_IS_NOT,
|
||||
} from '~/vue_shared/components/filtered_search_bar/constants';
|
||||
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
|
||||
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
|
||||
|
@ -35,8 +36,6 @@ export default {
|
|||
issue: __('Issue'),
|
||||
milestone: __('Milestone'),
|
||||
weight: __('Weight'),
|
||||
is: __('is'),
|
||||
isNot: __('is not'),
|
||||
},
|
||||
components: { BoardFilteredSearch },
|
||||
inject: ['isSignedIn'],
|
||||
|
@ -62,8 +61,6 @@ export default {
|
|||
tokensCE() {
|
||||
const {
|
||||
label,
|
||||
is,
|
||||
isNot,
|
||||
author,
|
||||
assignee,
|
||||
issue,
|
||||
|
@ -84,10 +81,7 @@ export default {
|
|||
icon: 'user',
|
||||
title: assignee,
|
||||
type: 'assignee_username',
|
||||
operators: [
|
||||
{ value: '=', description: is },
|
||||
{ value: '!=', description: isNot },
|
||||
],
|
||||
operators: OPERATOR_IS_AND_IS_NOT,
|
||||
token: AuthorToken,
|
||||
unique: true,
|
||||
fetchAuthors,
|
||||
|
@ -97,10 +91,7 @@ export default {
|
|||
icon: 'pencil',
|
||||
title: author,
|
||||
type: 'author_username',
|
||||
operators: [
|
||||
{ value: '=', description: is },
|
||||
{ value: '!=', description: isNot },
|
||||
],
|
||||
operators: OPERATOR_IS_AND_IS_NOT,
|
||||
symbol: '@',
|
||||
token: AuthorToken,
|
||||
unique: true,
|
||||
|
@ -111,10 +102,7 @@ export default {
|
|||
icon: 'labels',
|
||||
title: label,
|
||||
type: 'label_name',
|
||||
operators: [
|
||||
{ value: '=', description: is },
|
||||
{ value: '!=', description: isNot },
|
||||
],
|
||||
operators: OPERATOR_IS_AND_IS_NOT,
|
||||
token: LabelToken,
|
||||
unique: false,
|
||||
symbol: '~',
|
||||
|
|
|
@ -373,7 +373,6 @@ export default {
|
|||
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
|
||||
|
||||
const { fullPath, fullBoardId, boardType, filterParams } = state;
|
||||
|
||||
const variables = {
|
||||
fullPath,
|
||||
boardId: fullBoardId,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { GlDropdownDivider, GlDropdownSectionHeader, GlFilteredSearchSuggestion } from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { __ } from '~/locale';
|
||||
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
|
||||
import { formatDate } from '~/lib/utils/datetime_utility';
|
||||
|
@ -43,7 +42,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
getActiveIteration(iterations, data) {
|
||||
return iterations.find((iteration) => this.getValue(iteration) === data);
|
||||
return iterations.find((iteration) => iteration.id === data);
|
||||
},
|
||||
groupIterationsByCadence(iterations) {
|
||||
const cadences = [];
|
||||
|
@ -80,9 +79,6 @@ export default {
|
|||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getValue(iteration) {
|
||||
return String(getIdFromGraphQLId(iteration.id));
|
||||
},
|
||||
/**
|
||||
* TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/344619
|
||||
* This method also exists as a utility function in ee/../iterations/utils.js
|
||||
|
@ -125,7 +121,7 @@ export default {
|
|||
<gl-filtered-search-suggestion
|
||||
v-for="iteration in cadence.iterations"
|
||||
:key="iteration.id"
|
||||
:value="getValue(iteration)"
|
||||
:value="iteration.id"
|
||||
>
|
||||
{{ iteration.title }}
|
||||
<div v-if="glFeatures.iterationCadences" class="gl-text-gray-400">
|
||||
|
|
|
@ -179,6 +179,8 @@ export default {
|
|||
|
||||
document.addEventListener('mousedown', this.handleDocumentMousedown);
|
||||
document.addEventListener('click', this.handleDocumentClick);
|
||||
|
||||
this.updateLabelsSetState();
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('mousedown', this.handleDocumentMousedown);
|
||||
|
|
|
@ -341,3 +341,5 @@ module NotesActions
|
|||
noteable.discussions_rendered_on_frontend?
|
||||
end
|
||||
end
|
||||
|
||||
NotesActions.prepend_mod_with('NotesActions')
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class PropagateServiceTemplate
|
||||
include PropagateService
|
||||
|
||||
def propagate
|
||||
# TODO: Remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/335178
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
module PropagateService
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
BATCH_SIZE = 10_000
|
||||
|
||||
class_methods do
|
||||
def propagate(integration)
|
||||
new(integration).propagate
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(integration)
|
||||
@integration = integration
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :integration
|
||||
|
||||
def create_integration_for_projects_without_integration
|
||||
propagate_integrations(
|
||||
Project.without_integration(integration),
|
||||
PropagateIntegrationProjectWorker
|
||||
)
|
||||
end
|
||||
|
||||
def propagate_integrations(relation, worker_class)
|
||||
relation.each_batch(of: BATCH_SIZE) do |records|
|
||||
min_id, max_id = records.pick("MIN(#{relation.table_name}.id), MAX(#{relation.table_name}.id)")
|
||||
worker_class.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,8 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class PropagateIntegrationService
|
||||
include PropagateService
|
||||
module Integrations
|
||||
class PropagateService
|
||||
BATCH_SIZE = 10_000
|
||||
|
||||
def initialize(integration)
|
||||
@integration = integration
|
||||
end
|
||||
|
||||
def propagate
|
||||
if integration.instance_level?
|
||||
|
@ -16,8 +20,21 @@ module Admin
|
|||
end
|
||||
end
|
||||
|
||||
def self.propagate(integration)
|
||||
new(integration).propagate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :integration
|
||||
|
||||
def create_integration_for_projects_without_integration
|
||||
propagate_integrations(
|
||||
Project.without_integration(integration),
|
||||
PropagateIntegrationProjectWorker
|
||||
)
|
||||
end
|
||||
|
||||
def update_inherited_integrations
|
||||
propagate_integrations(
|
||||
Integration.by_type(integration.type).inherit_from_id(integration.id),
|
||||
|
@ -52,5 +69,12 @@ module Admin
|
|||
PropagateIntegrationProjectWorker
|
||||
)
|
||||
end
|
||||
|
||||
def propagate_integrations(relation, worker_class)
|
||||
relation.each_batch(of: BATCH_SIZE) do |records|
|
||||
min_id, max_id = records.pick("MIN(#{relation.table_name}.id), MAX(#{relation.table_name}.id)")
|
||||
worker_class.perform_async(integration.id, min_id, max_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
10
app/services/integrations/propagate_template_service.rb
Normal file
10
app/services/integrations/propagate_template_service.rb
Normal file
|
@ -0,0 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Integrations
|
||||
# TODO: Remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/335178
|
||||
class PropagateTemplateService
|
||||
def self.propagate(_integration)
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,7 +5,7 @@
|
|||
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
|
||||
- toggle_wip_link_start = '<a href="" class="js-toggle-wip">'
|
||||
- toggle_wip_link_end = '</a>'
|
||||
- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe } ).html_safe
|
||||
- add_wip_text = (_('%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it\'s ready.') % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft:</code>'.html_safe } ).html_safe
|
||||
- remove_wip_text = (_('%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it\'s ready.' ) % { link_start: toggle_wip_link_start, link_end: toggle_wip_link_end, draft_snippet: '<code>Draft</code>'.html_safe } ).html_safe
|
||||
|
||||
%div{ class: div_class }
|
||||
|
|
|
@ -12,6 +12,6 @@ class PropagateIntegrationWorker
|
|||
idempotent!
|
||||
|
||||
def perform(integration_id)
|
||||
Admin::PropagateIntegrationService.propagate(Integration.find(integration_id))
|
||||
::Integrations::PropagateService.propagate(Integration.find(integration_id))
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ class PropagateServiceTemplateWorker # rubocop:disable Scalability/IdempotentWor
|
|||
def perform(template_id)
|
||||
return unless try_obtain_lease_for(template_id)
|
||||
|
||||
Admin::PropagateServiceTemplate.propagate(Integration.find_by_id(template_id))
|
||||
::Integrations::PropagateTemplateService.propagate(Integration.find_by_id(template_id))
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -97,7 +97,7 @@ From there, you can see the following actions:
|
|||
- 2FA enforcement or grace period changed.
|
||||
- Roles allowed to create project changed.
|
||||
- Group CI/CD variable added, removed, or protected status changed. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30857) in GitLab 13.3.
|
||||
- Compliance framework created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.6.
|
||||
- Compliance framework created, updated, or deleted. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340649) in GitLab 14.5.
|
||||
|
||||
Group events can also be accessed via the [Group Audit Events API](../api/audit_events.md#group-audit-events)
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ Users are considered inactive in LDAP when they:
|
|||
|
||||
Status is checked for all LDAP users:
|
||||
|
||||
- When signing in using any authentication provider.
|
||||
- When signing in using any authentication provider. [In GitLab 14.4 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/343298), status was
|
||||
checked only when signing in using LDAP directly.
|
||||
- Once per hour for active web sessions or Git requests using tokens or SSH keys.
|
||||
- When performing Git over HTTP requests using LDAP username and password.
|
||||
- Once per day during [User Sync](ldap_synchronization.md#user-sync).
|
||||
|
|
|
@ -180,3 +180,11 @@ end
|
|||
NOTE:
|
||||
This example is unlikely in GitLab, because we usually look up the parent models to perform
|
||||
permission checks.
|
||||
|
||||
## A note on `dependent: :destroy` and `dependent: :nullify`
|
||||
|
||||
We considered using these Rails features as an alternative to foreign keys but there are several problems which include:
|
||||
|
||||
1. These run on a different connection in the context of a transaction [which we do not allow](multiple_databases.md#removing-cross-database-transactions).
|
||||
1. These can lead to severe performance degredation as we load all records from PostgreSQL, loop over them in Ruby, and call individual `DELETE` queries.
|
||||
1. These can miss data as they only cover the case when the `destroy` method is called directly on the model. There are other cases including `delete_all` and cascading deletes from another parent table that could mean these are missed.
|
||||
|
|
|
@ -557,6 +557,22 @@ Don't hesitate to reach out to the
|
|||
[sharding group](https://about.gitlab.com/handbook/engineering/development/enablement/sharding/)
|
||||
for advice.
|
||||
|
||||
##### Avoid `dependent: :nullify` and `dependent: :destroy` across databases
|
||||
|
||||
There may be cases where we want to use `dependent: :nullify` or `dependent: :destroy`
|
||||
across databases. This is technically possible, but it's problematic because
|
||||
these hooks run in the context of an outer transaction from the call to
|
||||
`#destroy`, which creates a cross-database transaction and we are trying to
|
||||
avoid that. Cross-database transactions caused this way could lead to confusing
|
||||
outcomes when we switch to decomposed, because now you have some queries
|
||||
happening outside the transaction and they may be partially applied while the
|
||||
outer transaction fails, which could lead to surprising bugs.
|
||||
|
||||
If you need to do some cleanup after a `destroy` you will need to choose
|
||||
from some of the options above. If all you need to do is cleanup the child
|
||||
records themselves from PostgreSQL then you could consider using ["loose foreign
|
||||
keys"](loose_foreign_keys.md).
|
||||
|
||||
## `config/database.yml`
|
||||
|
||||
GitLab will support running multiple databases in the future, for example to [separate tables for the continuous integration features](https://gitlab.com/groups/gitlab-org/-/epics/6167) from the main database. In order to prepare for this change, we [validate the structure of the configuration](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67877) in `database.yml` to ensure that only known databases are used.
|
||||
|
@ -603,3 +619,15 @@ production:
|
|||
database: gitlabhq_production_ci
|
||||
...
|
||||
```
|
||||
|
||||
## Foreign keys that cross databases
|
||||
|
||||
There are many places where we use foreign keys that reference across the two
|
||||
databases. This is not possible to do with two separate PostgreSQL
|
||||
databases, so we need to replicate the behavior we get from PostgreSQL in a
|
||||
performant way. We can't, and shouldn't, try to replicate the data guarantees
|
||||
given by PostgreSQL which prevent creating invalid references, but we still need a
|
||||
way to replace cascading deletes so we don't end up with orphaned data
|
||||
or records that point to nowhere, which might lead to bugs. As such we created
|
||||
["loose foreign keys"](loose_foreign_keys.md) which is an asynchronous
|
||||
process of cleaning up orphaned records.
|
||||
|
|
|
@ -15,15 +15,18 @@ For the latest updates, check the [Tasks Roadmap](https://gitlab.com/groups/gitl
|
|||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available,
|
||||
ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `work_items`.
|
||||
On GitLab.com, this feature is not available.
|
||||
The feature is not ready for production use.
|
||||
|
||||
Use tasks to track steps needed for the [issue](project/issues/index.md) to be closed.
|
||||
|
||||
When planning an issue, you need a way to capture and break down technical
|
||||
requirements or steps necessary to complete it. An issue with tasks is better defined,
|
||||
requirements or steps necessary to complete it. An issue with related tasks is better defined,
|
||||
and so you can provide a more accurate issue weight and completion criteria.
|
||||
|
||||
To see the roadmap for migrating issues and [epics](group/epics/index.md)
|
||||
Tasks are a type of work item, a step towards [default issue types](https://gitlab.com/gitlab-org/gitlab/-/issues/323404)
|
||||
in GitLab.
|
||||
For the roadmap of migrating issues and [epics](group/epics/index.md)
|
||||
to work items and adding custom work item types, visit
|
||||
[epic 6033](https://gitlab.com/groups/gitlab-org/-/epics/6033) or
|
||||
[Plan direction page](https://about.gitlab.com/direction/plan/).
|
||||
|
@ -31,4 +34,4 @@ to work items and adding custom work item types, visit
|
|||
## View a task
|
||||
|
||||
The only way to view a task is to open it with a deep link,
|
||||
for example: `/<group_name>/<project_name>/-/work_item/1`
|
||||
for example: `/<group_name>/<project_name>/-/work_item/1`.
|
||||
|
|
|
@ -717,7 +717,7 @@ msgstr ""
|
|||
msgid "%{link_start}Remove the %{draft_snippet} prefix%{link_end} from the title to allow this merge request to be merged when it's ready."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request that is a work in progress from being merged before it's ready."
|
||||
msgid "%{link_start}Start the title with %{draft_snippet}%{link_end} to prevent a merge request draft from merging before it's ready."
|
||||
msgstr ""
|
||||
|
||||
msgid "%{link_start}What information does GitLab Inc. collect?%{link_end}"
|
||||
|
|
|
@ -46,8 +46,8 @@ RSpec.describe 'Merge request > User sees draft help message' do
|
|||
'It looks like you have some draft commits in this branch'
|
||||
)
|
||||
expect(page).to have_text(
|
||||
"Start the title with Draft: to prevent a merge request that is a \
|
||||
work in progress from being merged before it's ready."
|
||||
"Start the title with Draft: to prevent a merge request draft \
|
||||
from merging before it's ready."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -123,6 +123,7 @@ describe('BoardFilteredSearch', () => {
|
|||
{ type: 'milestone_title', value: { data: 'New Milestone', operator: '=' } },
|
||||
{ type: 'types', value: { data: 'INCIDENT', operator: '=' } },
|
||||
{ type: 'weight', value: { data: '2', operator: '=' } },
|
||||
{ type: 'iteration', value: { data: '3341', operator: '=' } },
|
||||
];
|
||||
jest.spyOn(urlUtility, 'updateHistory');
|
||||
findFilteredSearch().vm.$emit('onFilter', mockFilters);
|
||||
|
@ -131,7 +132,7 @@ describe('BoardFilteredSearch', () => {
|
|||
title: '',
|
||||
replace: true,
|
||||
url:
|
||||
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&types=INCIDENT&weight=2',
|
||||
'http://test.host/?author_username=root&label_name[]=label&label_name[]=label2&milestone_title=New+Milestone&iteration_id=3341&types=INCIDENT&weight=2',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Admin::PropagateIntegrationService do
|
||||
RSpec.describe Integrations::PropagateService do
|
||||
describe '.propagate' do
|
||||
include JiraServiceHelper
|
||||
|
|
@ -18,7 +18,7 @@ RSpec.describe PropagateIntegrationWorker do
|
|||
end
|
||||
|
||||
it 'calls the propagate service with the integration' do
|
||||
expect(Admin::PropagateIntegrationService).to receive(:propagate).with(integration)
|
||||
expect(Integrations::PropagateService).to receive(:propagate).with(integration)
|
||||
|
||||
subject.perform(integration.id)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue