Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/boards/boards_util.js14
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue30
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue3
-rw-r--r--app/assets/javascripts/boards/index.js17
-rw-r--r--app/assets/javascripts/boards/stores/actions.js36
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js3
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js17
-rw-r--r--app/assets/javascripts/boards/stores/state.js1
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js10
-rw-r--r--app/assets/javascripts/graphql_shared/constants.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js52
-rw-r--r--app/assets/javascripts/single_file_diff.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/utils.js10
-rw-r--r--app/models/application_record.rb1
-rw-r--r--app/models/concerns/sensitive_serializable_hash.rb46
-rw-r--r--app/models/concerns/token_authenticatable.rb10
-rw-r--r--app/models/concerns/token_authenticatable_strategies/base.rb8
-rw-r--r--app/models/concerns/token_authenticatable_strategies/digest.rb4
-rw-r--r--app/models/concerns/token_authenticatable_strategies/encrypted.rb4
-rw-r--r--app/models/wiki.rb4
-rw-r--r--config/feature_flags/development/prevent_sensitive_fields_from_serializable_hash.yml8
-rw-r--r--danger/documentation/Dangerfile4
-rw-r--r--doc/administration/encrypted_configuration.md2
-rw-r--r--doc/administration/package_information/index.md2
-rw-r--r--doc/administration/package_information/licensing.md2
-rw-r--r--doc/administration/package_information/omnibus_packages.md2
-rw-r--r--doc/administration/package_information/postgresql_versions.md2
-rw-r--r--doc/administration/package_information/signed_packages.md2
-rw-r--r--doc/api/graphql/reference/index.md2
-rw-r--r--doc/api/oauth2.md2
-rw-r--r--doc/architecture/blueprints/container_registry_metadata_database/index.md2
-rw-r--r--doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md2
-rw-r--r--doc/ci/yaml/gitlab_ci_yaml.md2
-rw-r--r--doc/development/bulk_import.md2
-rw-r--r--doc/development/contributing/design.md2
-rw-r--r--doc/development/export_csv.md2
-rw-r--r--doc/install/aws/manual_install_aws.md2
-rw-r--r--doc/integration/security_partners/index.md2
-rw-r--r--doc/update/package/convert_to_ee.md2
-rw-r--r--doc/update/package/downgrade.md2
-rw-r--r--doc/update/package/index.md2
-rw-r--r--doc/user/admin_area/reporting/spamcheck.md2
-rw-r--r--doc/user/application_security/api_fuzzing/create_har_files.md2
-rw-r--r--doc/user/clusters/agent/gitops.md2
-rw-r--r--doc/user/clusters/agent/gitops/secrets_management.md2
-rw-r--r--doc/user/clusters/agent/repository.md2
-rw-r--r--doc/user/clusters/agent/vulnerabilities.md2
-rw-r--r--doc/user/group/value_stream_analytics/index.md2
-rw-r--r--doc/user/project/clusters/protect/container_host_security/index.md2
-rw-r--r--doc/user/project/clusters/protect/container_host_security/quick_start_guide.md2
-rw-r--r--doc/user/project/clusters/protect/container_network_security/index.md2
-rw-r--r--doc/user/project/clusters/protect/container_network_security/quick_start_guide.md2
-rw-r--r--doc/user/project/clusters/protect/index.md2
-rw-r--r--doc/user/usage_quotas.md2
-rw-r--r--lib/gitlab/git/wiki.rb8
-rw-r--r--lib/gitlab/gitaly_client/wiki_service.rb5
-rw-r--r--lib/gitlab/json_cache.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb59
-rw-r--r--qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb6
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb9
-rw-r--r--spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb2
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js1
-rw-r--r--spec/frontend/boards/mock_data.js31
-rw-r--r--spec/frontend/boards/stores/actions_spec.js72
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js40
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js108
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/utils_spec.js27
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb16
-rw-r--r--spec/models/concerns/sensitive_serializable_hash_spec.rb150
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb6
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/base_spec.rb18
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/digest_spec.rb18
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb10
-rw-r--r--spec/services/ci/job_artifacts/create_service_spec.rb2
-rw-r--r--spec/services/ci/parse_dotenv_artifact_service_spec.rb24
-rw-r--r--tooling/graphql/docs/helper.rb2
81 files changed, 820 insertions, 176 deletions
diff --git a/Gemfile b/Gemfile
index 40cf9bcf465..b7fd39b3617 100644
--- a/Gemfile
+++ b/Gemfile
@@ -476,7 +476,7 @@ gem 'ssh_data', '~> 1.2'
gem 'spamcheck', '~> 0.1.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 14.8.0.pre.rc1'
+gem 'gitaly', '~> 14.9.0.pre.rc2'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index a66a2fff1bd..eb64191b6e6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -455,7 +455,7 @@ GEM
rails (>= 3.2.0)
git (1.7.0)
rchardet (~> 1.8)
- gitaly (14.8.0.pre.rc1)
+ gitaly (14.9.0.pre.rc2)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab (4.16.1)
@@ -1475,7 +1475,7 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 14.8.0.pre.rc1)
+ gitaly (~> 14.9.0.pre.rc2)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 2.10.2)
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 7e4d3ebb686..96cc774a280 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,5 +1,6 @@
import { sortBy, cloneDeep } from 'lodash';
-import { isGid } from '~/graphql_shared/utils';
+import { TYPE_BOARD, TYPE_ITERATION, TYPE_MILESTONE, TYPE_USER } from '~/graphql_shared/constants';
+import { isGid, convertToGraphQLId } from '~/graphql_shared/utils';
import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants';
export function getMilestone() {
@@ -80,19 +81,22 @@ export function formatListsPageInfo(lists) {
}
export function fullBoardId(boardId) {
- return `gid://gitlab/Board/${boardId}`;
+ if (!boardId) {
+ return null;
+ }
+ return convertToGraphQLId(TYPE_BOARD, boardId);
}
export function fullIterationId(id) {
- return `gid://gitlab/Iteration/${id}`;
+ return convertToGraphQLId(TYPE_ITERATION, id);
}
export function fullUserId(id) {
- return `gid://gitlab/User/${id}`;
+ return convertToGraphQLId(TYPE_USER, id);
}
export function fullMilestoneId(id) {
- return `gid://gitlab/Milestone/${id}`;
+ return convertToGraphQLId(TYPE_MILESTONE, id);
}
export function fullLabelId(label) {
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index cc048e2af1a..f39f9751c83 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -1,11 +1,8 @@
<script>
import { GlModal, GlAlert } from '@gitlab/ui';
import { mapGetters, mapActions, mapState } from 'vuex';
-import { TYPE_USER, TYPE_ITERATION, TYPE_MILESTONE } from '~/graphql_shared/constants';
-import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { getParameterByName, visitUrl } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
-import { fullLabelId } from '../boards_util';
import { formType } from '../constants';
import createBoardMutation from '../graphql/board_create.mutation.graphql';
@@ -158,33 +155,8 @@ export default {
groupPath: this.isGroupBoard ? this.fullPath : undefined,
};
},
- issueBoardScopeMutationVariables() {
- return {
- weight: this.board.weight,
- assigneeId: this.board.assignee?.id
- ? convertToGraphQLId(TYPE_USER, this.board.assignee.id)
- : null,
- // Temporarily converting to milestone ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
- milestoneId: this.board.milestone?.id
- ? convertToGraphQLId(TYPE_MILESTONE, getIdFromGraphQLId(this.board.milestone.id))
- : null,
- // Temporarily converting to iteration ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779
- iterationId: this.board.iteration?.id
- ? convertToGraphQLId(TYPE_ITERATION, getIdFromGraphQLId(this.board.iteration.id))
- : null,
- };
- },
- boardScopeMutationVariables() {
- return {
- labelIds: this.board.labels.map(fullLabelId),
- ...(this.isIssueBoard && this.issueBoardScopeMutationVariables),
- };
- },
mutationVariables() {
- return {
- ...this.baseMutationVariables,
- ...(this.scopedIssueBoardFeatureEnabled ? this.boardScopeMutationVariables : {}),
- };
+ return this.baseMutationVariables;
},
},
mounted() {
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index 5d37cd4930f..91fdfd668fc 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -101,6 +101,7 @@ export default {
},
update(data) {
const board = data.workspace?.board;
+ this.setBoardConfig(board);
return {
...board,
labels: board?.labels?.nodes,
@@ -170,7 +171,7 @@ export default {
eventHub.$off('showBoardModal', this.showPage);
},
methods: {
- ...mapActions(['setError']),
+ ...mapActions(['setError', 'setBoardConfig']),
showPage(page) {
this.currentPage = page;
},
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index d2a64246aa7..b31b56e6839 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -28,6 +28,12 @@ const apolloProvider = new VueApollo({
function mountBoardApp(el) {
const { boardId, groupId, fullPath, rootPath } = el.dataset;
+ store.dispatch('fetchBoard', {
+ fullPath,
+ fullBoardId: fullBoardId(boardId),
+ boardType: el.dataset.parent,
+ });
+
store.dispatch('setInitialBoardData', {
boardId,
fullBoardId: fullBoardId(boardId),
@@ -35,17 +41,6 @@ function mountBoardApp(el) {
boardType: el.dataset.parent,
disabled: parseBoolean(el.dataset.disabled) || true,
issuableType: issuableTypes.issue,
- boardConfig: {
- milestoneId: parseInt(el.dataset.boardMilestoneId, 10),
- milestoneTitle: el.dataset.boardMilestoneTitle || '',
- iterationId: parseInt(el.dataset.boardIterationId, 10),
- iterationTitle: el.dataset.boardIterationTitle || '',
- assigneeId: el.dataset.boardAssigneeId,
- assigneeUsername: el.dataset.boardAssigneeUsername,
- labels: el.dataset.labels ? JSON.parse(el.dataset.labels) : [],
- labelIds: el.dataset.labelIds ? JSON.parse(el.dataset.labelIds) : [],
- weight: el.dataset.boardWeight ? parseInt(el.dataset.boardWeight, 10) : null,
- },
});
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 1ebfcfc331b..82307da2572 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -36,6 +36,8 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { gqlClient } from '../graphql';
+import projectBoardQuery from '../graphql/project_board.query.graphql';
+import groupBoardQuery from '../graphql/group_board.query.graphql';
import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql';
import groupProjectsQuery from '../graphql/group_projects.query.graphql';
@@ -46,10 +48,44 @@ import projectBoardMilestonesQuery from '../graphql/project_board_milestones.que
import * as types from './mutation_types';
export default {
+ fetchBoard: ({ commit, dispatch }, { fullPath, fullBoardId, boardType }) => {
+ const variables = {
+ fullPath,
+ boardId: fullBoardId,
+ };
+
+ return gqlClient
+ .query({
+ query: boardType === BoardType.group ? groupBoardQuery : projectBoardQuery,
+ variables,
+ })
+ .then(({ data }) => {
+ const board = data.workspace?.board;
+ commit(types.RECEIVE_BOARD_SUCCESS, board);
+ dispatch('setBoardConfig', board);
+ })
+ .catch(() => commit(types.RECEIVE_BOARD_FAILURE));
+ },
+
setInitialBoardData: ({ commit }, data) => {
commit(types.SET_INITIAL_BOARD_DATA, data);
},
+ setBoardConfig: ({ commit }, board) => {
+ const config = {
+ milestoneId: board.milestone?.id || null,
+ milestoneTitle: board.milestone?.title || null,
+ iterationId: board.iteration?.id || null,
+ iterationTitle: board.iteration?.title || null,
+ assigneeId: board.assignee?.id || null,
+ assigneeUsername: board.assignee?.username || null,
+ labels: board.labels?.nodes || [],
+ labelIds: board.labels?.nodes?.map((label) => label.id) || [],
+ weight: board.weight,
+ };
+ commit(types.SET_BOARD_CONFIG, config);
+ },
+
setActiveId({ commit }, { id, sidebarType }) {
commit(types.SET_ACTIVE_ID, { id, sidebarType });
},
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 31b78014525..668a3b5e0f9 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -1,4 +1,7 @@
+export const RECEIVE_BOARD_SUCCESS = 'RECEIVE_BOARD_SUCCESS';
+export const RECEIVE_BOARD_FAILURE = 'RECEIVE_BOARD_FAILURE';
export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
+export const SET_BOARD_CONFIG = 'SET_BOARD_CONFIG';
export const SET_FILTERS = 'SET_FILTERS';
export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS';
export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 2a2ce7652e6..9a50dcf05b8 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -33,10 +33,20 @@ export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId
};
export default {
+ [mutationTypes.RECEIVE_BOARD_SUCCESS]: (state, board) => {
+ state.board = {
+ ...board,
+ labels: board?.labels?.nodes || [],
+ };
+ },
+
+ [mutationTypes.RECEIVE_BOARD_FAILURE]: (state) => {
+ state.error = s__('Boards|An error occurred while fetching the board. Please reload the page.');
+ },
+
[mutationTypes.SET_INITIAL_BOARD_DATA](state, data) {
const {
allowSubEpics,
- boardConfig,
boardId,
boardType,
disabled,
@@ -45,7 +55,6 @@ export default {
issuableType,
} = data;
state.allowSubEpics = allowSubEpics;
- state.boardConfig = boardConfig;
state.boardId = boardId;
state.boardType = boardType;
state.disabled = disabled;
@@ -54,6 +63,10 @@ export default {
state.issuableType = issuableType;
},
+ [mutationTypes.SET_BOARD_CONFIG](state, boardConfig) {
+ state.boardConfig = boardConfig;
+ },
+
[mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => {
state.boardLists = lists;
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index 80c51c966d2..7af4e5a8798 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -1,6 +1,7 @@
import { inactiveId, ListType } from '~/boards/constants';
export default () => ({
+ board: {},
boardType: null,
issuableType: null,
fullPath: null,
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 7282a92a847..8cb2e9e249b 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -3,6 +3,7 @@ import '~/lib/utils/jquery_at_who';
import { escape as lodashEscape, sortBy, template, escapeRegExp } from 'lodash';
import * as Emoji from '~/emoji';
import axios from '~/lib/utils/axios_utils';
+import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
import { s__, __, sprintf } from '~/locale';
import { isUserBusy } from '~/set_status_modal/utils';
import SidebarMediator from '~/sidebar/sidebar_mediator';
@@ -957,9 +958,14 @@ GfmAutoComplete.Contacts = {
return `<li><small>${firstName} ${lastName}</small> ${escape(email)}</li>`;
},
};
+
+const loadingSpinner = loadingIconForLegacyJS({
+ inline: true,
+ classes: ['gl-mr-2'],
+}).outerHTML;
+
GfmAutoComplete.Loading = {
- template:
- '<li style="pointer-events: none;"><span class="spinner align-text-bottom mr-1"></span>Loading...</li>',
+ template: `<li style="pointer-events: none;">${loadingSpinner}Loading...</li>`,
};
export default GfmAutoComplete;
diff --git a/app/assets/javascripts/graphql_shared/constants.js b/app/assets/javascripts/graphql_shared/constants.js
index 3b36c3e6ac5..4ebb49b4756 100644
--- a/app/assets/javascripts/graphql_shared/constants.js
+++ b/app/assets/javascripts/graphql_shared/constants.js
@@ -1,9 +1,11 @@
export const MINIMUM_SEARCH_LENGTH = 3;
+export const TYPE_BOARD = 'Board';
export const TYPE_CI_RUNNER = 'Ci::Runner';
export const TYPE_CRM_CONTACT = 'CustomerRelations::Contact';
export const TYPE_DISCUSSION = 'Discussion';
export const TYPE_EPIC = 'Epic';
+export const TYPE_EPIC_BOARD = 'Boards::EpicBoard';
export const TYPE_GROUP = 'Group';
export const TYPE_ISSUE = 'Issue';
export const TYPE_ITERATION = 'Iteration';
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index a4accf0ee8f..ac2eb34260c 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -9,7 +9,7 @@ const LINK_TAG_PATTERN = '[{text}](url)';
// a bullet point character (*+-) and an optional checkbox ([ ] [x])
// OR a number with a . after it and an optional checkbox ([ ] [x])
// followed by one or more whitespace characters
-const LIST_LINE_HEAD_PATTERN = /^(?<indent>\s*)(?<leader>((?<isOl>[*+-])|(?<isUl>\d+\.))( \[([x ])\])?\s)(?<content>.)?/;
+const LIST_LINE_HEAD_PATTERN = /^(?<indent>\s*)(?<leader>((?<isUl>[*+-])|(?<isOl>\d+\.))( \[([x ])\])?\s)(?<content>.)?/;
function selectedText(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
@@ -31,8 +31,19 @@ function lineBefore(text, textarea, trimNewlines = true) {
return split[split.length - 1];
}
-function lineAfter(text, textarea) {
- return text.substring(textarea.selectionEnd).trim().split('\n')[0];
+function lineAfter(text, textarea, trimNewlines = true) {
+ let split = text.substring(textarea.selectionEnd);
+
+ if (trimNewlines) {
+ split = split.trim();
+ } else {
+ // remove possible leading newline to get at the real line
+ split = split.replace(/^\n/, '');
+ }
+
+ split = split.split('\n');
+
+ return split[0];
}
function convertMonacoSelectionToAceFormat(sel) {
@@ -329,6 +340,25 @@ function handleSurroundSelectedText(e, textArea) {
}
/* eslint-enable @gitlab/require-i18n-strings */
+/**
+ * Returns the content for a new line following a list item.
+ *
+ * @param {Object} result - regex match of the current line
+ * @param {Object?} nextLineResult - regex match of the next line
+ * @returns string with the new list item
+ */
+function continueOlText(result, nextLineResult) {
+ const { indent, leader } = result.groups;
+ const { indent: nextIndent, isOl: nextIsOl } = nextLineResult?.groups ?? {};
+
+ const [numStr, postfix = ''] = leader.split('.');
+
+ const incrementBy = nextIsOl && nextIndent === indent ? 0 : 1;
+ const num = parseInt(numStr, 10) + incrementBy;
+
+ return `${indent}${num}.${postfix}`;
+}
+
function handleContinueList(e, textArea) {
if (!gon.features?.markdownContinueLists) return;
if (!(e.key === 'Enter')) return;
@@ -339,7 +369,7 @@ function handleContinueList(e, textArea) {
const result = currentLine.match(LIST_LINE_HEAD_PATTERN);
if (result) {
- const { indent, content, leader } = result.groups;
+ const { leader, indent, content, isOl } = result.groups;
const prevLineEmpty = !content;
if (prevLineEmpty) {
@@ -349,12 +379,22 @@ function handleContinueList(e, textArea) {
return;
}
- const itemInsert = `${indent}${leader}`;
+ let itemToInsert;
+
+ if (isOl) {
+ const nextLine = lineAfter(textArea.value, textArea, false);
+ const nextLineResult = nextLine.match(LIST_LINE_HEAD_PATTERN);
+
+ itemToInsert = continueOlText(result, nextLineResult);
+ } else {
+ // isUl
+ itemToInsert = `${indent}${leader}`;
+ }
e.preventDefault();
updateText({
- tag: itemInsert,
+ tag: itemToInsert,
textArea,
blockTag: '',
wrap: false,
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index d2841156e55..b7159fd6835 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -1,6 +1,7 @@
/* eslint-disable consistent-return */
import $ from 'jquery';
+import { loadingIconForLegacyJS } from '~/loading_icon_for_legacy_js';
import { spriteIcon } from '~/lib/utils/common_utils';
import FilesCommentButton from './files_comment_button';
import createFlash from './flash';
@@ -10,7 +11,7 @@ import { __ } from './locale';
import syntaxHighlight from './syntax_highlight';
const WRAPPER = '<div class="diff-content"></div>';
-const LOADING_HTML = '<span class="spinner"></span>';
+const LOADING_HTML = loadingIconForLegacyJS().outerHTML;
const ERROR_HTML = `<div class="nothing-here-block">${spriteIcon(
'warning-solid',
's16',
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
index 5aae1812de3..4a78cbacec0 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
@@ -35,16 +35,20 @@ export default {
},
highlightedContent() {
let highlightedContent;
+ let { language } = this;
if (this.hljs) {
- if (!this.language) {
- highlightedContent = this.hljs.highlightAuto(this.content).value;
+ if (!language) {
+ const hljsHighlightAuto = this.hljs.highlightAuto(this.content);
+
+ highlightedContent = hljsHighlightAuto.value;
+ language = hljsHighlightAuto.language;
} else if (this.languageDefinition) {
highlightedContent = this.hljs.highlight(this.content, { language: this.language }).value;
}
}
- return wrapLines(highlightedContent);
+ return wrapLines(highlightedContent, language);
},
},
watch: {
@@ -110,7 +114,7 @@ export default {
data-qa-selector="blob_viewer_file_content"
>
<line-numbers :lines="lineNumbers" />
- <pre class="code gl-pb-0!"><code v-safe-html="highlightedContent"></code>
+ <pre class="code highlight gl-pb-0!"><code v-safe-html="highlightedContent"></code>
</pre>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/utils.js b/app/assets/javascripts/vue_shared/components/source_viewer/utils.js
index e64e564bf61..d726a8a55ff 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/utils.js
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/utils.js
@@ -1,11 +1,13 @@
-export const wrapLines = (content) => {
+export const wrapLines = (content, language) => {
+ const isValidLanguage = /^[a-z\d\-_]+$/.test(language); // To prevent the possibility of a vulnerability we only allow languages that contain alphanumeric characters ([a-z\d), dashes (-) or underscores (_).
+
return (
content &&
content
.split('\n')
.map((line, i) => {
let formattedLine;
- const idAttribute = `id="LC${i + 1}"`;
+ const attributes = `id="LC${i + 1}" lang="${isValidLanguage ? language : ''}"`;
if (line.includes('<span class="hljs') && !line.includes('</span>')) {
/**
@@ -14,9 +16,9 @@ export const wrapLines = (content) => {
* example (before): <span class="hljs-code">```bash
* example (after): <span id="LC67" class="hljs-code">```bash
*/
- formattedLine = line.replace(/(?=class="hljs)/, `${idAttribute} `);
+ formattedLine = line.replace(/(?=class="hljs)/, `${attributes} `);
} else {
- formattedLine = `<span ${idAttribute} class="line">${line}</span>`;
+ formattedLine = `<span ${attributes} class="line">${line}</span>`;
}
return formattedLine;
diff --git a/app/models/application_record.rb b/app/models/application_record.rb
index c9c46250a6d..75e1bab294f 100644
--- a/app/models/application_record.rb
+++ b/app/models/application_record.rb
@@ -5,6 +5,7 @@ class ApplicationRecord < ActiveRecord::Base
include Transactions
include LegacyBulkInsert
include CrossDatabaseModification
+ include SensitiveSerializableHash
self.abstract_class = true
diff --git a/app/models/concerns/sensitive_serializable_hash.rb b/app/models/concerns/sensitive_serializable_hash.rb
new file mode 100644
index 00000000000..725ec60e9b6
--- /dev/null
+++ b/app/models/concerns/sensitive_serializable_hash.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module SensitiveSerializableHash
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :attributes_exempt_from_serializable_hash, default: []
+ end
+
+ class_methods do
+ def prevent_from_serialization(*keys)
+ self.attributes_exempt_from_serializable_hash ||= []
+ self.attributes_exempt_from_serializable_hash.concat keys
+ end
+ end
+
+ # Override serializable_hash to exclude sensitive attributes by default
+ #
+ # In general, prefer NOT to use serializable_hash / to_json / as_json in favor
+ # of serializers / entities instead which has an allowlist of attributes
+ def serializable_hash(options = nil)
+ return super unless prevent_sensitive_fields_from_serializable_hash?
+ return super if options && options[:unsafe_serialization_hash]
+
+ options = options.try(:dup) || {}
+ options[:except] = Array(options[:except]).dup
+
+ options[:except].concat self.class.attributes_exempt_from_serializable_hash
+
+ if self.class.respond_to?(:encrypted_attributes)
+ options[:except].concat self.class.encrypted_attributes.keys
+
+ # Per https://github.com/attr-encrypted/attr_encrypted/blob/a96693e9a2a25f4f910bf915e29b0f364f277032/lib/attr_encrypted.rb#L413
+ options[:except].concat self.class.encrypted_attributes.values.map { |v| v[:attribute] }
+ options[:except].concat self.class.encrypted_attributes.values.map { |v| "#{v[:attribute]}_iv" }
+ end
+
+ super(options)
+ end
+
+ private
+
+ def prevent_sensitive_fields_from_serializable_hash?
+ Feature.enabled?(:prevent_sensitive_fields_from_serializable_hash, default_enabled: :yaml)
+ end
+end
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index f44ad8ebe90..d91ec161b84 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -8,6 +8,10 @@ module TokenAuthenticatable
@encrypted_token_authenticatable_fields ||= []
end
+ def token_authenticatable_fields
+ @token_authenticatable_fields ||= []
+ end
+
private
def add_authentication_token_field(token_field, options = {})
@@ -23,6 +27,8 @@ module TokenAuthenticatable
strategy = TokenAuthenticatableStrategies::Base
.fabricate(self, token_field, options)
+ prevent_from_serialization(*strategy.token_fields) if respond_to?(:prevent_from_serialization)
+
if options.fetch(:unique, true)
define_singleton_method("find_by_#{token_field}") do |token|
strategy.find_token_authenticatable(token)
@@ -82,9 +88,5 @@ module TokenAuthenticatable
@token_authenticatable_module ||=
const_set(:TokenAuthenticatable, Module.new).tap(&method(:include))
end
-
- def token_authenticatable_fields
- @token_authenticatable_fields ||= []
- end
end
end
diff --git a/app/models/concerns/token_authenticatable_strategies/base.rb b/app/models/concerns/token_authenticatable_strategies/base.rb
index 2cec4ab460e..2b677f37c89 100644
--- a/app/models/concerns/token_authenticatable_strategies/base.rb
+++ b/app/models/concerns/token_authenticatable_strategies/base.rb
@@ -23,6 +23,14 @@ module TokenAuthenticatableStrategies
raise NotImplementedError
end
+ def token_fields
+ result = [token_field]
+
+ result << @expires_at_field if expirable?
+
+ result
+ end
+
# Default implementation returns the token as-is
def format_token(instance, token)
instance.send("format_#{@token_field}", token) # rubocop:disable GitlabSecurity/PublicSend
diff --git a/app/models/concerns/token_authenticatable_strategies/digest.rb b/app/models/concerns/token_authenticatable_strategies/digest.rb
index 9926662ed66..5c94f25949f 100644
--- a/app/models/concerns/token_authenticatable_strategies/digest.rb
+++ b/app/models/concerns/token_authenticatable_strategies/digest.rb
@@ -2,6 +2,10 @@
module TokenAuthenticatableStrategies
class Digest < Base
+ def token_fields
+ super + [token_field_name]
+ end
+
def find_token_authenticatable(token, unscoped = false)
return unless token
diff --git a/app/models/concerns/token_authenticatable_strategies/encrypted.rb b/app/models/concerns/token_authenticatable_strategies/encrypted.rb
index e957d09fbc6..1db88c27181 100644
--- a/app/models/concerns/token_authenticatable_strategies/encrypted.rb
+++ b/app/models/concerns/token_authenticatable_strategies/encrypted.rb
@@ -2,6 +2,10 @@
module TokenAuthenticatableStrategies
class Encrypted < Base
+ def token_fields
+ super + [encrypted_field]
+ end
+
def find_token_authenticatable(token, unscoped = false)
return if token.blank?
diff --git a/app/models/wiki.rb b/app/models/wiki.rb
index e114e30d589..307f31edfef 100644
--- a/app/models/wiki.rb
+++ b/app/models/wiki.rb
@@ -150,10 +150,10 @@ class Wiki
# the page.
#
# Returns an initialized WikiPage instance or nil
- def find_page(title, version = nil)
+ def find_page(title, version = nil, load_content: true)
page_title, page_dir = page_title_and_dir(title)
- if page = wiki.page(title: page_title, version: version, dir: page_dir)
+ if page = wiki.page(title: page_title, version: version, dir: page_dir, load_content: load_content)
WikiPage.new(self, page)
end
end
diff --git a/config/feature_flags/development/prevent_sensitive_fields_from_serializable_hash.yml b/config/feature_flags/development/prevent_sensitive_fields_from_serializable_hash.yml
new file mode 100644
index 00000000000..7bcbe6b79c2
--- /dev/null
+++ b/config/feature_flags/development/prevent_sensitive_fields_from_serializable_hash.yml
@@ -0,0 +1,8 @@
+---
+name: prevent_sensitive_fields_from_serializable_hash
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81773
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353878
+milestone: '14.9'
+type: development
+group: group::sharding
+default_enabled: false
diff --git a/danger/documentation/Dangerfile b/danger/documentation/Dangerfile
index 918c787075e..41c75a9f176 100644
--- a/danger/documentation/Dangerfile
+++ b/danger/documentation/Dangerfile
@@ -5,7 +5,7 @@ def feature_mr?
end
DOCUMENTATION_UPDATE_MISSING = <<~MSG
-~"feature::addition" and ~"feature::enhancement" merge requests normally have a documentation change. Consider adding a documentation update or confirming the documentation plan with the [Technical Writer counterpart](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers).
+~"feature::addition" and ~"feature::enhancement" merge requests normally have a documentation change. Consider adding a documentation update or confirming the documentation plan with the [Technical Writer counterpart](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments).
For more information, see:
@@ -36,6 +36,6 @@ markdown(<<~MARKDOWN)
The review does not need to block merging this merge request. See the:
- [Metadata for the `*.md` files](https://docs.gitlab.com/ee/development/documentation/#metadata) that you've changed. The first few lines of each `*.md` file identify the stage and group most closely associated with your docs change.
- - The [Technical Writer assigned](https://about.gitlab.com/handbook/engineering/technical-writing/#designated-technical-writers) for that stage and group.
+ - The [Technical Writer assigned](https://about.gitlab.com/handbook/engineering/technical-writing/#assignments) for that stage and group.
- [Documentation workflows](https://docs.gitlab.com/ee/development/documentation/workflow.html) for information on when to assign a merge request for review.
MARKDOWN
diff --git a/doc/administration/encrypted_configuration.md b/doc/administration/encrypted_configuration.md
index 9224def4a5a..baa6e967507 100644
--- a/doc/administration/encrypted_configuration.md
+++ b/doc/administration/encrypted_configuration.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers"
+info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference
---
diff --git a/doc/administration/package_information/index.md b/doc/administration/package_information/index.md
index 2781f789409..c908f526569 100644
--- a/doc/administration/package_information/index.md
+++ b/doc/administration/package_information/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Package information **(FREE SELF)**
diff --git a/doc/administration/package_information/licensing.md b/doc/administration/package_information/licensing.md
index 02358c66993..a7bf5c52d7b 100644
--- a/doc/administration/package_information/licensing.md
+++ b/doc/administration/package_information/licensing.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Package Licensing **(FREE SELF)**
diff --git a/doc/administration/package_information/omnibus_packages.md b/doc/administration/package_information/omnibus_packages.md
index 115d6c394ad..21e3f5711ba 100644
--- a/doc/administration/package_information/omnibus_packages.md
+++ b/doc/administration/package_information/omnibus_packages.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Omnibus based packages and images **(FREE SELF)**
diff --git a/doc/administration/package_information/postgresql_versions.md b/doc/administration/package_information/postgresql_versions.md
index 97a35fb29ed..e707cb70187 100644
--- a/doc/administration/package_information/postgresql_versions.md
+++ b/doc/administration/package_information/postgresql_versions.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# PostgreSQL versions shipped with Omnibus GitLab **(FREE SELF)**
diff --git a/doc/administration/package_information/signed_packages.md b/doc/administration/package_information/signed_packages.md
index fb605f8d5be..d7bcfa113ff 100644
--- a/doc/administration/package_information/signed_packages.md
+++ b/doc/administration/package_information/signed_packages.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Package Signatures **(FREE SELF)**
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 6715bd78656..2ed55c9803c 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1,7 +1,7 @@
---
stage: Ecosystem
group: Integrations
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
<!---
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index 51fb12d9620..7b38ac39b96 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -2,7 +2,7 @@
type: reference, howto
stage: Manage
group: Authentication and Authorization
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# OAuth 2.0 identity provider API **(FREE)**
diff --git a/doc/architecture/blueprints/container_registry_metadata_database/index.md b/doc/architecture/blueprints/container_registry_metadata_database/index.md
index c1aac235085..ba0bee04f35 100644
--- a/doc/architecture/blueprints/container_registry_metadata_database/index.md
+++ b/doc/architecture/blueprints/container_registry_metadata_database/index.md
@@ -1,7 +1,7 @@
---
stage: Package
group: Package
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
description: 'Container Registry metadata database'
---
diff --git a/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
index 7039eb4cd02..b22636ac1d9 100644
--- a/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
+++ b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
@@ -1,7 +1,7 @@
---
stage: Configure
group: Configure
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
description: 'GitLab to Kubernetes communication'
---
diff --git a/doc/ci/yaml/gitlab_ci_yaml.md b/doc/ci/yaml/gitlab_ci_yaml.md
index 9bab4a7fc77..f3773fbf143 100644
--- a/doc/ci/yaml/gitlab_ci_yaml.md
+++ b/doc/ci/yaml/gitlab_ci_yaml.md
@@ -1,7 +1,7 @@
---
stage: Verify
group: Pipeline Authoring
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
diff --git a/doc/development/bulk_import.md b/doc/development/bulk_import.md
index ff0c8a19ca1..a2620faed35 100644
--- a/doc/development/bulk_import.md
+++ b/doc/development/bulk_import.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# GitLab Group Migration
diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md
index efa8d4b0c41..463a7ee0e0b 100644
--- a/doc/development/contributing/design.md
+++ b/doc/development/contributing/design.md
@@ -35,7 +35,7 @@ Check these aspects both when _designing_ and _reviewing_ UI changes.
- Use clear and consistent [terminology](https://design.gitlab.com/content/terminology/).
- Check grammar and spelling.
- Consider help content and follow its [guidelines](https://design.gitlab.com/usability/helping-users/).
-- Request review from the [appropriate Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers),
+- Request review from the [appropriate Technical Writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments),
indicating any specific files or lines they should review, and how to preview
or understand the location/context of the text from the user's perspective.
diff --git a/doc/development/export_csv.md b/doc/development/export_csv.md
index ff827023a50..998e5b1fb3b 100644
--- a/doc/development/export_csv.md
+++ b/doc/development/export_csv.md
@@ -1,7 +1,7 @@
---
stage: Manage
group: Import
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Export to CSV
diff --git a/doc/install/aws/manual_install_aws.md b/doc/install/aws/manual_install_aws.md
index eeb8c57bddf..b0e71cbc77e 100644
--- a/doc/install/aws/manual_install_aws.md
+++ b/doc/install/aws/manual_install_aws.md
@@ -390,7 +390,7 @@ persistence and is used to store session data, temporary cache information, and
chance to deploy Redis in multiple availability zones.
1. In the settings section:
1. Give the cluster a name (`gitlab-redis`) and a description.
- 1. For the version, select the latest of the `5.0` series (for example, `5.0.6`).
+ 1. For the version, select the latest.
1. Leave the port as `6379` since this is what we used in our Redis security group above.
1. Select the node type (at least `cache.t3.medium`, but adjust to your needs) and the number of replicas.
1. In the advanced settings section:
diff --git a/doc/integration/security_partners/index.md b/doc/integration/security_partners/index.md
index 2c7641124a0..51c2c72769e 100644
--- a/doc/integration/security_partners/index.md
+++ b/doc/integration/security_partners/index.md
@@ -1,7 +1,7 @@
---
stage: Secure
group: Static Analysis
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: index
---
diff --git a/doc/update/package/convert_to_ee.md b/doc/update/package/convert_to_ee.md
index 887fdcb4f34..fe3bc8090ab 100644
--- a/doc/update/package/convert_to_ee.md
+++ b/doc/update/package/convert_to_ee.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Convert Community Edition to Enterprise Edition **(FREE SELF)**
diff --git a/doc/update/package/downgrade.md b/doc/update/package/downgrade.md
index 96b17a7632b..81f7d089bea 100644
--- a/doc/update/package/downgrade.md
+++ b/doc/update/package/downgrade.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Downgrade **(FREE SELF)**
diff --git a/doc/update/package/index.md b/doc/update/package/index.md
index d7c18f15e44..b6417e10917 100644
--- a/doc/update/package/index.md
+++ b/doc/update/package/index.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Upgrade GitLab by using the GitLab package **(FREE SELF)**
diff --git a/doc/user/admin_area/reporting/spamcheck.md b/doc/user/admin_area/reporting/spamcheck.md
index 7f5732d88ea..1021e4eb63a 100644
--- a/doc/user/admin_area/reporting/spamcheck.md
+++ b/doc/user/admin_area/reporting/spamcheck.md
@@ -1,7 +1,7 @@
---
stage: Enablement
group: Distribution
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Spamcheck anti-spam service **(PREMIUM SELF)**
diff --git a/doc/user/application_security/api_fuzzing/create_har_files.md b/doc/user/application_security/api_fuzzing/create_har_files.md
index db0b2a32bcf..1ba19359fde 100644
--- a/doc/user/application_security/api_fuzzing/create_har_files.md
+++ b/doc/user/application_security/api_fuzzing/create_har_files.md
@@ -1,7 +1,7 @@
---
stage: Secure
group: Dynamic Analysis
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: howto
---
diff --git a/doc/user/clusters/agent/gitops.md b/doc/user/clusters/agent/gitops.md
index 8e6449fe639..8f0e2255121 100644
--- a/doc/user/clusters/agent/gitops.md
+++ b/doc/user/clusters/agent/gitops.md
@@ -1,7 +1,7 @@
---
stage: Configure
group: Configure
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Using a GitOps workflow for Kubernetes **(PREMIUM)**
diff --git a/doc/user/clusters/agent/gitops/secrets_management.md b/doc/user/clusters/agent/gitops/secrets_management.md
index 01720bd602f..cf520c881bf 100644
--- a/doc/user/clusters/agent/gitops/secrets_management.md
+++ b/doc/user/clusters/agent/gitops/secrets_management.md
@@ -1,7 +1,7 @@
---
stage: Configure
group: Configure
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Managing Kubernetes secrets in a GitOps workflow
diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md
index 6def4513614..19132a7fe10 100644
--- a/doc/user/clusters/agent/repository.md
+++ b/doc/user/clusters/agent/repository.md
@@ -1,7 +1,7 @@
---
stage: Configure
group: Configure
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Working with the agent for Kubernetes **(FREE)**
diff --git a/doc/user/clusters/agent/vulnerabilities.md b/doc/user/clusters/agent/vulnerabilities.md
index 41a80f43467..480b09ff2ab 100644
--- a/doc/user/clusters/agent/vulnerabilities.md
+++ b/doc/user/clusters/agent/vulnerabilities.md
@@ -1,7 +1,7 @@
---
stage: Configure
group: Configure
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Container vulnerability scanning **(ULTIMATE)**
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
index f0ee9a8da56..5ea8512ed5a 100644
--- a/doc/user/group/value_stream_analytics/index.md
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -2,7 +2,7 @@
type: reference
stage: Manage
group: Optimize
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Value stream analytics for groups **(PREMIUM)**
diff --git a/doc/user/project/clusters/protect/container_host_security/index.md b/doc/user/project/clusters/protect/container_host_security/index.md
index a0297a7a86f..f6f31ee8f36 100644
--- a/doc/user/project/clusters/protect/container_host_security/index.md
+++ b/doc/user/project/clusters/protect/container_host_security/index.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Container Host Security **(FREE)**
diff --git a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md
index 4e56b7e5140..25e2f6ddb91 100644
--- a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md
+++ b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Getting started with Container Host Security **(FREE)**
diff --git a/doc/user/project/clusters/protect/container_network_security/index.md b/doc/user/project/clusters/protect/container_network_security/index.md
index 7176a1cf1b9..eeaf7e82ef4 100644
--- a/doc/user/project/clusters/protect/container_network_security/index.md
+++ b/doc/user/project/clusters/protect/container_network_security/index.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Container Network Security **(FREE)**
diff --git a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
index e6c91302d7b..43f4ea6c326 100644
--- a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
+++ b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Getting started with Container Network Security **(FREE)**
diff --git a/doc/user/project/clusters/protect/index.md b/doc/user/project/clusters/protect/index.md
index 473195f4c17..3a80132fb50 100644
--- a/doc/user/project/clusters/protect/index.md
+++ b/doc/user/project/clusters/protect/index.md
@@ -1,7 +1,7 @@
---
stage: Protect
group: Container Security
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Protecting your deployed applications **(FREE)**
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 5e310be4c9c..84a2449f481 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -2,7 +2,7 @@
type: howto
stage: Fulfillment
group: Utilization
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Storage usage quota **(FREE)**
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 194f5da0a5c..4bab94968d7 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -93,9 +93,9 @@ module Gitlab
end
end
- def page(title:, version: nil, dir: nil)
+ def page(title:, version: nil, dir: nil, load_content: true)
wrapped_gitaly_errors do
- gitaly_find_page(title: title, version: version, dir: dir)
+ gitaly_find_page(title: title, version: version, dir: dir, load_content: load_content)
end
end
@@ -121,10 +121,10 @@ module Gitlab
gitaly_wiki_client.update_page(page_path, title, format, content, commit_details)
end
- def gitaly_find_page(title:, version: nil, dir: nil)
+ def gitaly_find_page(title:, version: nil, dir: nil, load_content: true)
return unless title.present?
- wiki_page, version = gitaly_wiki_client.find_page(title: title, version: version, dir: dir)
+ wiki_page, version = gitaly_wiki_client.find_page(title: title, version: version, dir: dir, load_content: load_content)
return unless wiki_page
Gitlab::Git::WikiPage.new(wiki_page, version)
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index 3613cd01122..ca839b232cf 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -64,12 +64,13 @@ module Gitlab
GitalyClient.call(@repository.storage, :wiki_service, :wiki_update_page, enum, timeout: GitalyClient.medium_timeout)
end
- def find_page(title:, version: nil, dir: nil)
+ def find_page(title:, version: nil, dir: nil, load_content: true)
request = Gitaly::WikiFindPageRequest.new(
repository: @gitaly_repo,
title: encode_binary(title),
revision: encode_binary(version),
- directory: encode_binary(dir)
+ directory: encode_binary(dir),
+ skip_content: !load_content
)
response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_page, request, timeout: GitalyClient.fast_timeout)
diff --git a/lib/gitlab/json_cache.rb b/lib/gitlab/json_cache.rb
index d2916a01809..d5c018cfc68 100644
--- a/lib/gitlab/json_cache.rb
+++ b/lib/gitlab/json_cache.rb
@@ -43,7 +43,9 @@ module Gitlab
end
def write(key, value, options = nil)
- backend.write(cache_key(key), value.to_json, options)
+ # As we use json as the serialization format, return everything from
+ # ActiveModel objects included encrypted values.
+ backend.write(cache_key(key), value.to_json(unsafe_serialization_hash: true), options)
end
def fetch(key, options = {}, &block)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0c34c8b3556..cb2427fe50a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5885,6 +5885,9 @@ msgstr ""
msgid "Boards|An error occurred while fetching the board swimlanes. Please reload the page."
msgstr ""
+msgid "Boards|An error occurred while fetching the board. Please reload the page."
+msgstr ""
+
msgid "Boards|An error occurred while generating lists. Please reload the page."
msgstr ""
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
new file mode 100644
index 00000000000..484c32956e3
--- /dev/null
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_migration_pipeline_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require_relative 'gitlab_project_migration_common'
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Gitlab migration' do
+ include_context 'with gitlab project migration'
+
+ context 'with ci pipeline' do
+ let!(:source_project_with_readme) { true }
+
+ let(:source_pipelines) do
+ source_project.pipelines.map do |pipeline|
+ pipeline.except(:id, :web_url, :project_id)
+ end
+ end
+
+ let(:imported_pipelines) do
+ imported_project.pipelines.map do |pipeline|
+ pipeline.except(:id, :web_url, :project_id)
+ end
+ end
+
+ before do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.api_client = api_client
+ commit.project = source_project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YML
+ test-success:
+ script: echo 'OK'
+ YML
+ }
+ ]
+ )
+ end
+
+ Support::Waiter.wait_until(max_duration: 10, sleep_interval: 1) do
+ !source_project.pipelines.empty?
+ end
+ end
+
+ it(
+ 'successfully imports ci pipeline',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/354650'
+ ) do
+ expect_import_finished
+
+ expect(imported_pipelines).to eq(source_pipelines)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
index a86b8903645..70f19e9f3d7 100644
--- a/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
+++ b/qa/qa/specs/features/api/1_manage/migration/gitlab_project_migration_common.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
module QA
- # Disable on staging until bulk_import_projects toggle is on by default
+ # Disable on live envs until bulk_import_projects toggle is on by default
# Otherwise tests running in parallel can disable feature in the middle of other test
- RSpec.shared_context 'with gitlab project migration', :requires_admin, except: { subdomain: :staging } do
+ RSpec.shared_context 'with gitlab project migration', :requires_admin, :skip_live_env do
let(:source_project_with_readme) { false }
let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
let(:admin_api_client) { Runtime::API::Client.as_admin }
@@ -79,8 +79,6 @@ module QA
# Log failures for easier debugging
Runtime::Logger.warn("Import failures: #{import_failures}") if example.exception && !import_failures.empty?
ensure
- Runtime::Feature.disable(:bulk_import_projects)
-
user.remove_via_api!
end
end
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index 6951d8298e5..d472134a2c7 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -7,10 +7,6 @@ RSpec.describe 'Copy as GFM', :js do
include RepoHelpers
include ActionView::Helpers::JavaScriptHelper
- before do
- stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350454
- end
-
describe 'Copying rendered GFM' do
before do
@feat = MarkdownFeature.new
@@ -764,8 +760,8 @@ RSpec.describe 'Copy as GFM', :js do
context 'selecting one word of text' do
it 'copies as inline code' do
verify(
- '.line[id="LC9"] .no',
- '`RuntimeError`'
+ '.line[id="LC10"]',
+ '`end`'
)
end
end
@@ -834,6 +830,7 @@ RSpec.describe 'Copy as GFM', :js do
end
def verify(selector, gfm, target: nil)
+ expect(page).to have_selector('.js-syntax-highlight')
html = html_for_selector(selector)
output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target)
wait_for_requests
diff --git a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
index e8a14694d88..eea7e070a35 100644
--- a/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
+++ b/spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'User triggers manual job with variables', :js do
wait_for_requests
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'key_name', 'value' => 'key_value'))
end
end
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 26a5bf34595..0c044deb78c 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -41,6 +41,7 @@ describe('BoardsSelector', () => {
...defaultStore,
actions: {
setError: jest.fn(),
+ setBoardConfig: jest.fn(),
},
getters: {
isGroupBoard: () => isGroupBoard,
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 24096fddea6..ec9342cffc2 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -8,6 +8,37 @@ import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
import ReleaseToken from '~/vue_shared/components/filtered_search_bar/tokens/release_token.vue';
+export const mockBoard = {
+ milestone: {
+ id: 'gid://gitlab/Milestone/114',
+ title: '14.9',
+ },
+ iteration: {
+ id: 'gid://gitlab/Iteration/124',
+ title: 'Iteration 9',
+ },
+ assignee: {
+ id: 'gid://gitlab/User/1',
+ username: 'admin',
+ },
+ labels: {
+ nodes: [{ id: 'gid://gitlab/Label/32', title: 'Deliverable' }],
+ },
+ weight: 2,
+};
+
+export const mockBoardConfig = {
+ milestoneId: 'gid://gitlab/Milestone/114',
+ milestoneTitle: '14.9',
+ iterationId: 'gid://gitlab/Iteration/124',
+ iterationTitle: 'Iteration 9',
+ assigneeId: 'gid://gitlab/User/1',
+ assigneeUsername: 'admin',
+ labels: [{ id: 'gid://gitlab/Label/32', title: 'Deliverable' }],
+ labelIds: ['gid://gitlab/Label/32'],
+ weight: 2,
+};
+
export const boardObj = {
id: 1,
name: 'test',
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index 0eca0cb3ee5..ad661a31556 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -32,6 +32,8 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import projectBoardMilestones from '~/boards/graphql/project_board_milestones.query.graphql';
import groupBoardMilestones from '~/boards/graphql/group_board_milestones.query.graphql';
import {
+ mockBoard,
+ mockBoardConfig,
mockLists,
mockListsById,
mockIssue,
@@ -60,6 +62,52 @@ beforeEach(() => {
window.gon = { features: {} };
});
+describe('fetchBoard', () => {
+ const payload = {
+ fullPath: 'gitlab-org',
+ fullBoardId: 'gid://gitlab/Board/1',
+ boardType: 'project',
+ };
+
+ const queryResponse = {
+ data: {
+ workspace: {
+ board: mockBoard,
+ },
+ },
+ };
+
+ it('should commit mutation RECEIVE_BOARD_SUCCESS and dispatch setBoardConfig on success', async () => {
+ jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
+
+ await testAction({
+ action: actions.fetchBoard,
+ payload,
+ expectedMutations: [
+ {
+ type: types.RECEIVE_BOARD_SUCCESS,
+ payload: mockBoard,
+ },
+ ],
+ expectedActions: [{ type: 'setBoardConfig', payload: mockBoard }],
+ });
+ });
+
+ it('should commit mutation RECEIVE_BOARD_FAILURE on failure', async () => {
+ jest.spyOn(gqlClient, 'query').mockResolvedValue(Promise.reject());
+
+ await testAction({
+ action: actions.fetchBoard,
+ payload,
+ expectedMutations: [
+ {
+ type: types.RECEIVE_BOARD_FAILURE,
+ },
+ ],
+ });
+ });
+});
+
describe('setInitialBoardData', () => {
it('sets data object', () => {
const mockData = {
@@ -67,13 +115,21 @@ describe('setInitialBoardData', () => {
bar: 'baz',
};
- return testAction(
- actions.setInitialBoardData,
- mockData,
- {},
- [{ type: types.SET_INITIAL_BOARD_DATA, payload: mockData }],
- [],
- );
+ return testAction({
+ action: actions.setInitialBoardData,
+ payload: mockData,
+ expectedMutations: [{ type: types.SET_INITIAL_BOARD_DATA, payload: mockData }],
+ });
+ });
+});
+
+describe('setBoardConfig', () => {
+ it('sets board config object from board object', () => {
+ return testAction({
+ action: actions.setBoardConfig,
+ payload: mockBoard,
+ expectedMutations: [{ type: types.SET_BOARD_CONFIG, payload: mockBoardConfig }],
+ });
});
});
@@ -87,7 +143,7 @@ describe('setFilters', () => {
},
],
[
- "and use 'assigneeWildcardId' as filter variable for 'assigneId' param",
+ "and use 'assigneeWildcardId' as filter variable for 'assigneeId' param",
{
filters: { assigneeId: 'None' },
filterVariables: { assigneeWildcardId: 'NONE', not: {} },
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index 0e830258327..738737bf4b6 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -4,6 +4,7 @@ import * as types from '~/boards/stores/mutation_types';
import mutations from '~/boards/stores/mutations';
import defaultState from '~/boards/stores/state';
import {
+ mockBoard,
mockLists,
rawIssue,
mockIssue,
@@ -33,6 +34,27 @@ describe('Board Store Mutations', () => {
state = defaultState();
});
+ describe('RECEIVE_BOARD_SUCCESS', () => {
+ it('Should set board to state', () => {
+ mutations[types.RECEIVE_BOARD_SUCCESS](state, mockBoard);
+
+ expect(state.board).toEqual({
+ ...mockBoard,
+ labels: mockBoard.labels.nodes,
+ });
+ });
+ });
+
+ describe('RECEIVE_BOARD_FAILURE', () => {
+ it('Should set error in state', () => {
+ mutations[types.RECEIVE_BOARD_FAILURE](state);
+
+ expect(state.error).toEqual(
+ 'An error occurred while fetching the board. Please reload the page.',
+ );
+ });
+ });
+
describe('SET_INITIAL_BOARD_DATA', () => {
it('Should set initial Boards data to state', () => {
const allowSubEpics = true;
@@ -40,9 +62,6 @@ describe('Board Store Mutations', () => {
const fullPath = 'gitlab-org';
const boardType = 'group';
const disabled = false;
- const boardConfig = {
- milestoneTitle: 'Milestone 1',
- };
const issuableType = issuableTypes.issue;
mutations[types.SET_INITIAL_BOARD_DATA](state, {
@@ -51,7 +70,6 @@ describe('Board Store Mutations', () => {
fullPath,
boardType,
disabled,
- boardConfig,
issuableType,
});
@@ -60,11 +78,23 @@ describe('Board Store Mutations', () => {
expect(state.fullPath).toEqual(fullPath);
expect(state.boardType).toEqual(boardType);
expect(state.disabled).toEqual(disabled);
- expect(state.boardConfig).toEqual(boardConfig);
expect(state.issuableType).toEqual(issuableType);
});
});
+ describe('SET_BOARD_CONFIG', () => {
+ it('Should set board config data o state', () => {
+ const boardConfig = {
+ milestoneId: 1,
+ milestoneTitle: 'Milestone 1',
+ };
+
+ mutations[types.SET_BOARD_CONFIG](state, boardConfig);
+
+ expect(state.boardConfig).toEqual(boardConfig);
+ });
+ });
+
describe('RECEIVE_BOARD_LISTS_SUCCESS', () => {
it('Should set boardLists to state', () => {
mutations[types.RECEIVE_BOARD_LISTS_SUCCESS](state, initialBoardListsState);
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index a78dab1a44d..a5877aa6e3e 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -181,12 +181,13 @@ describe('init markdown', () => {
${'- [ ] item'} | ${'- [ ] item\n- [ ] '}
${'- [x] item'} | ${'- [x] item\n- [x] '}
${'- item\n - second'} | ${'- item\n - second\n - '}
- ${'1. item'} | ${'1. item\n1. '}
- ${'1. [ ] item'} | ${'1. [ ] item\n1. [ ] '}
- ${'1. [x] item'} | ${'1. [x] item\n1. [x] '}
- ${'108. item'} | ${'108. item\n108. '}
+ ${'1. item'} | ${'1. item\n2. '}
+ ${'1. [ ] item'} | ${'1. [ ] item\n2. [ ] '}
+ ${'1. [x] item'} | ${'1. [x] item\n2. [x] '}
+ ${'108. item'} | ${'108. item\n109. '}
${'108. item\n - second'} | ${'108. item\n - second\n - '}
- ${'108. item\n 1. second'} | ${'108. item\n 1. second\n 1. '}
+ ${'108. item\n 1. second'} | ${'108. item\n 1. second\n 2. '}
+ ${'non-item, will not change'} | ${'non-item, will not change'}
`('adds correct list continuation characters', ({ text, expected }) => {
textArea.value = text;
textArea.setSelectionRange(text.length, text.length);
@@ -207,10 +208,10 @@ describe('init markdown', () => {
${'- [ ] item\n- [ ] '} | ${'- [ ] item\n'}
${'- [x] item\n- [x] '} | ${'- [x] item\n'}
${'- item\n - second\n - '} | ${'- item\n - second\n'}
- ${'1. item\n1. '} | ${'1. item\n'}
- ${'1. [ ] item\n1. [ ] '} | ${'1. [ ] item\n'}
- ${'1. [x] item\n1. [x] '} | ${'1. [x] item\n'}
- ${'108. item\n108. '} | ${'108. item\n'}
+ ${'1. item\n2. '} | ${'1. item\n'}
+ ${'1. [ ] item\n2. [ ] '} | ${'1. [ ] item\n'}
+ ${'1. [x] item\n2. [x] '} | ${'1. [x] item\n'}
+ ${'108. item\n109. '} | ${'108. item\n'}
${'108. item\n - second\n - '} | ${'108. item\n - second\n'}
${'108. item\n 1. second\n 1. '} | ${'108. item\n 1. second\n'}
`('adds correct list continuation characters', ({ text, expected }) => {
@@ -243,6 +244,23 @@ describe('init markdown', () => {
expect(textArea.value).toEqual(expected);
});
+ it.each`
+ text | add_at | expected
+ ${'1. one\n2. two\n3. three'} | ${13} | ${'1. one\n2. two\n2. \n3. three'}
+ ${'108. item\n 5. second\n 6. six\n 7. seven'} | ${36} | ${'108. item\n 5. second\n 6. six\n 6. \n 7. seven'}
+ `(
+ 'adds correct numbered continuation characters when in middle of list',
+ ({ text, add_at, expected }) => {
+ textArea.value = text;
+ textArea.setSelectionRange(add_at, add_at);
+
+ textArea.addEventListener('keydown', keypressNoteText);
+ textArea.dispatchEvent(enterEvent);
+
+ expect(textArea.value).toEqual(expected);
+ },
+ );
+
it('does nothing if feature flag disabled', () => {
gon.features = { markdownContinueLists: false };
@@ -262,8 +280,8 @@ describe('init markdown', () => {
});
describe('with selection', () => {
- const text = 'initial selected value';
- const selected = 'selected';
+ let text = 'initial selected value';
+ let selected = 'selected';
let selectedIndex;
beforeEach(() => {
@@ -409,6 +427,46 @@ describe('init markdown', () => {
expectedText.indexOf(expectedSelectionText, 1) + expectedSelectionText.length,
);
});
+
+ it('adds block tags on line above and below selection', () => {
+ selected = 'this text\nis multiple\nlines';
+ text = `before \n${selected}\nafter `;
+
+ textArea.value = text;
+ selectedIndex = text.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+
+ insertMarkdownText({
+ textArea,
+ text,
+ tag: '',
+ blockTag: '***',
+ selected,
+ wrap: true,
+ });
+
+ expect(textArea.value).toEqual(`before \n***\n${selected}\n***\nafter `);
+ });
+
+ it('removes block tags on line above and below selection', () => {
+ selected = 'this text\nis multiple\nlines';
+ text = `before \n***\n${selected}\n***\nafter `;
+
+ textArea.value = text;
+ selectedIndex = text.indexOf(selected);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + selected.length);
+
+ insertMarkdownText({
+ textArea,
+ text,
+ tag: '',
+ blockTag: '***',
+ selected,
+ wrap: true,
+ });
+
+ expect(textArea.value).toEqual(`before \n${selected}\nafter `);
+ });
});
});
});
@@ -460,7 +518,31 @@ describe('init markdown', () => {
expect(editor.replaceSelectedText).toHaveBeenCalledWith(`***\n${selected}\n***\n`, undefined);
});
- it('uses ace editor to navigate back tag length when nothing is selected', () => {
+ it('removes block tags on line above and below selection', () => {
+ const selected = 'this text\nis multiple\nlines';
+ const text = `before\n***\n${selected}\n***\nafter`;
+
+ editor.getSelection = jest.fn().mockReturnValue({
+ startLineNumber: 2,
+ startColumn: 1,
+ endLineNumber: 4,
+ endColumn: 2,
+ setSelectionRange: jest.fn(),
+ });
+
+ insertMarkdownText({
+ text,
+ tag: '',
+ blockTag: '***',
+ selected,
+ wrap: true,
+ editor,
+ });
+
+ expect(editor.replaceSelectedText).toHaveBeenCalledWith(`${selected}\n`, undefined);
+ });
+
+ it('uses editor to navigate back tag length when nothing is selected', () => {
editor.getSelection = jest.fn().mockReturnValue({
startLineNumber: 1,
startColumn: 1,
@@ -480,7 +562,7 @@ describe('init markdown', () => {
expect(editor.moveCursor).toHaveBeenCalledWith(-1);
});
- it('ace editor does not navigate back when there is selected text', () => {
+ it('editor does not navigate back when there is selected text', () => {
insertMarkdownText({
text: editor.getValue,
tag: '*',
diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
index 2010bac7060..ab579945e22 100644
--- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
@@ -7,6 +7,7 @@ import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vu
import { ROUGE_TO_HLJS_LANGUAGE_MAP } from '~/vue_shared/components/source_viewer/constants';
import LineNumbers from '~/vue_shared/components/line_numbers.vue';
import waitForPromises from 'helpers/wait_for_promises';
+import * as sourceViewerUtils from '~/vue_shared/components/source_viewer/utils';
jest.mock('highlight.js/lib/core');
Vue.use(VueRouter);
@@ -36,6 +37,7 @@ describe('Source Viewer component', () => {
beforeEach(() => {
hljs.highlight.mockImplementation(() => ({ value: highlightedContent }));
hljs.highlightAuto.mockImplementation(() => ({ value: highlightedContent }));
+ jest.spyOn(sourceViewerUtils, 'wrapLines');
return createComponent();
});
@@ -73,6 +75,10 @@ describe('Source Viewer component', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
+ it('calls the wrapLines helper method with highlightedContent and mappedLanguage', () => {
+ expect(sourceViewerUtils.wrapLines).toHaveBeenCalledWith(highlightedContent, mappedLanguage);
+ });
+
it('renders Line Numbers', () => {
expect(findLineNumbers().props('lines')).toBe(1);
});
diff --git a/spec/frontend/vue_shared/components/source_viewer/utils_spec.js b/spec/frontend/vue_shared/components/source_viewer/utils_spec.js
index 937c3b26c67..0631e7efd54 100644
--- a/spec/frontend/vue_shared/components/source_viewer/utils_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/utils_spec.js
@@ -2,12 +2,25 @@ import { wrapLines } from '~/vue_shared/components/source_viewer/utils';
describe('Wrap lines', () => {
it.each`
- input | output
- ${'line 1'} | ${'<span id="LC1" class="line">line 1</span>'}
- ${'line 1\nline 2'} | ${`<span id="LC1" class="line">line 1</span>\n<span id="LC2" class="line">line 2</span>`}
- ${'<span class="hljs-code">line 1\nline 2</span>'} | ${`<span id="LC1" class="hljs-code">line 1\n<span id="LC2" class="line">line 2</span></span>`}
- ${'<span class="hljs-code">```bash'} | ${'<span id="LC1" class="hljs-code">```bash'}
- `('returns lines wrapped in spans containing line numbers', ({ input, output }) => {
- expect(wrapLines(input)).toBe(output);
+ content | language | output
+ ${'line 1'} | ${'javascript'} | ${'<span id="LC1" lang="javascript" class="line">line 1</span>'}
+ ${'line 1\nline 2'} | ${'html'} | ${`<span id="LC1" lang="html" class="line">line 1</span>\n<span id="LC2" lang="html" class="line">line 2</span>`}
+ ${'<span class="hljs-code">line 1\nline 2</span>'} | ${'html'} | ${`<span id="LC1" lang="html" class="hljs-code">line 1\n<span id="LC2" lang="html" class="line">line 2</span></span>`}
+ ${'<span class="hljs-code">```bash'} | ${'bash'} | ${'<span id="LC1" lang="bash" class="hljs-code">```bash'}
+ ${'<span class="hljs-code">```bash'} | ${'valid-language1'} | ${'<span id="LC1" lang="valid-language1" class="hljs-code">```bash'}
+ ${'<span class="hljs-code">```bash'} | ${'valid_language2'} | ${'<span id="LC1" lang="valid_language2" class="hljs-code">```bash'}
+ `('returns lines wrapped in spans containing line numbers', ({ content, language, output }) => {
+ expect(wrapLines(content, language)).toBe(output);
+ });
+
+ it.each`
+ language
+ ${'invalidLanguage>'}
+ ${'"invalidLanguage"'}
+ ${'<invalidLanguage'}
+ `('returns lines safely without XSS language is not valid', ({ language }) => {
+ expect(wrapLines('<span class="hljs-code">```bash', language)).toBe(
+ '<span id="LC1" lang="" class="hljs-code">```bash',
+ );
});
});
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index ee0c0e2708e..dddcf8c40fc 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -48,14 +48,26 @@ RSpec.describe Gitlab::Git::Wiki do
end
it 'returns the right page' do
- expect(subject.page(title: 'page1', dir: '').url_path).to eq 'page1'
- expect(subject.page(title: 'page1', dir: 'foo').url_path).to eq 'foo/page1'
+ page = subject.page(title: 'page1', dir: '')
+ expect(page.url_path).to eq 'page1'
+ expect(page.raw_data).to eq 'content'
+
+ page = subject.page(title: 'page1', dir: 'foo')
+ expect(page.url_path).to eq 'foo/page1'
+ expect(page.raw_data).to eq 'content foo/page1'
end
it 'returns nil for invalid arguments' do
expect(subject.page(title: '')).to be_nil
expect(subject.page(title: 'foo', version: ':')).to be_nil
end
+
+ it 'does not return content if load_content param is set to false' do
+ page = subject.page(title: 'page1', dir: '', load_content: false)
+
+ expect(page.url_path).to eq 'page1'
+ expect(page.raw_data).to be_empty
+ end
end
describe '#preview_slug' do
diff --git a/spec/models/concerns/sensitive_serializable_hash_spec.rb b/spec/models/concerns/sensitive_serializable_hash_spec.rb
new file mode 100644
index 00000000000..923f9e80c1f
--- /dev/null
+++ b/spec/models/concerns/sensitive_serializable_hash_spec.rb
@@ -0,0 +1,150 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe SensitiveSerializableHash do
+ describe '.prevent_from_serialization' do
+ let(:test_class) do
+ Class.new do
+ include ActiveModel::Serialization
+ include SensitiveSerializableHash
+
+ attr_accessor :name, :super_secret
+
+ prevent_from_serialization :super_secret
+
+ def attributes
+ { 'name' => nil, 'super_secret' => nil }
+ end
+ end
+ end
+
+ let(:model) { test_class.new }
+
+ it 'does not include the field in serializable_hash' do
+ expect(model.serializable_hash).not_to include('super_secret')
+ end
+
+ context 'unsafe_serialization_hash option' do
+ it 'includes the field in serializable_hash' do
+ expect(model.serializable_hash(unsafe_serialization_hash: true)).to include('super_secret')
+ end
+ end
+
+ context 'when prevent_sensitive_fields_from_serializable_hash feature flag is disabled' do
+ before do
+ stub_feature_flags(prevent_sensitive_fields_from_serializable_hash: false)
+ end
+
+ it 'includes the field in serializable_hash' do
+ expect(model.serializable_hash).to include('super_secret')
+ end
+ end
+ end
+
+ describe '#serializable_hash' do
+ shared_examples "attr_encrypted attribute" do |klass, attribute_name|
+ context "#{klass.name}\##{attribute_name}" do
+ let(:attributes) { [attribute_name, "encrypted_#{attribute_name}", "encrypted_#{attribute_name}_iv"] }
+
+ it 'has a encrypted_attributes field' do
+ expect(klass.encrypted_attributes).to include(attribute_name.to_sym)
+ end
+
+ it 'does not include the attribute in serializable_hash', :aggregate_failures do
+ attributes.each do |attribute|
+ expect(model.attributes).to include(attribute) # double-check the attribute does exist
+
+ expect(model.serializable_hash).not_to include(attribute)
+ expect(model.to_json).not_to include(attribute)
+ expect(model.as_json).not_to include(attribute)
+ end
+ end
+
+ context 'unsafe_serialization_hash option' do
+ it 'includes the field in serializable_hash' do
+ attributes.each do |attribute|
+ expect(model.attributes).to include(attribute) # double-check the attribute does exist
+
+ expect(model.serializable_hash(unsafe_serialization_hash: true)).to include(attribute)
+ expect(model.to_json(unsafe_serialization_hash: true)).to include(attribute)
+ expect(model.as_json(unsafe_serialization_hash: true)).to include(attribute)
+ end
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'attr_encrypted attribute', WebHook, 'token' do
+ let_it_be(:model) { create(:system_hook) }
+ end
+
+ it_behaves_like 'attr_encrypted attribute', Ci::InstanceVariable, 'value' do
+ let_it_be(:model) { create(:ci_instance_variable) }
+ end
+
+ shared_examples "add_authentication_token_field attribute" do |klass, attribute_name, encrypted_attribute: true, digest_attribute: false|
+ context "#{klass.name}\##{attribute_name}" do
+ let(:attributes) do
+ if digest_attribute
+ ["#{attribute_name}_digest"]
+ elsif encrypted_attribute
+ [attribute_name, "#{attribute_name}_encrypted"]
+ else
+ [attribute_name]
+ end
+ end
+
+ it 'has a add_authentication_token_field field' do
+ expect(klass.token_authenticatable_fields).to include(attribute_name.to_sym)
+ end
+
+ it 'does not include the attribute in serializable_hash', :aggregate_failures do
+ attributes.each do |attribute|
+ expect(model.attributes).to include(attribute) # double-check the attribute does exist
+
+ expect(model.serializable_hash).not_to include(attribute)
+ expect(model.to_json).not_to include(attribute)
+ expect(model.as_json).not_to include(attribute)
+ end
+ end
+
+ context 'unsafe_serialization_hash option' do
+ it 'includes the field in serializable_hash' do
+ attributes.each do |attribute|
+ expect(model.attributes).to include(attribute) # double-check the attribute does exist
+
+ expect(model.serializable_hash(unsafe_serialization_hash: true)).to include(attribute)
+ expect(model.to_json(unsafe_serialization_hash: true)).to include(attribute)
+ expect(model.as_json(unsafe_serialization_hash: true)).to include(attribute)
+ end
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'add_authentication_token_field attribute', Ci::Runner, 'token' do
+ let_it_be(:model) { create(:ci_runner) }
+
+ it 'does not include token_expires_at in serializable_hash' do
+ attribute = 'token_expires_at'
+
+ expect(model.attributes).to include(attribute) # double-check the attribute does exist
+
+ expect(model.serializable_hash).not_to include(attribute)
+ expect(model.to_json).not_to include(attribute)
+ expect(model.as_json).not_to include(attribute)
+ end
+ end
+
+ it_behaves_like 'add_authentication_token_field attribute', ApplicationSetting, 'health_check_access_token', encrypted_attribute: false do
+ # health_check_access_token_encrypted column does not exist
+ let_it_be(:model) { create(:application_setting) }
+ end
+
+ it_behaves_like 'add_authentication_token_field attribute', PersonalAccessToken, 'token', encrypted_attribute: false, digest_attribute: true do
+ # PersonalAccessToken only has token_digest column
+ let_it_be(:model) { create(:personal_access_token) }
+ end
+ end
+end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 8a9e0248ed3..5fec8ad1113 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -9,6 +9,12 @@ RSpec.shared_examples 'TokenAuthenticatable' do
it { is_expected.to respond_to("set_#{token_field}") }
it { is_expected.to respond_to("reset_#{token_field}!") }
end
+
+ describe 'SensitiveSerializableHash' do
+ it 'includes the token field in list of sensitive attributes prevented from serialization' do
+ expect(described_class.attributes_exempt_from_serializable_hash).to include(token_field)
+ end
+ end
end
RSpec.describe User, 'TokenAuthenticatable' do
diff --git a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
index bccef9b9554..89ddc797a9d 100644
--- a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
@@ -6,6 +6,24 @@ RSpec.describe TokenAuthenticatableStrategies::Base do
let(:instance) { double(:instance) }
let(:field) { double(:field) }
+ describe '#token_fields' do
+ let(:strategy) { described_class.new(instance, field, options) }
+ let(:field) { 'some_token' }
+ let(:options) { {} }
+
+ it 'includes the token field' do
+ expect(strategy.token_fields).to contain_exactly(field)
+ end
+
+ context 'with expires_at option' do
+ let(:options) { { expires_at: true } }
+
+ it 'includes the token_expires_at field' do
+ expect(strategy.token_fields).to contain_exactly(field, 'some_token_expires_at')
+ end
+ end
+ end
+
describe '.fabricate' do
context 'when digest stragegy is specified' do
it 'fabricates digest strategy object' do
diff --git a/spec/models/concerns/token_authenticatable_strategies/digest_spec.rb b/spec/models/concerns/token_authenticatable_strategies/digest_spec.rb
new file mode 100644
index 00000000000..bcd6e1e7316
--- /dev/null
+++ b/spec/models/concerns/token_authenticatable_strategies/digest_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe TokenAuthenticatableStrategies::Digest do
+ let(:model) { class_double('Project') }
+ let(:options) { { digest: true } }
+
+ subject(:strategy) do
+ described_class.new(model, 'some_field', options)
+ end
+
+ describe '#token_fields' do
+ it 'includes the digest field' do
+ expect(strategy.token_fields).to contain_exactly('some_field', 'some_field_digest')
+ end
+ end
+end
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index 458dfb47394..e0ebb86585a 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -14,10 +14,18 @@ RSpec.describe TokenAuthenticatableStrategies::Encrypted do
Gitlab::CryptoHelper.aes256_gcm_encrypt('my-value')
end
- subject do
+ subject(:strategy) do
described_class.new(model, 'some_field', options)
end
+ describe '#token_fields' do
+ let(:options) { { encrypted: :required } }
+
+ it 'includes the encrypted field' do
+ expect(strategy.token_fields).to contain_exactly('some_field', 'some_field_encrypted')
+ end
+ end
+
describe '#find_token_authenticatable' do
context 'when encryption is required' do
let(:options) { { encrypted: :required } }
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index 2d309bfe425..b8487e438a9 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -175,7 +175,7 @@ RSpec.describe Ci::JobArtifacts::CreateService do
end
expect(subject[:status]).to eq(:success)
- expect(job.job_variables.as_json).to contain_exactly(
+ expect(job.job_variables.as_json(only: [:key, :value, :source])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR1', 'source' => 'dotenv'),
hash_including('key' => 'KEY2', 'value' => 'VAR2', 'source' => 'dotenv'))
end
diff --git a/spec/services/ci/parse_dotenv_artifact_service_spec.rb b/spec/services/ci/parse_dotenv_artifact_service_spec.rb
index 6bf22b7c8b2..aaab849cd93 100644
--- a/spec/services/ci/parse_dotenv_artifact_service_spec.rb
+++ b/spec/services/ci/parse_dotenv_artifact_service_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the artifact' do
expect(subject[:status]).to eq(:success)
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR1'),
hash_including('key' => 'KEY2', 'value' => 'VAR2'))
end
@@ -57,7 +57,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
expect(subject[:status]).to eq(:success)
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR4'),
hash_including('key' => 'KEY2', 'value' => 'VAR3'))
end
@@ -101,7 +101,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'trims the trailing space' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR1'))
end
end
@@ -112,7 +112,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the dotenv data' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY', 'value' => 'VARCONTAINING=EQLS'))
end
end
@@ -133,7 +133,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the dotenv data' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'skateboard', 'value' => '🛹'))
end
end
@@ -154,7 +154,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the dotenv data' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'V A R 1'))
end
end
@@ -165,7 +165,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the value as-is' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => '"VAR1"'))
end
end
@@ -176,7 +176,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the value as-is' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => "'VAR1'"))
end
end
@@ -187,7 +187,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the value as-is' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => '" VAR1 "'))
end
end
@@ -208,7 +208,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'parses the dotenv data' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => ''))
end
end
@@ -250,7 +250,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'does not support variable expansion in dotenv parser' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR1'),
hash_including('key' => 'KEY2', 'value' => '${KEY1}_Test'))
end
@@ -284,7 +284,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do
it 'does not support comment in dotenv parser' do
subject
- expect(build.job_variables.as_json).to contain_exactly(
+ expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly(
hash_including('key' => 'KEY1', 'value' => 'VAR1 # This is variable'))
end
end
diff --git a/tooling/graphql/docs/helper.rb b/tooling/graphql/docs/helper.rb
index ca153c806c4..e4f14129f3b 100644
--- a/tooling/graphql/docs/helper.rb
+++ b/tooling/graphql/docs/helper.rb
@@ -57,7 +57,7 @@ module Tooling
---
stage: Ecosystem
group: Integrations
- info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+ info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
<!---