Skip to content

Commit efc9c53

Browse files
author
GitLab Bot
committed
Add latest changes from gitlab-org/gitlab@master
1 parent 658dd27 commit efc9c53

File tree

87 files changed

+6578
-8814
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+6578
-8814
lines changed

.rubocop_todo/layout/first_hash_element_indentation.yml

-24
This file was deleted.

.rubocop_todo/rspec/feature_category.yml

-1
Original file line numberDiff line numberDiff line change
@@ -3091,7 +3091,6 @@ RSpec/FeatureCategory:
30913091
- 'spec/models/ci/build_trace_chunks/redis_spec.rb'
30923092
- 'spec/models/ci/group_spec.rb'
30933093
- 'spec/models/ci/instance_variable_spec.rb'
3094-
- 'spec/models/ci/persistent_ref_spec.rb'
30953094
- 'spec/models/ci/pipeline_schedule_variable_spec.rb'
30963095
- 'spec/models/ci/resource_spec.rb'
30973096
- 'spec/models/ci/unit_test_failure_spec.rb'

GITALY_SERVER_VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6a69fdde9d24d290cc33e48d7062a57da2f0c508
1+
1365200de8ffffff9a46f274e58fbd3956d16638

app/assets/javascripts/boards/boards_util.js

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { sortBy, cloneDeep, find, inRange } from 'lodash';
22
import {
33
TYPENAME_BOARD,
4+
TYPENAME_CUSTOM_FIELD,
5+
TYPENAME_CUSTOM_FIELD_SELECT_OPTION,
46
TYPENAME_ITERATION,
57
TYPENAME_MILESTONE,
68
TYPENAME_USER,
@@ -311,8 +313,10 @@ const parseFilters = (filters) => {
311313
* @param {Object} objParam.filterInfo - data on filters such as how to transform filter value, if filter can be negated, etc.
312314
* @param {Object} objParam.filterFields - data on what filters are available for given issuableType (based on GraphQL schema)
313315
*/
314-
export const filterVariables = ({ filters, issuableType, filterInfo, filterFields }) =>
315-
parseFilters(filters)
316+
export const filterVariables = ({ filters, issuableType, filterInfo, filterFields, options }) => {
317+
const customFields = new Map();
318+
319+
return parseFilters(filters)
316320
.map(([k, v, negated]) => {
317321
// for legacy reasons, some filters need to be renamed to correct GraphQL fields.
318322
const remapAvailable = filterInfo[k]?.remap;
@@ -321,6 +325,10 @@ export const filterVariables = ({ filters, issuableType, filterInfo, filterField
321325
return [remappedKey, v, negated];
322326
})
323327
.filter(([k, , negated]) => {
328+
if (k.startsWith('custom-field') && options.hasCustomFieldsFeature) {
329+
return true;
330+
}
331+
324332
// remove unsupported filters (+ check if the filters support negation)
325333
const supported = filterFields[issuableType].includes(k);
326334
if (supported) {
@@ -336,6 +344,32 @@ export const filterVariables = ({ filters, issuableType, filterInfo, filterField
336344

337345
return [k, newVal, negated];
338346
})
347+
.map(([k, v, negated]) => {
348+
let newK = k;
349+
let newV = v;
350+
if (k.startsWith('custom-field') && options.hasCustomFieldsFeature) {
351+
let customFieldId = k.replace('custom-field[', '').replace(']', '');
352+
customFieldId = convertToGraphQLId(TYPENAME_CUSTOM_FIELD, customFieldId);
353+
354+
const existingSelectedOptions = customFields.has(customFieldId)
355+
? customFields.get(customFieldId)
356+
: [];
357+
358+
const selectedOptionIds = [...existingSelectedOptions];
359+
selectedOptionIds.push(convertToGraphQLId(TYPENAME_CUSTOM_FIELD_SELECT_OPTION, v));
360+
customFields.set(customFieldId, selectedOptionIds);
361+
362+
newV = [
363+
{
364+
customFieldId,
365+
selectedOptionIds,
366+
},
367+
];
368+
newK = 'customField';
369+
}
370+
371+
return [newK, newV, negated];
372+
})
339373
.reduce(
340374
(acc, [k, v, negated]) => {
341375
return negated
@@ -353,6 +387,7 @@ export const filterVariables = ({ filters, issuableType, filterInfo, filterField
353387
},
354388
{ not: {} },
355389
);
390+
};
356391

357392
// EE-specific feature. Find the implementation in the `ee/`-folder
358393
export function transformBoardConfig() {

app/assets/javascripts/boards/components/board_app.vue

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default {
3434
'isGroupBoard',
3535
'issuableType',
3636
'boardType',
37+
'hasCustomFieldsFeature',
3738
],
3839
data() {
3940
return {
@@ -121,6 +122,7 @@ export default {
121122
issuableType: this.issuableType,
122123
filterInfo: FiltersInfo,
123124
filterFields: FilterFields,
125+
options: { hasCustomFieldsFeature: this.hasCustomFieldsFeature },
124126
});
125127
},
126128
isShowingEpicSwimlanesLocalStorageKey() {

app/assets/javascripts/boards/components/board_filtered_search.vue

+29-1
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ import {
2323
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
2424
import { AssigneeFilterType, GroupByParamType } from 'ee_else_ce/boards/constants';
2525
26+
const customFieldRegex = /custom-field\[([0-9]+)\]/g;
27+
2628
export default {
2729
i18n: {
2830
search: __('Search'),
2931
},
3032
components: { FilteredSearch },
31-
inject: ['initialFilterParams'],
33+
inject: ['initialFilterParams', 'hasCustomFieldsFeature'],
3234
props: {
3335
isSwimlanesOn: {
3436
type: Boolean,
@@ -73,9 +75,21 @@ export default {
7375
releaseTag,
7476
confidential,
7577
healthStatus,
78+
...otherValues
7679
} = this.filterParams;
7780
const filteredSearchValue = [];
7881
82+
if (this.hasCustomFieldsFeature) {
83+
for (const [key, value] of Object.entries(otherValues)) {
84+
if (key.match(customFieldRegex)) {
85+
filteredSearchValue.push({
86+
type: key,
87+
value: { data: value, operator: '=' },
88+
});
89+
}
90+
}
91+
}
92+
7993
if (authorUsername) {
8094
filteredSearchValue.push({
8195
type: TOKEN_TYPE_AUTHOR,
@@ -281,10 +295,20 @@ export default {
281295
releaseTag,
282296
confidential,
283297
healthStatus,
298+
...otherValues
284299
} = this.filterParams;
285300
let iteration = iterationId;
286301
let cadence = iterationCadenceId;
287302
let notParams = {};
303+
const customFieldParams = {};
304+
305+
if (this.hasCustomFieldsFeature) {
306+
Object.entries(otherValues).forEach(([key, value]) => {
307+
if (key.match(customFieldRegex)) {
308+
customFieldParams[key] = value;
309+
}
310+
});
311+
}
288312
289313
if (Object.prototype.hasOwnProperty.call(this.filterParams, 'not')) {
290314
notParams = pickBy(
@@ -311,6 +335,7 @@ export default {
311335
312336
return mapValues(
313337
{
338+
...customFieldParams,
314339
...notParams,
315340
author_username: authorUsername,
316341
'label_name[]': labelName,
@@ -441,6 +466,9 @@ export default {
441466
filterParams.healthStatus = filter.value.data;
442467
break;
443468
default:
469+
if (this.hasCustomFieldsFeature && filter.type.match(customFieldRegex)) {
470+
filterParams[filter.type] = filter.value.data;
471+
}
444472
break;
445473
}
446474
});

app/assets/javascripts/boards/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ function mountBoardApp(el) {
6969
wiGroupPath,
7070
wiCanAdminLabel,
7171
wiNewCommentTemplatePaths,
72+
hasCustomFieldsFeature,
7273
} = el.dataset;
7374

7475
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
@@ -141,6 +142,7 @@ function mountBoardApp(el) {
141142
hasSubepicsFeature: parseBoolean(el.dataset.subEpicsFeatureAvailable),
142143
hasLinkedItemsEpicsFeature: parseBoolean(el.dataset.hasLinkedItemsEpicsFeature),
143144
hasOkrsFeature: parseBoolean(el.dataset.hasOkrsFeature),
145+
hasCustomFieldsFeature: parseBoolean(hasCustomFieldsFeature),
144146
commentTemplatePaths: JSON.parse(wiNewCommentTemplatePaths),
145147
},
146148
render: (createComponent) => createComponent(BoardApp),

app/assets/javascripts/graphql_shared/constants.js

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const TYPE_ORGANIZATION = 'Organizations::Organization';
4141
export const TYPE_USERS_SAVED_REPLY = 'Users::SavedReply';
4242
export const TYPE_WORKSPACE = 'RemoteDevelopment::Workspace';
4343
export const TYPE_COMPLIANCE_FRAMEWORK = 'ComplianceManagement::Framework';
44+
export const TYPENAME_CUSTOM_FIELD = 'Issuables::CustomField';
45+
export const TYPENAME_CUSTOM_FIELD_SELECT_OPTION = 'Issuables::CustomFieldSelectOption';
4446

4547
export const QUERY_PARAM_START_CURSOR = 'start_cursor';
4648
export const QUERY_PARAM_END_CURSOR = 'end_cursor';

app/assets/javascripts/work_items/components/shared/work_item_sidebar_dropdown_widget.vue

+6-1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ export default {
107107
required: false,
108108
default: () => ({}),
109109
},
110+
noResetButton: {
111+
type: Boolean,
112+
required: false,
113+
default: false,
114+
},
110115
},
111116
data() {
112117
return {
@@ -123,7 +128,7 @@ export default {
123128
return `work-item-dropdown-listbox-value-${this.dropdownName}`;
124129
},
125130
resetButton() {
126-
return this.resetButtonLabel || __('Clear');
131+
return this.noResetButton ? null : this.resetButtonLabel || __('Clear');
127132
},
128133
toggleText() {
129134
return !this.toggleDropdownText && !this.hasValue

app/assets/javascripts/work_items/components/work_item_attributes_wrapper.vue

+18
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
WIDGET_TYPE_ITERATION,
1414
WIDGET_TYPE_LABELS,
1515
WIDGET_TYPE_MILESTONE,
16+
WIDGET_TYPE_STATUS,
1617
WIDGET_TYPE_PARTICIPANTS,
1718
WIDGET_TYPE_PROGRESS,
1819
WIDGET_TYPE_START_AND_DUE_DATE,
@@ -54,6 +55,7 @@ export default {
5455
WorkItemColor: () => import('ee_component/work_items/components/work_item_color.vue'),
5556
WorkItemCustomFields: () =>
5657
import('ee_component/work_items/components/work_item_custom_fields.vue'),
58+
WorkItemStatus: () => import('ee_component/work_items/components/work_item_status.vue'),
5759
},
5860
mixins: [glFeatureFlagMixin()],
5961
inject: ['hasSubepicsFeature'],
@@ -183,6 +185,9 @@ export default {
183185
workItemColor() {
184186
return this.isWidgetPresent(WIDGET_TYPE_COLOR);
185187
},
188+
workItemStatus() {
189+
return this.isWidgetPresent(WIDGET_TYPE_STATUS);
190+
},
186191
workItemAuthor() {
187192
return this.workItem?.author;
188193
},
@@ -198,6 +203,9 @@ export default {
198203
showWorkItemCustomFields() {
199204
return this.glFeatures.customFieldsFeature && this.customFields;
200205
},
206+
showWorkItemStatus() {
207+
return this.glFeatures.workItemStatusFeatureFlag;
208+
},
201209
},
202210
methods: {
203211
isWidgetPresent(type, workItem = this.workItem) {
@@ -209,6 +217,16 @@ export default {
209217
210218
<template>
211219
<div class="work-item-attributes-wrapper">
220+
<work-item-status
221+
v-if="showWorkItemStatus"
222+
class="work-item-attributes-item"
223+
:can-update="canUpdateMetadata"
224+
:work-item-id="workItem.id"
225+
:work-item-iid="workItem.iid"
226+
:work-item-type="workItemType"
227+
:full-path="fullPath"
228+
@error="$emit('error', $event)"
229+
/>
212230
<work-item-assignees
213231
v-if="workItemAssignees"
214232
class="js-assignee work-item-attributes-item"

app/assets/javascripts/work_items/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const WIDGET_TYPE_PROGRESS = 'PROGRESS';
2424
export const WIDGET_TYPE_HIERARCHY = 'HIERARCHY';
2525
export const WIDGET_TYPE_MILESTONE = 'MILESTONE';
2626
export const WIDGET_TYPE_ITERATION = 'ITERATION';
27+
export const WIDGET_TYPE_STATUS = 'STATUS';
2728
export const WIDGET_TYPE_NOTES = 'NOTES';
2829
export const WIDGET_TYPE_HEALTH_STATUS = 'HEALTH_STATUS';
2930
export const WIDGET_TYPE_LINKED_ITEMS = 'LINKED_ITEMS';

app/controllers/concerns/preview_markdown.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ def preview_markdown
88
container: resource_parent,
99
current_user: current_user,
1010
params: markdown_service_params
11-
).execute
11+
).execute do |text|
12+
view_context.markdown(text, markdown_context_params)
13+
end
1214

1315
render json: {
14-
body: view_context.markdown(result[:text], markdown_context_params),
16+
body: result[:rendered_html],
1517
references: {
1618
users: result[:users],
1719
suggestions: SuggestionSerializer.new.represent_diff(result[:suggestions]),

app/controllers/groups/work_items_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class WorkItemsController < Groups::ApplicationController
1616
push_force_frontend_feature_flag(:glql_load_on_click, !!group&.glql_load_on_click_feature_flag_enabled?)
1717
push_force_frontend_feature_flag(:continue_indented_text, !!group&.continue_indented_text_feature_flag_enabled?)
1818
push_frontend_feature_flag(:issues_list_drawer, group)
19+
push_frontend_feature_flag(:work_item_status_feature_flag, group&.root_ancestor)
1920
end
2021
before_action :handle_new_work_item_path, only: [:show]
2122

app/controllers/groups_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class GroupsController < Groups::ApplicationController
3939
push_force_frontend_feature_flag(:work_items_alpha, group.work_items_alpha_feature_flag_enabled?)
4040
push_frontend_feature_flag(:issues_grid_view)
4141
push_frontend_feature_flag(:issues_list_drawer, group)
42+
push_frontend_feature_flag(:work_item_status_feature_flag, group&.root_ancestor)
4243
push_force_frontend_feature_flag(:namespace_level_work_items, group.namespace_work_items_enabled?)
4344
end
4445

app/controllers/projects/issues_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class Projects::IssuesController < Projects::ApplicationController
5656
push_force_frontend_feature_flag(:work_items_beta, !!project&.work_items_beta_feature_flag_enabled?)
5757
push_force_frontend_feature_flag(:work_items_alpha, !!project&.work_items_alpha_feature_flag_enabled?)
5858
push_frontend_feature_flag(:work_item_view_for_issues, project&.group)
59+
push_frontend_feature_flag(:work_item_status_feature_flag, project&.root_ancestor)
5960
end
6061

6162
before_action only: [:index, :show] do

app/controllers/projects/merge_requests/drafts_controller.rb

+6-3
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,13 @@ def prepare_notes_for_rendering(notes)
155155

156156
def render_draft_note(note)
157157
params = { target_id: merge_request.iid, target_type: 'MergeRequest', text: note.note }
158-
result = PreviewMarkdownService.new(container: @project, current_user: current_user, params: params).execute
159-
markdown_params = { markdown_engine: result[:markdown_engine], issuable_reference_expansion_enabled: true }
158+
result = PreviewMarkdownService.new(container: @project, current_user: current_user, params: params)
159+
.execute do |text|
160+
markdown_params = { issuable_reference_expansion_enabled: true }
161+
view_context.markdown(text, markdown_params)
162+
end
160163

161-
note.rendered_note = view_context.markdown(result[:text], markdown_params)
164+
note.rendered_note = result[:rendered_html]
162165
note.users_referenced = result[:users]
163166
note.commands_changes = view_context.markdown(result[:commands])
164167

app/controllers/projects/work_items_controller.rb

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Projects::WorkItemsController < Projects::ApplicationController
1515
push_force_frontend_feature_flag(:glql_integration, !!project&.glql_integration_feature_flag_enabled?)
1616
push_force_frontend_feature_flag(:glql_load_on_click, !!project&.glql_load_on_click_feature_flag_enabled?)
1717
push_force_frontend_feature_flag(:continue_indented_text, !!project&.continue_indented_text_feature_flag_enabled?)
18+
push_frontend_feature_flag(:work_item_status_feature_flag, project&.root_ancestor)
1819
push_frontend_feature_flag(:namespace_level_work_items, project&.group)
1920
push_frontend_feature_flag(:work_item_planning_view, project&.group)
2021
end

app/models/ci/persistent_ref.rb

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def create
2929
rescue StandardError => e
3030
Gitlab::ErrorTracking
3131
.track_exception(e, pipeline_id: pipeline.id)
32+
false
3233
end
3334

3435
def delete

0 commit comments

Comments
 (0)