Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
75f2fd2ba8
commit
804348d39b
|
|
@ -2531,8 +2531,9 @@
|
|||
- "scripts/rspec_helpers.sh"
|
||||
- <<: *if-merge-request
|
||||
changes:
|
||||
- "gems/gitlab-rspec"
|
||||
- "gems/rspec_flaky"
|
||||
- "gems/gitlab-rspec/**/*"
|
||||
- "gems/rspec_flaky/**/*"
|
||||
- "scripts/flaky_examples/prune-old-flaky-examples"
|
||||
|
||||
###################
|
||||
# workhorse rules #
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ update-tests-metadata:
|
|||
- rspec-ee system pg14
|
||||
- rspec-ee background_migration pg14
|
||||
script:
|
||||
- run_timed_command "retry gem install fog-aws mime-types activesupport rspec rspec_profiling postgres-copy --no-document"
|
||||
- run_timed_command "retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document"
|
||||
- source ./scripts/rspec_helpers.sh
|
||||
- test -f "${FLAKY_RSPEC_SUITE_REPORT_PATH}" || echo -e "\e[31m" 'Consider add ~"pipeline:run-all-rspec" to run full rspec jobs' "\e[0m"
|
||||
- update_tests_metadata
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
# Cop supports --autocorrect.
|
||||
Gitlab/StrongMemoizeAttr:
|
||||
Details: grace period
|
||||
Exclude:
|
||||
- 'app/components/pajamas/avatar_component.rb'
|
||||
- 'app/controllers/application_controller.rb'
|
||||
|
|
@ -27,7 +28,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'app/controllers/projects/incidents_controller.rb'
|
||||
- 'app/controllers/projects/merge_requests/drafts_controller.rb'
|
||||
- 'app/controllers/projects/merge_requests_controller.rb'
|
||||
- 'app/controllers/projects/metrics_dashboard_controller.rb'
|
||||
- 'app/controllers/projects/milestones_controller.rb'
|
||||
- 'app/controllers/projects/pipelines/application_controller.rb'
|
||||
- 'app/controllers/projects/pipelines_controller.rb'
|
||||
|
|
@ -52,7 +52,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'app/finders/merge_requests_finder/params.rb'
|
||||
- 'app/finders/projects/members/effective_access_level_finder.rb'
|
||||
- 'app/finders/releases/evidence_pipeline_finder.rb'
|
||||
- 'app/finders/releases_finder.rb'
|
||||
- 'app/finders/snippets_finder.rb'
|
||||
- 'app/finders/todos_finder.rb'
|
||||
- 'app/graphql/resolvers/issue_status_counts_resolver.rb'
|
||||
|
|
@ -125,7 +124,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'app/models/namespaces/traversal/linear.rb'
|
||||
- 'app/models/namespaces/traversal/recursive.rb'
|
||||
- 'app/models/note.rb'
|
||||
- 'app/models/pages/lookup_path.rb'
|
||||
- 'app/models/project.rb'
|
||||
- 'app/models/release.rb'
|
||||
- 'app/models/resource_event.rb'
|
||||
|
|
@ -134,7 +132,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'app/models/snippet_input_action_collection.rb'
|
||||
- 'app/models/state_note.rb'
|
||||
- 'app/models/tree.rb'
|
||||
- 'app/models/uploads/fog.rb'
|
||||
- 'app/models/user.rb'
|
||||
- 'app/models/wiki_page.rb'
|
||||
- 'app/models/work_item.rb'
|
||||
|
|
@ -249,7 +246,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'app/workers/merge_request_cleanup_refs_worker.rb'
|
||||
- 'app/workers/packages/cleanup/execute_policy_worker.rb'
|
||||
- 'app/workers/packages/debian/generate_distribution_worker.rb'
|
||||
- 'app/workers/packages/debian/process_changes_worker.rb'
|
||||
- 'app/workers/packages/maven/metadata/sync_worker.rb'
|
||||
- 'app/workers/projects/inactive_projects_deletion_cron_worker.rb'
|
||||
- 'ee/app/controllers/admin/audit_logs_controller.rb'
|
||||
|
|
@ -283,10 +279,10 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/app/helpers/ee/preferences_helper.rb'
|
||||
- 'ee/app/helpers/ee/registrations_helper.rb'
|
||||
- 'ee/app/helpers/ee/timeboxes_helper.rb'
|
||||
- 'ee/app/helpers/trials_helper.rb'
|
||||
- 'ee/app/helpers/welcome_helper.rb'
|
||||
- 'ee/app/helpers/license_monitoring_helper.rb'
|
||||
- 'ee/app/helpers/subscriptions_helper.rb'
|
||||
- 'ee/app/helpers/trials_helper.rb'
|
||||
- 'ee/app/helpers/welcome_helper.rb'
|
||||
- 'ee/app/models/approval_merge_request_rule.rb'
|
||||
- 'ee/app/models/approval_state.rb'
|
||||
- 'ee/app/models/approval_wrapped_any_approver_rule.rb'
|
||||
|
|
@ -316,7 +312,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/app/models/ee/namespace.rb'
|
||||
- 'ee/app/models/ee/project.rb'
|
||||
- 'ee/app/models/ee/snippet.rb'
|
||||
- 'ee/app/models/ee/user.rb'
|
||||
- 'ee/app/models/ee/work_item.rb'
|
||||
- 'ee/app/models/gitlab/seat_link_data.rb'
|
||||
- 'ee/app/models/gitlab_subscription.rb'
|
||||
|
|
@ -328,6 +323,7 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/app/models/vulnerabilities/finding.rb'
|
||||
- 'ee/app/presenters/approval_rule_presenter.rb'
|
||||
- 'ee/app/presenters/ci/minutes/usage_presenter.rb'
|
||||
- 'ee/app/presenters/merge_request_approver_presenter.rb'
|
||||
- 'ee/app/serializers/dashboard_operations_project_entity.rb'
|
||||
- 'ee/app/serializers/ee/member_user_entity.rb'
|
||||
- 'ee/app/services/app_sec/dast/pipelines/find_latest_service.rb'
|
||||
|
|
@ -337,7 +333,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/app/services/ci/compare_license_scanning_reports_collapsed_service.rb'
|
||||
- 'ee/app/services/ci/minutes/update_project_and_namespace_usage_service.rb'
|
||||
- 'ee/app/services/ci/subscribe_bridge_service.rb'
|
||||
- 'ee/app/services/ci/sync_reports_to_approval_rules_service.rb'
|
||||
- 'ee/app/services/deployments/approval_service.rb'
|
||||
- 'ee/app/services/ee/allowed_email_domains/update_service.rb'
|
||||
- 'ee/app/services/ee/auto_merge_service.rb'
|
||||
|
|
@ -388,13 +383,11 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/app/services/status_page/publish_service.rb'
|
||||
- 'ee/app/services/status_page/trigger_publish_service.rb'
|
||||
- 'ee/app/services/timebox_report_service.rb'
|
||||
- 'ee/app/services/vulnerabilities/create_service.rb'
|
||||
- 'ee/app/services/vulnerability_feedback/create_service.rb'
|
||||
- 'ee/app/services/vulnerability_feedback/destroy_service.rb'
|
||||
- 'ee/app/workers/auth/saml_group_sync_worker.rb'
|
||||
- 'ee/app/workers/geo/repository_cleanup_worker.rb'
|
||||
- 'ee/app/workers/geo/scheduler/scheduler_worker.rb'
|
||||
- 'ee/app/workers/group_saml_group_sync_worker.rb'
|
||||
- 'ee/app/workers/status_page/publish_worker.rb'
|
||||
- 'ee/lib/api/analytics/project_deployment_frequency.rb'
|
||||
- 'ee/lib/api/epic_links.rb'
|
||||
|
|
@ -416,7 +409,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/lib/ee/gitlab/checks/diff_check.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/matching/runner_matcher.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/external.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/activity.rb'
|
||||
- 'ee/lib/ee/gitlab/ci/pipeline/quota/size.rb'
|
||||
- 'ee/lib/ee/gitlab/etag_caching/router/rails.rb'
|
||||
- 'ee/lib/ee/gitlab/git_access.rb'
|
||||
|
|
@ -426,7 +418,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'ee/lib/ee/gitlab/security/scan_configuration.rb'
|
||||
- 'ee/lib/ee/gitlab/web_hooks/rate_limiter.rb'
|
||||
- 'ee/lib/ee/sidebars/groups/menus/issues_menu.rb'
|
||||
- 'ee/lib/ee/sidebars/groups/menus/settings_menu.rb'
|
||||
- 'ee/lib/elastic/multi_version_util.rb'
|
||||
- 'ee/lib/gitlab/auth/group_saml/auth_hash.rb'
|
||||
- 'ee/lib/gitlab/auth/group_saml/membership_updater.rb'
|
||||
|
|
@ -480,7 +471,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'lib/atlassian/jira_connect/jwt/symmetric.rb'
|
||||
- 'lib/banzai/filter/base_sanitization_filter.rb'
|
||||
- 'lib/banzai/filter/custom_emoji_filter.rb'
|
||||
- 'lib/banzai/filter/inline_metrics_redactor_filter.rb'
|
||||
- 'lib/banzai/filter/issuable_reference_expansion_filter.rb'
|
||||
- 'lib/banzai/filter/references/reference_cache.rb'
|
||||
- 'lib/banzai/filter/repository_link_filter.rb'
|
||||
|
|
@ -570,7 +560,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'lib/gitlab/config/entry/composable_array.rb'
|
||||
- 'lib/gitlab/config/loader/yaml.rb'
|
||||
- 'lib/gitlab/conflict/file.rb'
|
||||
- 'lib/gitlab/database/background_migration/health_status/indicators/write_ahead_log.rb'
|
||||
- 'lib/gitlab/database/bulk_update.rb'
|
||||
- 'lib/gitlab/database/load_balancing/srv_resolver.rb'
|
||||
- 'lib/gitlab/database/metrics.rb'
|
||||
|
|
@ -613,7 +602,6 @@ Gitlab/StrongMemoizeAttr:
|
|||
- 'lib/gitlab/import_export/importer.rb'
|
||||
- 'lib/gitlab/import_export/lfs_restorer.rb'
|
||||
- 'lib/gitlab/import_export/project/sample/date_calculator.rb'
|
||||
- 'lib/gitlab/import_export/project/tree_restorer.rb'
|
||||
- 'lib/gitlab/inactive_projects_deletion_warning_tracker.rb'
|
||||
- 'lib/gitlab/instrumentation/redis_base.rb'
|
||||
- 'lib/gitlab/instrumentation/redis_payload.rb'
|
||||
|
|
|
|||
|
|
@ -173,18 +173,11 @@ export default {
|
|||
item.text.toLowerCase().includes(lowerCasedSearchTerm),
|
||||
);
|
||||
},
|
||||
isKasUserAccessAvailable() {
|
||||
return this.glFeatures?.kasUserAccessProject;
|
||||
},
|
||||
isKasKubernetesNamespaceAvailable() {
|
||||
return this.glFeatures?.kubernetesNamespaceForEnvironment;
|
||||
},
|
||||
showNamespaceSelector() {
|
||||
return Boolean(
|
||||
this.isKasUserAccessAvailable &&
|
||||
this.isKasKubernetesNamespaceAvailable &&
|
||||
this.selectedAgentId,
|
||||
);
|
||||
return Boolean(this.isKasKubernetesNamespaceAvailable && this.selectedAgentId);
|
||||
},
|
||||
namespaceDropdownToggleText() {
|
||||
return this.selectedNamespace || this.$options.i18n.namespaceHelpText;
|
||||
|
|
|
|||
|
|
@ -165,15 +165,9 @@ export default {
|
|||
rolloutStatus() {
|
||||
return this.environment?.rolloutStatus;
|
||||
},
|
||||
isKubernetesOverviewAvailable() {
|
||||
return this.glFeatures?.kasUserAccessProject;
|
||||
},
|
||||
isKubernetesNamespaceAvailable() {
|
||||
return this.glFeatures?.kubernetesNamespaceForEnvironment;
|
||||
},
|
||||
showKubernetesOverview() {
|
||||
return Boolean(this.isKubernetesOverviewAvailable && this.clusterAgent);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggleEnvironmentCollapse() {
|
||||
|
|
@ -184,7 +178,7 @@ export default {
|
|||
}
|
||||
},
|
||||
getClusterAgent() {
|
||||
if (!this.isKubernetesOverviewAvailable || this.clusterAgent) return;
|
||||
if (this.clusterAgent) return;
|
||||
|
||||
this.$apollo.addSmartQuery('environmentClusterAgent', {
|
||||
variables() {
|
||||
|
|
@ -377,7 +371,7 @@ export default {
|
|||
</template>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
<div v-if="showKubernetesOverview" :class="$options.kubernetesOverviewClasses">
|
||||
<div v-if="clusterAgent" :class="$options.kubernetesOverviewClasses">
|
||||
<kubernetes-overview :cluster-agent="clusterAgent" :namespace="kubernetesNamespace" />
|
||||
</div>
|
||||
<div v-if="rolloutStatus" :class="$options.deployBoardClasses">
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { GlButton, GlEmptyState, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { createAlert, VARIANT_INFO } from '~/alert';
|
||||
import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { historyReplaceState } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import { SHOW_DELETE_SUCCESS_ALERT } from '~/packages_and_registries/shared/constants';
|
||||
|
|
@ -38,11 +39,13 @@ export default {
|
|||
sort: '',
|
||||
filters: {},
|
||||
mutationLoading: false,
|
||||
pageParams: {},
|
||||
};
|
||||
},
|
||||
apollo: {
|
||||
packagesResource: {
|
||||
query: getPackagesQuery,
|
||||
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
|
||||
variables() {
|
||||
return this.queryVariables;
|
||||
},
|
||||
|
|
@ -72,6 +75,7 @@ export default {
|
|||
packageName: this.filters?.packageName,
|
||||
packageType: this.filters?.packageType,
|
||||
first: GRAPHQL_PAGE_SIZE,
|
||||
...this.pageParams,
|
||||
};
|
||||
},
|
||||
graphqlResource() {
|
||||
|
|
@ -120,37 +124,22 @@ export default {
|
|||
}
|
||||
},
|
||||
handleSearchUpdate({ sort, filters }) {
|
||||
this.pageParams = {};
|
||||
this.sort = sort;
|
||||
this.filters = { ...filters };
|
||||
},
|
||||
updateQuery(_, { fetchMoreResult }) {
|
||||
return fetchMoreResult;
|
||||
},
|
||||
fetchNextPage() {
|
||||
const variables = {
|
||||
...this.queryVariables,
|
||||
this.pageParams = {
|
||||
first: GRAPHQL_PAGE_SIZE,
|
||||
last: null,
|
||||
after: this.pageInfo?.endCursor,
|
||||
};
|
||||
|
||||
this.$apollo.queries.packagesResource.fetchMore({
|
||||
variables,
|
||||
updateQuery: this.updateQuery,
|
||||
});
|
||||
},
|
||||
fetchPreviousPage() {
|
||||
const variables = {
|
||||
...this.queryVariables,
|
||||
this.pageParams = {
|
||||
first: null,
|
||||
last: GRAPHQL_PAGE_SIZE,
|
||||
before: this.pageInfo?.startCursor,
|
||||
};
|
||||
|
||||
this.$apollo.queries.packagesResource.fetchMore({
|
||||
variables,
|
||||
updateQuery: this.updateQuery,
|
||||
});
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Vue from 'vue';
|
|||
import Vuex from 'vuex';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import VueRouter from 'vue-router';
|
||||
import { provideWebIdeLink } from 'ee_else_ce/pages/projects/shared/web_ide_link/provide_web_ide_link';
|
||||
import TableOfContents from '~/blob/components/table_contents.vue';
|
||||
import PipelineTourSuccessModal from '~/blob/pipeline_tour_success_modal.vue';
|
||||
import { BlobViewer, initAuxiliaryViewer } from '~/blob/viewer/index';
|
||||
|
|
@ -70,6 +71,7 @@ if (viewBlobEl) {
|
|||
resourceId,
|
||||
userId,
|
||||
explainCodeAvailable,
|
||||
...dataset
|
||||
} = viewBlobEl.dataset;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
|
|
@ -85,6 +87,7 @@ if (viewBlobEl) {
|
|||
resourceId,
|
||||
userId,
|
||||
explainCodeAvailable: parseBoolean(explainCodeAvailable),
|
||||
...provideWebIdeLink(dataset),
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(BlobContentViewer, {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { provideWebIdeLink } from 'ee_else_ce/pages/projects/shared/web_ide_link/provide_web_ide_link';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import { joinPaths, webIDEUrl } from '~/lib/utils/url_utility';
|
||||
import WebIdeButton from '~/vue_shared/components/web_ide_link.vue';
|
||||
import WebIdeButton from 'ee_else_ce/vue_shared/components/web_ide_link.vue';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
|
@ -26,6 +27,7 @@ export default ({ el, router }) => {
|
|||
apolloProvider,
|
||||
provide: {
|
||||
projectPath,
|
||||
...provideWebIdeLink(options),
|
||||
},
|
||||
render(h) {
|
||||
return h(WebIdeButton, {
|
||||
|
|
@ -37,6 +39,7 @@ export default ({ el, router }) => {
|
|||
: webIDEUrl(
|
||||
joinPaths('/', projectPath, 'edit', ref, '-', this.$route?.params.path || '', '/'),
|
||||
),
|
||||
projectPath,
|
||||
...options,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Inspects an object and extracts properties
|
||||
* that are relevant to the web_ide_link.vue
|
||||
* component.
|
||||
*
|
||||
* @returns An object with properties that are
|
||||
* relevant to the web_ide_link.vue component. See EE version.
|
||||
*/
|
||||
export const provideWebIdeLink = () => ({});
|
||||
|
|
@ -7,10 +7,11 @@ import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constant
|
|||
import { createAlert } from '~/alert';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
|
||||
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
|
||||
import { __ } from '~/locale';
|
||||
import { redirectTo, getLocationHash } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
import WebIdeLink from 'ee_else_ce/vue_shared/components/web_ide_link.vue';
|
||||
import CodeIntelligence from '~/code_navigation/components/app.vue';
|
||||
import LineHighlighter from '~/blob/line_highlighter';
|
||||
import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql';
|
||||
|
|
@ -226,6 +227,9 @@ export default {
|
|||
isUsingLfs() {
|
||||
return this.blobInfo.storedExternally && this.blobInfo.externalStorage === LFS_STORAGE;
|
||||
},
|
||||
projectIdAsNumber() {
|
||||
return getIdFromGraphQLId(this.project?.id);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// Watch the URL 'plain' query value to know if the viewer needs changing.
|
||||
|
|
@ -356,6 +360,8 @@ export default {
|
|||
:gitpod-url="blobInfo.gitpodBlobUrl"
|
||||
:show-gitpod-button="gitpodEnabled"
|
||||
:gitpod-enabled="currentUser && currentUser.gitpodEnabled"
|
||||
:project-path="projectPath"
|
||||
:project-id="projectIdAsNumber"
|
||||
:user-preferences-gitpod-path="currentUser && currentUser.preferencesGitpodPath"
|
||||
:user-profile-enable-gitpod-path="currentUser && currentUser.profileEnableGitpodPath"
|
||||
is-blob
|
||||
|
|
|
|||
|
|
@ -45,8 +45,12 @@ export default {
|
|||
:category="category"
|
||||
:toggle-text="toggleText"
|
||||
data-qa-selector="action_dropdown"
|
||||
fluid-width
|
||||
block
|
||||
@shown="$emit('shown')"
|
||||
@hidden="$emit('hidden')"
|
||||
>
|
||||
<gl-disclosure-dropdown-group>
|
||||
<gl-disclosure-dropdown-group class="edit-dropdown-group-width">
|
||||
<gl-disclosure-dropdown-item
|
||||
v-for="action in actions"
|
||||
:key="action.key"
|
||||
|
|
@ -65,5 +69,6 @@ export default {
|
|||
</template>
|
||||
</gl-disclosure-dropdown-item>
|
||||
</gl-disclosure-dropdown-group>
|
||||
<slot></slot>
|
||||
</gl-disclosure-dropdown>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export const i18n = {
|
|||
};
|
||||
|
||||
export default {
|
||||
name: 'CEWebIdeLink',
|
||||
components: {
|
||||
ActionsButton,
|
||||
GlModal,
|
||||
|
|
@ -319,7 +320,11 @@ export default {
|
|||
:toggle-text="$options.i18n.toggleText"
|
||||
:variant="isBlob ? 'confirm' : 'default'"
|
||||
:category="isBlob ? 'primary' : 'secondary'"
|
||||
/>
|
||||
@hidden="$emit('hidden')"
|
||||
@shown="$emit('shown')"
|
||||
>
|
||||
<slot></slot>
|
||||
</actions-button>
|
||||
<gl-modal
|
||||
v-if="computedShowGitpodButton && !gitpodEnabled"
|
||||
v-model="showEnableGitpodModal"
|
||||
|
|
|
|||
|
|
@ -205,17 +205,6 @@
|
|||
margin-top: $gl-padding;
|
||||
}
|
||||
|
||||
|
||||
.web-ide-promo-popover {
|
||||
box-shadow: 0 0 18px -1.9px rgba(119, 89, 194, 0.16),
|
||||
0 0 12.9px -1.7px rgba(119, 89, 194, 0.16), 0 0 9.2px -1.4px rgba(119, 89, 194, 0.16),
|
||||
0 0 6.4px -1.1px rgba(119, 89, 194, 0.16), 0 0 4.5px -0.8px rgba(119, 89, 194, 0.16),
|
||||
0 0 3px -0.6px rgba(119, 89, 194, 0.16), 0 0 1.8px -0.3px rgba(119, 89, 194, 0.16),
|
||||
0 0 0.6px rgba(119, 89, 194, 0.16);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.web-ide-promo-popover-illustration {
|
||||
width: calc(100% + 24px);
|
||||
margin: -28px -12px 0;
|
||||
.edit-dropdown-group-width {
|
||||
width: 320px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
before_action only: [:index, :edit, :new] do
|
||||
push_frontend_feature_flag(:kas_user_access_project, @project)
|
||||
push_frontend_feature_flag(:kubernetes_namespace_for_environment)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -172,18 +172,6 @@ module ProjectsHelper
|
|||
project.fork_source if project.fork_source && can?(current_user, :read_project, project.fork_source)
|
||||
end
|
||||
|
||||
def project_search_tabs?(tab)
|
||||
return false unless @project.present?
|
||||
|
||||
abilities = Array(search_tab_ability_map[tab])
|
||||
|
||||
if @project.respond_to?(:each) # support multi-project select
|
||||
@project.any? { |project| abilities.any? { |ability| can?(current_user, ability, project) } }
|
||||
else
|
||||
abilities.any? { |ability| can?(current_user, ability, @project) }
|
||||
end
|
||||
end
|
||||
|
||||
def can_change_visibility_level?(project, current_user)
|
||||
can?(current_user, :change_visibility_level, project)
|
||||
end
|
||||
|
|
@ -614,41 +602,6 @@ module ProjectsHelper
|
|||
s_(str).html_safe % { provider: provider, link_start: link_start, link_end: '</a>'.html_safe }
|
||||
end
|
||||
|
||||
def tab_ability_map
|
||||
{
|
||||
cycle_analytics: :read_cycle_analytics,
|
||||
environments: :read_environment,
|
||||
metrics_dashboards: :metrics_dashboard,
|
||||
milestones: :read_milestone,
|
||||
snippets: :read_snippet,
|
||||
settings: :admin_project,
|
||||
builds: :read_build,
|
||||
clusters: :read_cluster,
|
||||
serverless: :read_cluster,
|
||||
terraform: :read_terraform_state,
|
||||
error_tracking: :read_sentry_issue,
|
||||
alert_management: :read_alert_management_alert,
|
||||
incidents: :read_issue,
|
||||
labels: :read_label,
|
||||
issues: :read_issue,
|
||||
project_members: :read_project_member,
|
||||
wiki: :read_wiki,
|
||||
feature_flags: :read_feature_flag,
|
||||
analytics: :read_analytics
|
||||
}
|
||||
end
|
||||
|
||||
def search_tab_ability_map
|
||||
@search_tab_ability_map ||= tab_ability_map.merge(
|
||||
blobs: :read_code,
|
||||
commits: :read_code,
|
||||
merge_requests: :read_merge_request,
|
||||
notes: [:read_merge_request, :read_code, :read_issue, :read_snippet],
|
||||
users: :read_project_member,
|
||||
wiki_blobs: :read_wiki
|
||||
)
|
||||
end
|
||||
|
||||
def project_lfs_status(project)
|
||||
if project.lfs_enabled?
|
||||
content_tag(:span, class: 'lfs-enabled') do
|
||||
|
|
|
|||
|
|
@ -464,65 +464,15 @@ module SearchHelper
|
|||
result
|
||||
end
|
||||
|
||||
def show_code_search_tab?
|
||||
return true if project_search_tabs?(:blobs)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)
|
||||
end
|
||||
|
||||
def show_wiki_search_tab?
|
||||
return true if project_search_tabs?(:wiki_blobs)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_wiki_tab)
|
||||
end
|
||||
|
||||
def show_commits_search_tab?
|
||||
return true if project_search_tabs?(:commits)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)
|
||||
end
|
||||
|
||||
def show_issues_search_tab?
|
||||
return true if project_search_tabs?(:issues)
|
||||
|
||||
@project.nil? && feature_flag_tab_enabled?(:global_search_issues_tab)
|
||||
end
|
||||
|
||||
def show_merge_requests_search_tab?
|
||||
return true if project_search_tabs?(:merge_requests)
|
||||
|
||||
@project.nil? && feature_flag_tab_enabled?(:global_search_merge_requests_tab)
|
||||
end
|
||||
|
||||
def show_comments_search_tab?
|
||||
return true if project_search_tabs?(:notes)
|
||||
|
||||
@project.nil? && search_service.show_elasticsearch_tabs?
|
||||
end
|
||||
|
||||
def show_snippets_search_tab?
|
||||
search_service.show_snippets? && @project.nil? && feature_flag_tab_enabled?(:global_search_snippet_titles_tab)
|
||||
end
|
||||
|
||||
# search page scope navigation
|
||||
def search_navigation
|
||||
def nav_options
|
||||
{
|
||||
projects: { sort: 1, label: _("Projects"), data: { qa_selector: 'projects_tab' }, condition: @project.nil? },
|
||||
blobs: { sort: 2, label: _("Code"), data: { qa_selector: 'code_tab' }, condition: show_code_search_tab? },
|
||||
# sort: 3 is reserved for EE items
|
||||
issues: { sort: 4, label: _("Issues"), condition: show_issues_search_tab? },
|
||||
merge_requests: { sort: 5, label: _("Merge requests"), condition: show_merge_requests_search_tab? },
|
||||
wiki_blobs: { sort: 6, label: _("Wiki"), condition: show_wiki_search_tab? },
|
||||
commits: { sort: 7, label: _("Commits"), condition: show_commits_search_tab? },
|
||||
notes: { sort: 8, label: _("Comments"), condition: show_comments_search_tab? },
|
||||
milestones: { sort: 9, label: _("Milestones"), condition: project_search_tabs?(:milestones) || @project.nil? },
|
||||
users: { sort: 10, label: _("Users"), condition: show_user_search_tab? },
|
||||
snippet_titles: { sort: 11, label: _("Snippets"), search: { snippets: true, group_id: nil, project_id: nil }, condition: show_snippets_search_tab? }
|
||||
show_snippets: search_service.show_snippets?
|
||||
}
|
||||
end
|
||||
|
||||
def search_navigation_json
|
||||
sorted_navigation = search_navigation.sort_by { |_, h| h[:sort] }
|
||||
search_navigation = Search::Navigation.new(user: current_user, project: @project, group: @group, options: nav_options)
|
||||
sorted_navigation = search_navigation.tabs.sort_by { |_, h| h[:sort] }
|
||||
|
||||
sorted_navigation.each_with_object({}) do |(key, value), hash|
|
||||
hash[key] = search_filter_link_json(key, value[:label], value[:data], value[:search]) if value[:condition]
|
||||
|
|
@ -604,14 +554,6 @@ module SearchHelper
|
|||
simple_search_highlight_and_truncate(issuable.description, search_term, highlighter: '<span class="gl-text-gray-900 gl-font-weight-bold">\1</span>')
|
||||
end
|
||||
|
||||
def show_user_search_tab?
|
||||
return project_search_tabs?(:users) if @project
|
||||
return false unless can?(current_user, :read_users_list)
|
||||
return true if @group
|
||||
|
||||
Feature.enabled?(:global_search_users_tab, current_user, type: :ops)
|
||||
end
|
||||
|
||||
def issuable_state_to_badge_class(issuable)
|
||||
# Closed is considered "danger" for MR so we need to handle separately
|
||||
if issuable.is_a?(::MergeRequest)
|
||||
|
|
@ -640,10 +582,6 @@ module SearchHelper
|
|||
end
|
||||
end
|
||||
|
||||
def feature_flag_tab_enabled?(flag)
|
||||
@group.present? || Feature.enabled?(flag, current_user, type: :ops)
|
||||
end
|
||||
|
||||
def sanitized_search_params
|
||||
sanitized_params = params.dup
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ module Clusters
|
|||
|
||||
def ci_access_authorized_for?(user)
|
||||
return false unless user
|
||||
return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project)
|
||||
|
||||
all_ci_access_authorized_projects_for(user).exists? ||
|
||||
all_ci_access_authorized_namespaces_for(user).exists?
|
||||
|
|
@ -74,7 +73,6 @@ module Clusters
|
|||
|
||||
def user_access_authorized_for?(user)
|
||||
return false unless user
|
||||
return false unless ::Feature.enabled?(:expose_authorized_cluster_agents, project)
|
||||
|
||||
Clusters::Agents::Authorizations::UserAccess::Finder
|
||||
.new(user, agent: self, preload: false, limit: 1).execute.any?
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ module Search
|
|||
class ProjectService
|
||||
include Search::Filter
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include ProjectsHelper
|
||||
|
||||
ALLOWED_SCOPES = %w(blobs issues merge_requests wiki_blobs commits notes milestones users).freeze
|
||||
|
||||
|
|
@ -18,13 +17,13 @@ module Search
|
|||
|
||||
def execute
|
||||
Gitlab::ProjectSearchResults.new(current_user,
|
||||
params[:search],
|
||||
project: project,
|
||||
repository_ref: params[:repository_ref],
|
||||
order_by: params[:order_by],
|
||||
sort: params[:sort],
|
||||
filters: filters
|
||||
)
|
||||
params[:search],
|
||||
project: project,
|
||||
repository_ref: params[:repository_ref],
|
||||
order_by: params[:order_by],
|
||||
sort: params[:sort],
|
||||
filters: filters
|
||||
)
|
||||
end
|
||||
|
||||
def allowed_scopes
|
||||
|
|
@ -33,10 +32,12 @@ module Search
|
|||
|
||||
def scope
|
||||
strong_memoize(:scope) do
|
||||
next params[:scope] if allowed_scopes.include?(params[:scope]) && project_search_tabs?(params[:scope].to_sym)
|
||||
search_navigation = Search::Navigation.new(user: current_user, project: project)
|
||||
scope = params[:scope]
|
||||
next scope if allowed_scopes.include?(scope) && search_navigation.tab_enabled_for_project?(scope.to_sym)
|
||||
|
||||
allowed_scopes.find do |scope|
|
||||
project_search_tabs?(scope.to_sym)
|
||||
allowed_scopes.find do |s|
|
||||
search_navigation.tab_enabled_for_project?(s.to_sym)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,16 +102,6 @@ class SearchService
|
|||
end
|
||||
end
|
||||
|
||||
def show_elasticsearch_tabs?
|
||||
# overridden in EE
|
||||
false
|
||||
end
|
||||
|
||||
def show_epics?
|
||||
# overridden in EE
|
||||
false
|
||||
end
|
||||
|
||||
def global_search_enabled_for_scope?
|
||||
return false if show_snippets? && Feature.disabled?(:global_search_snippet_titles_tab, current_user, type: :ops)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
- gitlab_com = Gitlab.com?
|
||||
|
||||
- return unless Feature.enabled?(:slack_app_self_managed) || gitlab_com
|
||||
|
||||
- expanded = integration_expanded?('slack_app_')
|
||||
|
||||
%section.settings.as-slack.no-animate#js-slack-settings{ class: ('expanded' if expanded) }
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: expose_authorized_cluster_agents
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117128
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/407841
|
||||
milestone: '16.0'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: true
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: kas_user_access
|
||||
introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104504'
|
||||
rollout_issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391201'
|
||||
milestone: '15.10'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: true
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: kas_user_access_project
|
||||
introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104504'
|
||||
rollout_issue_url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/391211'
|
||||
milestone: '15.10'
|
||||
type: development
|
||||
group: group::environments
|
||||
default_enabled: true
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: slack_app_self_managed
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124823
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/416448
|
||||
milestone: '16.2'
|
||||
type: development
|
||||
group: group::import and integrate
|
||||
default_enabled: false
|
||||
|
|
@ -807,6 +807,9 @@ Gitlab.ee do
|
|||
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker'] ||= {}
|
||||
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker']['cron'] ||= '15 3 * * *'
|
||||
Settings.cron_jobs['vulnerability_historical_statistics_deletion_worker']['job_class'] = 'Vulnerabilities::HistoricalStatistics::DeletionWorker'
|
||||
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker'] ||= {}
|
||||
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker']['job_class'] = 'Vulnerabilities::OrphanedRemediationsCleanupWorker'
|
||||
Settings.cron_jobs['vulnerability_orphaned_remediations_cleanup_worker']['cron'] ||= '15 3 3 * *'
|
||||
Settings.cron_jobs['security_create_orchestration_policy_worker'] ||= {}
|
||||
Settings.cron_jobs['security_create_orchestration_policy_worker']['cron'] ||= '*/10 * * * *'
|
||||
Settings.cron_jobs['security_create_orchestration_policy_worker']['job_class'] = 'Security::CreateOrchestrationPolicyWorker'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ChangePmAdvisoriesUrlsConstraint < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
CONSTRAINT_NAME = "chk_rails_e73af9de76"
|
||||
|
||||
def up
|
||||
remove_check_constraint :pm_advisories, CONSTRAINT_NAME
|
||||
add_check_constraint :pm_advisories, "CARDINALITY(urls) <= 20", CONSTRAINT_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_check_constraint :pm_advisories, CONSTRAINT_NAME
|
||||
add_check_constraint :pm_advisories, "CARDINALITY(urls) <= 10", CONSTRAINT_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
8fcab29ea25760d876c2b985cf9e4f2a62e25450322a92fe769533b0882b5402
|
||||
|
|
@ -20285,7 +20285,7 @@ CREATE TABLE pm_advisories (
|
|||
CONSTRAINT check_bed97fa77a CHECK ((char_length(cvss_v3) <= 128)),
|
||||
CONSTRAINT check_e4bfd3ffbf CHECK ((char_length(title) <= 256)),
|
||||
CONSTRAINT check_fee880f7aa CHECK ((char_length(description) <= 8192)),
|
||||
CONSTRAINT chk_rails_e73af9de76 CHECK ((cardinality(urls) <= 10))
|
||||
CONSTRAINT chk_rails_e73af9de76 CHECK ((cardinality(urls) <= 20))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE pm_advisories_id_seq
|
||||
|
|
|
|||
|
|
@ -142,10 +142,9 @@ For details, see [how to use the GitLab-KAS chart](https://docs.gitlab.com/chart
|
|||
|
||||
## Kubernetes API proxy cookie
|
||||
|
||||
> Introduced in GitLab 15.10 [with feature flags](../feature_flags.md) named `kas_user_access` and `kas_user_access_project`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flags](../feature_flags.md) named `kas_user_access` and `kas_user_access_project`.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104504) in GitLab 15.10 [with feature flags](../feature_flags.md) named `kas_user_access` and `kas_user_access_project`. Disabled by default.
|
||||
> - Feature flags `kas_user_access` and `kas_user_access_project` [enabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123479) in GitLab 16.1.
|
||||
> - Feature flags `kas_user_access` and `kas_user_access_project` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125835) in GitLab 16.2.
|
||||
|
||||
KAS proxies Kubernetes API requests to the GitLab agent with either:
|
||||
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ authentication mode (`patroni['tls_client_mode']`), must each have the same valu
|
|||
|
||||
1. Make sure you collect [`CONSUL_SERVER_NODES`](#consul-information), [`CONSUL_PASSWORD_HASH`](#consul-information), and [`PGBOUNCER_PASSWORD_HASH`](#pgbouncer-information) before executing the next step.
|
||||
|
||||
1. One each node, edit the `/etc/gitlab/gitlab.rb` configuration file and replace values noted in the `# START user configuration` section as below:
|
||||
1. On each node, edit the `/etc/gitlab/gitlab.rb` configuration file and replace values noted in the `# START user configuration` section as below:
|
||||
|
||||
```ruby
|
||||
# Disable all components except PgBouncer and Consul agent
|
||||
|
|
@ -864,7 +864,7 @@ patroni['remove_data_directory_on_rewind_failure'] = false
|
|||
patroni['remove_data_directory_on_diverged_timelines'] = false
|
||||
```
|
||||
|
||||
[The upstream documentation is always more up to date](https://patroni.readthedocs.io/en/latest/SETTINGS.html#postgresql), but the table below should provide a minimal overview of functionality.
|
||||
[The upstream documentation is always more up to date](https://patroni.readthedocs.io/en/latest/patroni_configuration.html), but the table below should provide a minimal overview of functionality.
|
||||
|
||||
|Setting|Overview|
|
||||
|-|-|
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@ type: reference
|
|||
|
||||
# Dashboard for Kubernetes (Beta) **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/390769) in GitLab 16.1, with [flags](../../administration/feature_flags.md) named `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents`. This feature is in [Beta](../../policy/experiment-beta-support.md#beta).
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/390769) in GitLab 16.1, with [flags](../../administration/feature_flags.md) named `environment_settings_to_graphql`, `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents`. This feature is in [Beta](../../policy/experiment-beta-support.md#beta).
|
||||
> - Feature flag `environment_settings_to_graphql` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124177) in GitLab 16.2.
|
||||
> - Feature flags `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125835) in GitLab 16.2.
|
||||
|
||||
Use the Dashboard for Kubernetes to understand the status of your clusters with an intuitive visual interface.
|
||||
The dashboard works with every connected Kubernetes cluster, whether you deployed them
|
||||
|
|
|
|||
|
|
@ -316,6 +316,13 @@ Example migration:
|
|||
Changing column defaults is difficult because of how Rails handles values
|
||||
that are equal to the default.
|
||||
|
||||
NOTE:
|
||||
Rails ignores sending the default values to PostgreSQL when writing records. It leaves this task to
|
||||
the database. When migrations change the default values of the columns, the running application is unaware
|
||||
of this change due to the schema cache. The application is then under the risk of accidentally writing
|
||||
wrong data to the database, especially when deploying the new version of the code
|
||||
long after we run database migrations.
|
||||
|
||||
If running code ever explicitly writes the old default value of a column, you must follow a multi-step
|
||||
process to prevent Rails replacing the old default with the new default in INSERT queries that explicitly
|
||||
specify the old default.
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ delivery from customer launch.
|
|||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an example of feature flags in action, see [Feature Flags configuration, instrumentation and use](https://www.youtube.com/watch?v=ViA6suScxkE).
|
||||
|
||||
You can also explore feature flags with a [click-through demo](https://go.gitlab.com/YKuzLt).
|
||||
|
||||
NOTE:
|
||||
To contribute to the development of the GitLab product, view
|
||||
[this feature flag content](../development/feature_flags/index.md) instead.
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# GitLab for Slack app administration **(FREE SELF)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358872) for self-managed instances in GitLab 16.2 [with a flag](../../../administration/feature_flags.md) named `slack_app_self_managed`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `slack_app_self_managed`. On GitLab.com, this feature is available.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358872) for self-managed instances in GitLab 16.2.
|
||||
|
||||
NOTE:
|
||||
This page contains information about administering the GitLab for Slack app for self-managed instances. For user documentation, see [GitLab for Slack app](../../../user/project/integrations/gitlab_slack_application.md).
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Grant users Kubernetes access (Beta) **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/390769) in GitLab 16.1, with [flags](../../../administration/feature_flags.md) named `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents`. This feature is in [Beta](../../../policy/experiment-beta-support.md#beta).
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/390769) in GitLab 16.1, with [flags](../../../administration/feature_flags.md) named `environment_settings_to_graphql`, `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents`. This feature is in [Beta](../../../policy/experiment-beta-support.md#beta).
|
||||
> - Feature flag `environment_settings_to_graphql` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124177) in GitLab 16.2.
|
||||
> - Feature flags `kas_user_access`, `kas_user_access_project`, and `expose_authorized_cluster_agents` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/125835) in GitLab 16.2.
|
||||
|
||||
As an administrator of Kubernetes clusters in an organization, you can grant Kubernetes access to members
|
||||
of a specific project or group.
|
||||
|
|
|
|||
|
|
@ -6,10 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# GitLab for Slack app **(FREE)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358872) for self-managed instances in GitLab 16.2 [with a flag](../../../administration/feature_flags.md) named `slack_app_self_managed`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `slack_app_self_managed`. On GitLab.com, this feature is available.
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/358872) for self-managed instances in GitLab 16.2.
|
||||
|
||||
NOTE:
|
||||
This page contains information about configuring the GitLab for Slack app on GitLab.com. For administrator documentation, see [GitLab for Slack app administration](../../admin_area/settings/slack_app.md).
|
||||
|
|
@ -20,9 +17,10 @@ you run in Slack is run by your linked GitLab user.
|
|||
|
||||
## Install the GitLab for Slack app
|
||||
|
||||
Prerequisite:
|
||||
Prerequisites:
|
||||
|
||||
- You must have the [appropriate permissions to add apps to your Slack workspace](https://slack.com/help/articles/202035138-Add-apps-to-your-Slack-workspace).
|
||||
- On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature](../../admin_area/settings/slack_app.md). On GitLab.com, this feature is available.
|
||||
|
||||
In GitLab 15.0 and later, the GitLab for Slack app uses
|
||||
[granular permissions](https://medium.com/slack-developer-blog/more-precision-less-restrictions-a3550006f9c3).
|
||||
|
|
@ -34,7 +32,7 @@ To install the GitLab for Slack app from project integration settings:
|
|||
|
||||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
|
||||
1. Select **Settings > Integrations**.
|
||||
1. Select **GitLab for Slack app**. On self-managed GitLab, an administrator must first [enable the integration](../../admin_area/settings/slack_app.md).
|
||||
1. Select **GitLab for Slack app**.
|
||||
1. Select **Install GitLab for Slack app**.
|
||||
1. On the Slack confirmation page, select **Allow**.
|
||||
|
||||
|
|
@ -109,7 +107,7 @@ By default, slash commands expect a project full path. To create a shorter proje
|
|||
|
||||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your project.
|
||||
1. Select **Settings > Integrations**.
|
||||
1. Select **GitLab for Slack app**. On self-managed GitLab, an administrator must first [enable the integration](../../admin_area/settings/slack_app.md).
|
||||
1. Select **GitLab for Slack app**.
|
||||
1. The current **Project Alias**, if any, is displayed. To edit this value,
|
||||
select **Edit**.
|
||||
1. Enter your desired alias, and select **Save changes**.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ To open the command palette:
|
|||
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**).
|
||||
1. Type one of the special characters:
|
||||
|
||||
- <kbd>></kbd> - Use to create a new object or to find a menu item.
|
||||
- <kbd>@</kbd> - Search for user.
|
||||
- <kbd>:</kbd> - Search for project.
|
||||
- <kbd>></kbd> - Create a new object or find a menu item.
|
||||
- <kbd>@</kbd> - Search for a user.
|
||||
- <kbd>:</kbd> - Search for a project.
|
||||
- <kbd>/</kbd> - Search for project files in the default repository branch.
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ module API
|
|||
|
||||
# Load agent
|
||||
agent = ::Clusters::Agent.find(params[:agent_id])
|
||||
unauthorized!('Feature disabled for agent') unless ::Gitlab::Kas::UserAccess.enabled_for?(agent)
|
||||
unauthorized!('Feature disabled for agent') unless ::Gitlab::Kas::UserAccess.enabled?
|
||||
|
||||
service_response = ::Clusters::Agents::AuthorizeProxyUserService.new(user, agent).execute
|
||||
render_api_error!(service_response[:message], service_response[:reason]) unless service_response.success?
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ module API
|
|||
end
|
||||
|
||||
def search(additional_params = {})
|
||||
return Kaminari.paginate_array([]) if @project.present? && !project_scope_allowed?
|
||||
|
||||
search_service = search_service(additional_params)
|
||||
if search_service.global_search? && !search_service.global_search_enabled_for_scope?
|
||||
forbidden!('Global Search is disabled for this scope')
|
||||
|
|
@ -95,6 +97,10 @@ module API
|
|||
)
|
||||
end
|
||||
|
||||
def project_scope_allowed?
|
||||
::Search::Navigation.new(user: current_user, project: @project).tab_enabled_for_project?(params[:scope].to_sym)
|
||||
end
|
||||
|
||||
def snippets?
|
||||
%w(snippet_titles).include?(params[:scope]).to_s
|
||||
end
|
||||
|
|
@ -108,9 +114,7 @@ module API
|
|||
end
|
||||
|
||||
def verify_search_scope!(resource:)
|
||||
# In EE we have additional validation requirements for searches.
|
||||
# Defining this method here as a noop allows us to easily extend it in
|
||||
# EE, without having to modify this file directly.
|
||||
# no-op
|
||||
end
|
||||
|
||||
def search_type(additional_params = {})
|
||||
|
|
|
|||
|
|
@ -9,11 +9,7 @@ module Gitlab
|
|||
class UserAccess
|
||||
class << self
|
||||
def enabled?
|
||||
::Gitlab::Kas.enabled? && ::Feature.enabled?(:kas_user_access)
|
||||
end
|
||||
|
||||
def enabled_for?(agent)
|
||||
enabled? && ::Feature.enabled?(:kas_user_access_project, agent.project)
|
||||
::Gitlab::Kas.enabled?
|
||||
end
|
||||
|
||||
def encrypt_public_session_id(data)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Search
|
||||
class Navigation
|
||||
include Gitlab::Allowable
|
||||
|
||||
def initialize(user:, project: nil, group: nil, options: {})
|
||||
@user = user
|
||||
@project = project
|
||||
@group = group
|
||||
@options = options
|
||||
end
|
||||
|
||||
def tab_enabled_for_project?(tab)
|
||||
return false unless project.present?
|
||||
|
||||
abilities = Array(search_tab_ability_map[tab])
|
||||
Array.wrap(project).any? { |p| abilities.any? { |ability| can?(user, ability, p) } }
|
||||
end
|
||||
|
||||
def tabs
|
||||
{
|
||||
projects: {
|
||||
sort: 1,
|
||||
label: _("Projects"),
|
||||
data: { qa_selector: 'projects_tab' },
|
||||
condition: project.nil?
|
||||
},
|
||||
blobs: {
|
||||
sort: 2,
|
||||
label: _("Code"),
|
||||
data: { qa_selector: 'code_tab' },
|
||||
condition: show_code_search_tab?
|
||||
},
|
||||
# sort: 3 is reserved for EE items
|
||||
issues: {
|
||||
sort: 4,
|
||||
label: _("Issues"),
|
||||
condition: show_issues_search_tab?
|
||||
},
|
||||
merge_requests: {
|
||||
sort: 5,
|
||||
label: _("Merge requests"),
|
||||
condition: show_merge_requests_search_tab?
|
||||
},
|
||||
wiki_blobs: {
|
||||
sort: 6,
|
||||
label: _("Wiki"),
|
||||
condition: show_wiki_search_tab?
|
||||
},
|
||||
commits: {
|
||||
sort: 7,
|
||||
label: _("Commits"),
|
||||
condition: show_commits_search_tab?
|
||||
},
|
||||
notes: {
|
||||
sort: 8,
|
||||
label: _("Comments"),
|
||||
condition: show_comments_search_tab?
|
||||
},
|
||||
milestones: {
|
||||
sort: 9, label: _("Milestones"),
|
||||
condition: show_milestones_search_tab?
|
||||
},
|
||||
users: {
|
||||
sort: 10,
|
||||
label: _("Users"),
|
||||
condition: show_user_search_tab?
|
||||
},
|
||||
snippet_titles: {
|
||||
sort: 11,
|
||||
label: _("Snippets"),
|
||||
search: { snippets: true, group_id: nil, project_id: nil },
|
||||
condition: show_snippets_search_tab?
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :user, :project, :group, :options
|
||||
|
||||
def show_elasticsearch_tabs?
|
||||
!!options[:show_elasticsearch_tabs]
|
||||
end
|
||||
|
||||
def search_tab_ability_map
|
||||
{
|
||||
milestones: :read_milestone,
|
||||
snippets: :read_snippet,
|
||||
issues: :read_issue,
|
||||
blobs: :read_code,
|
||||
commits: :read_code,
|
||||
merge_requests: :read_merge_request,
|
||||
notes: [:read_merge_request, :read_code, :read_issue, :read_snippet],
|
||||
users: :read_project_member,
|
||||
wiki_blobs: :read_wiki
|
||||
}
|
||||
end
|
||||
|
||||
def show_user_search_tab?
|
||||
return true if tab_enabled_for_project?(:users)
|
||||
return false unless can?(user, :read_users_list)
|
||||
|
||||
project.nil? && feature_flag_tab_enabled?(:global_search_users_tab)
|
||||
end
|
||||
|
||||
def show_code_search_tab?
|
||||
return true if tab_enabled_for_project?(:blobs)
|
||||
|
||||
project.nil? && show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_code_tab)
|
||||
end
|
||||
|
||||
def show_wiki_search_tab?
|
||||
return true if tab_enabled_for_project?(:wiki_blobs)
|
||||
|
||||
project.nil? && show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_wiki_tab)
|
||||
end
|
||||
|
||||
def show_commits_search_tab?
|
||||
return true if tab_enabled_for_project?(:commits)
|
||||
|
||||
project.nil? && show_elasticsearch_tabs? && feature_flag_tab_enabled?(:global_search_commits_tab)
|
||||
end
|
||||
|
||||
def show_issues_search_tab?
|
||||
return true if tab_enabled_for_project?(:issues)
|
||||
|
||||
project.nil? && feature_flag_tab_enabled?(:global_search_issues_tab)
|
||||
end
|
||||
|
||||
def show_merge_requests_search_tab?
|
||||
return true if tab_enabled_for_project?(:merge_requests)
|
||||
|
||||
project.nil? && feature_flag_tab_enabled?(:global_search_merge_requests_tab)
|
||||
end
|
||||
|
||||
def show_comments_search_tab?
|
||||
return true if tab_enabled_for_project?(:notes)
|
||||
|
||||
project.nil? && show_elasticsearch_tabs?
|
||||
end
|
||||
|
||||
def show_snippets_search_tab?
|
||||
!!options[:show_snippets] && project.nil? && feature_flag_tab_enabled?(:global_search_snippet_titles_tab)
|
||||
end
|
||||
|
||||
def show_milestones_search_tab?
|
||||
project.nil? || tab_enabled_for_project?(:milestones)
|
||||
end
|
||||
|
||||
def feature_flag_tab_enabled?(flag)
|
||||
group.present? || Feature.enabled?(flag, user, type: :ops)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Search::Navigation.prepend_mod
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
"@gitlab/svgs": "3.55.0",
|
||||
"@gitlab/ui": "64.20.0",
|
||||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230620122149",
|
||||
"@gitlab/web-ide": "0.0.1-dev-20230713160749",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"@rails/actioncable": "7.0.6",
|
||||
|
|
|
|||
|
|
@ -34,11 +34,13 @@ module RuboCop
|
|||
class StrongMemoizeAttr < RuboCop::Cop::Base
|
||||
extend RuboCop::Cop::AutoCorrector
|
||||
|
||||
MSG = 'Use `strong_memoize_attr`, instead of using `strong_memoize` directly.'
|
||||
STRONG_MEMOIZE_MSG = 'Use `strong_memoize_attr`, instead of using `strong_memoize` directly.'
|
||||
STRONG_MEMOIZE_WITH_MSG =
|
||||
'Use `strong_memoize_attr`, instead of using `strong_memoize_with` without parameters.'
|
||||
|
||||
def_node_matcher :strong_memoize?, <<~PATTERN
|
||||
(block
|
||||
$(send nil? :strong_memoize
|
||||
$(send nil? {:strong_memoize | :strong_memoize_with}
|
||||
(sym _)
|
||||
)
|
||||
(args)
|
||||
|
|
@ -58,7 +60,14 @@ module RuboCop
|
|||
|
||||
corrector = autocorrect_pure_definitions(node.parent, body) if node.parent.def_type?
|
||||
|
||||
add_offense(send_node, &corrector)
|
||||
message = case send_node.method_name
|
||||
when :strong_memoize
|
||||
STRONG_MEMOIZE_MSG
|
||||
when :strong_memoize_with
|
||||
STRONG_MEMOIZE_WITH_MSG
|
||||
end
|
||||
|
||||
add_offense(send_node, message: message, &corrector)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
require 'bundler/inline'
|
||||
|
||||
gemfile do
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rspec_flaky', path: 'gems/rspec_flaky'
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -42,14 +42,6 @@ RSpec.describe KasCookie, feature_category: :deployment_management do
|
|||
expect(kas_cookie).to eq('foobar')
|
||||
expect(::Gitlab::Kas::UserAccess).to have_received(:cookie_data)
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(kas_user_access: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be_blank }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -88,60 +80,42 @@ RSpec.describe KasCookie, feature_category: :deployment_management do
|
|||
request.env['action_dispatch.content_security_policy'].directives['connect-src']
|
||||
end
|
||||
|
||||
context "when feature flag is disabled" do
|
||||
context 'when KAS is on same domain as rails' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://gitlab.example.com/-/k8s-proxy/' }
|
||||
|
||||
before do
|
||||
stub_feature_flags(kas_user_access: false)
|
||||
end
|
||||
|
||||
it 'does not add KAS url to connect-src directives' do
|
||||
it 'does not add KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).not_to include(::Gitlab::Kas.tunnel_url)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag is enabled' do
|
||||
before do
|
||||
stub_feature_flags(kas_user_access: true)
|
||||
context 'when KAS is on subdomain' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://kas.gitlab.example.com/k8s-proxy/' }
|
||||
|
||||
it 'adds KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).to include(::Gitlab::Kas.tunnel_url)
|
||||
end
|
||||
|
||||
context 'when KAS is on same domain as rails' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://gitlab.example.com/-/k8s-proxy/' }
|
||||
context 'when content_security_policy is disabled' do
|
||||
let(:content_security_policy_enabled) { false }
|
||||
|
||||
it 'does not add KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).not_to include(::Gitlab::Kas.tunnel_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when KAS is on subdomain' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://kas.gitlab.example.com/k8s-proxy/' }
|
||||
context 'when KAS tunnel url is configured without trailing slash' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://kas.gitlab.example.com/k8s-proxy' }
|
||||
|
||||
it 'adds KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).to include(::Gitlab::Kas.tunnel_url)
|
||||
end
|
||||
|
||||
context 'when content_security_policy is disabled' do
|
||||
let(:content_security_policy_enabled) { false }
|
||||
|
||||
it 'does not add KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).not_to include(::Gitlab::Kas.tunnel_url)
|
||||
end
|
||||
end
|
||||
it 'adds KAS url to CSP connect-src directive with trailing slash' do
|
||||
expect(kas_csp_connect_src).to include("#{::Gitlab::Kas.tunnel_url}/")
|
||||
end
|
||||
|
||||
context 'when KAS tunnel url is configured without trailing slash' do
|
||||
let_it_be(:kas_tunnel_url) { 'ws://kas.gitlab.example.com/k8s-proxy' }
|
||||
context 'when content_security_policy is disabled' do
|
||||
let(:content_security_policy_enabled) { false }
|
||||
|
||||
it 'adds KAS url to CSP connect-src directive with trailing slash' do
|
||||
expect(kas_csp_connect_src).to include("#{::Gitlab::Kas.tunnel_url}/")
|
||||
end
|
||||
|
||||
context 'when content_security_policy is disabled' do
|
||||
let(:content_security_policy_enabled) { false }
|
||||
|
||||
it 'does not add KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).not_to include("#{::Gitlab::Kas.tunnel_url}/")
|
||||
end
|
||||
it 'does not add KAS url to CSP connect-src directive' do
|
||||
expect(kas_csp_connect_src).not_to include("#{::Gitlab::Kas.tunnel_url}/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -383,20 +383,6 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when the `slack_app_self_managed` flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(slack_app_self_managed: false)
|
||||
visit general_admin_application_settings_path
|
||||
end
|
||||
|
||||
it 'does not display any sections' do
|
||||
expect(page).not_to have_selector('.as-slack')
|
||||
expect(page).not_to have_content(configure_heading)
|
||||
expect(page).not_to have_content(create_heading)
|
||||
expect(page).not_to have_content(update_heading)
|
||||
end
|
||||
end
|
||||
|
||||
it 'changes the settings' do
|
||||
page.within('.as-slack') do
|
||||
check 'Enable GitLab for Slack app'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Search Snippets', :js, feature_category: :source_code_management do
|
||||
RSpec.describe 'Search Snippets', :js, feature_category: :global_search do
|
||||
it 'user searches for snippets by title' do
|
||||
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
|
||||
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ describe('~/environments/components/form.vue', () => {
|
|||
|
||||
const createWrapperWithApollo = ({
|
||||
propsData = {},
|
||||
kasUserAccessProject = false,
|
||||
kubernetesNamespaceForEnvironment = false,
|
||||
queryResult = null,
|
||||
} = {}) => {
|
||||
|
|
@ -74,7 +73,6 @@ describe('~/environments/components/form.vue', () => {
|
|||
provide: {
|
||||
...PROVIDE,
|
||||
glFeatures: {
|
||||
kasUserAccessProject,
|
||||
kubernetesNamespaceForEnvironment,
|
||||
},
|
||||
},
|
||||
|
|
@ -303,15 +301,9 @@ describe('~/environments/components/form.vue', () => {
|
|||
expect(findNamespaceSelector().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it("doesn't render namespace selector if `kasUserAccessProject` feature flag is disabled", () => {
|
||||
wrapper = createWrapperWithApollo();
|
||||
expect(findNamespaceSelector().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when `kasUserAccessProject` and `kubernetesNamespaceForEnvironment` feature flags are enabled', () => {
|
||||
describe('when `kubernetesNamespaceForEnvironment` feature flag is enabled', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = createWrapperWithApollo({
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
});
|
||||
});
|
||||
|
|
@ -410,7 +402,6 @@ describe('~/environments/components/form.vue', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
wrapper = createWrapperWithApollo({
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
queryResult: jest.fn().mockRejectedValueOnce(error),
|
||||
});
|
||||
|
|
@ -439,7 +430,6 @@ describe('~/environments/components/form.vue', () => {
|
|||
beforeEach(() => {
|
||||
wrapper = createWrapperWithApollo({
|
||||
propsData: { environment: environmentWithAgent },
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
});
|
||||
});
|
||||
|
|
@ -473,7 +463,6 @@ describe('~/environments/components/form.vue', () => {
|
|||
beforeEach(() => {
|
||||
wrapper = createWrapperWithApollo({
|
||||
propsData: { environment: environmentWithAgentAndNamespace },
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -537,11 +537,6 @@ describe('~/environments/components/new_environment_item.vue', () => {
|
|||
it('should request agent data when the environment is visible if the feature flag is enabled', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
provideData: {
|
||||
glFeatures: {
|
||||
kasUserAccessProject: true,
|
||||
},
|
||||
},
|
||||
apolloProvider: createApolloProvider(agent),
|
||||
});
|
||||
|
||||
|
|
@ -553,12 +548,11 @@ describe('~/environments/components/new_environment_item.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should request agent data with kubernetes namespace when `kasUserAccessProject` and `kubernetesNamespaceForEnvironment` feature flags are enabled', async () => {
|
||||
it('should request agent data with kubernetes namespace when `kubernetesNamespaceForEnvironment` feature flag is enabled', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
provideData: {
|
||||
glFeatures: {
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -573,14 +567,9 @@ describe('~/environments/components/new_environment_item.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should render if the feature flag is enabled and the environment has an agent associated', async () => {
|
||||
it('should render if the environment has an agent associated', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
provideData: {
|
||||
glFeatures: {
|
||||
kasUserAccessProject: true,
|
||||
},
|
||||
},
|
||||
apolloProvider: createApolloProvider(agent),
|
||||
});
|
||||
|
||||
|
|
@ -592,12 +581,11 @@ describe('~/environments/components/new_environment_item.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should render with the namespace if `kasUserAccessProject` and `kubernetesNamespaceForEnvironment` feature flags are enabled and the environment has an agent associated', async () => {
|
||||
it('should render with the namespace if `kubernetesNamespaceForEnvironment` feature flag is enabled and the environment has an agent associated', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
provideData: {
|
||||
glFeatures: {
|
||||
kasUserAccessProject: true,
|
||||
kubernetesNamespaceForEnvironment: true,
|
||||
},
|
||||
},
|
||||
|
|
@ -613,26 +601,9 @@ describe('~/environments/components/new_environment_item.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should not render if the feature flag is not enabled', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
apolloProvider: createApolloProvider(agent),
|
||||
});
|
||||
|
||||
await expandCollapsedSection();
|
||||
|
||||
expect(queryResponseHandler).not.toHaveBeenCalled();
|
||||
expect(findKubernetesOverview().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not render if the environment has no agent object', async () => {
|
||||
wrapper = createWrapper({
|
||||
propsData: { environment: resolvedEnvironment },
|
||||
provideData: {
|
||||
glFeatures: {
|
||||
kasUserAccessProject: true,
|
||||
},
|
||||
},
|
||||
apolloProvider: createApolloProvider(),
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -219,7 +219,11 @@ describe('PackagesListApp', () => {
|
|||
await waitForPromises();
|
||||
|
||||
expect(resolver).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ before: pagination().startCursor, last: GRAPHQL_PAGE_SIZE }),
|
||||
expect.objectContaining({
|
||||
first: null,
|
||||
before: pagination().startCursor,
|
||||
last: GRAPHQL_PAGE_SIZE,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import BlobContent from '~/blob/components/blob_content.vue';
|
|||
import BlobHeader from '~/blob/components/blob_header.vue';
|
||||
import BlobButtonGroup from '~/repository/components/blob_button_group.vue';
|
||||
import BlobContentViewer from '~/repository/components/blob_content_viewer.vue';
|
||||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
import WebIdeLink from 'ee_else_ce/vue_shared/components/web_ide_link.vue';
|
||||
import ForkSuggestion from '~/repository/components/fork_suggestion.vue';
|
||||
import { loadViewer } from '~/repository/components/blob_viewers';
|
||||
import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue';
|
||||
|
|
|
|||
|
|
@ -31,12 +31,13 @@ const TEST_ACTION_2 = {
|
|||
describe('vue_shared/components/actions_button', () => {
|
||||
let wrapper;
|
||||
|
||||
function createComponent(props) {
|
||||
function createComponent({ props = {}, slots = {} } = {}) {
|
||||
wrapper = shallowMountExtended(ActionsButton, {
|
||||
propsData: { actions: [TEST_ACTION, TEST_ACTION_2], toggleText: 'Edit', ...props },
|
||||
stubs: {
|
||||
GlDisclosureDropdownItem,
|
||||
},
|
||||
slots,
|
||||
});
|
||||
}
|
||||
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
|
||||
|
|
@ -47,11 +48,29 @@ describe('vue_shared/components/actions_button', () => {
|
|||
expect(findDropdown().props().toggleText).toBe('Edit');
|
||||
});
|
||||
|
||||
it('dropdown has a fluid width', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findDropdown().props().fluidWidth).toBe(true);
|
||||
});
|
||||
|
||||
it('provides a default slot', () => {
|
||||
const slotContent = 'default text';
|
||||
|
||||
createComponent({
|
||||
slots: {
|
||||
default: slotContent,
|
||||
},
|
||||
});
|
||||
|
||||
expect(findDropdown().text()).toContain(slotContent);
|
||||
});
|
||||
|
||||
it('allows customizing variant and category', () => {
|
||||
const variant = 'confirm';
|
||||
const category = 'secondary';
|
||||
|
||||
createComponent({ variant, category });
|
||||
createComponent({ props: { variant, category } });
|
||||
|
||||
expect(findDropdown().props()).toMatchObject({ category, variant });
|
||||
});
|
||||
|
|
@ -88,4 +107,13 @@ describe('vue_shared/components/actions_button', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['shown', 'hidden'])(
|
||||
'bubbles up %s event from the disclosure dropdown component',
|
||||
(event) => {
|
||||
createComponent();
|
||||
findDropdown().vm.$emit(event);
|
||||
expect(wrapper.emitted(event)).toHaveLength(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ describe('vue_shared/components/web_ide_link', () => {
|
|||
|
||||
let wrapper;
|
||||
|
||||
function createComponent(props, { mountFn = shallowMountExtended, glFeatures = {} } = {}) {
|
||||
function createComponent(props, { mountFn = shallowMountExtended, slots = {} } = {}) {
|
||||
const fakeApollo = createMockApollo([
|
||||
[getWritableForksQuery, jest.fn().mockResolvedValue(getWritableForksResponse)],
|
||||
]);
|
||||
|
|
@ -98,9 +98,7 @@ describe('vue_shared/components/web_ide_link', () => {
|
|||
forkPath,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
glFeatures,
|
||||
},
|
||||
slots,
|
||||
stubs: {
|
||||
GlModal: stubComponent(GlModal, {
|
||||
template: `
|
||||
|
|
@ -215,6 +213,27 @@ describe('vue_shared/components/web_ide_link', () => {
|
|||
expect(findActionsButton().props('actions')).toEqual(expectedActions);
|
||||
});
|
||||
|
||||
it('bubbles up shown and hidden events triggered by actions button component', () => {
|
||||
createComponent();
|
||||
|
||||
expect(wrapper.emitted('shown')).toBe(undefined);
|
||||
expect(wrapper.emitted('hidden')).toBe(undefined);
|
||||
|
||||
findActionsButton().vm.$emit('shown');
|
||||
findActionsButton().vm.$emit('hidden');
|
||||
|
||||
expect(wrapper.emitted('shown')).toHaveLength(1);
|
||||
expect(wrapper.emitted('hidden')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('exposes a default slot', () => {
|
||||
const slotContent = 'default slot content';
|
||||
|
||||
createComponent({}, { slots: { default: slotContent } });
|
||||
|
||||
expect(wrapper.text()).toContain(slotContent);
|
||||
});
|
||||
|
||||
describe('when pipeline editor action is available', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
|
|
|
|||
|
|
@ -722,78 +722,6 @@ RSpec.describe SearchHelper, feature_category: :global_search do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#show_user_search_tab?' do
|
||||
subject { show_user_search_tab? }
|
||||
|
||||
let(:current_user) { build(:user) }
|
||||
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(current_user)
|
||||
end
|
||||
|
||||
context 'when project search' do
|
||||
before do
|
||||
@project = :some_project
|
||||
|
||||
expect(self).to receive(:project_search_tabs?)
|
||||
.with(:users)
|
||||
.and_return(:value)
|
||||
end
|
||||
|
||||
it 'delegates to project_search_tabs?' do
|
||||
expect(subject).to eq(:value)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when group search' do
|
||||
before do
|
||||
@group = :some_group
|
||||
end
|
||||
|
||||
context 'when current_user can read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
context 'when current_user cannot read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when global search' do
|
||||
context 'when current_user can read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
|
||||
context 'when global_search_user_tab feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(global_search_users_tab: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when current_user cannot read_users_list' do
|
||||
before do
|
||||
allow(self).to receive(:can?).with(current_user, :read_users_list).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#repository_ref' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
@ -1113,248 +1041,24 @@ RSpec.describe SearchHelper, feature_category: :global_search do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.search_navigation' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
let(:user) { build(:user) }
|
||||
let_it_be(:project) { build(:project) }
|
||||
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
allow(self).to receive(:can?).and_return(true)
|
||||
allow(self).to receive(:project_search_tabs?).and_return(false)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
context 'projects' do
|
||||
where(:global_project, :condition) do
|
||||
nil | true
|
||||
ref(:project) | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
|
||||
expect(search_navigation[:projects][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'code' do
|
||||
where(:feature_flag_tab_enabled, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
|
||||
false | false | nil | false | false
|
||||
true | true | nil | true | true
|
||||
true | false | nil | false | false
|
||||
false | true | nil | false | false
|
||||
false | false | ref(:project) | true | true
|
||||
true | false | ref(:project) | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_code_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:blobs).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'issues' do
|
||||
where(:project_search_tabs, :global_search_issues_tab, :global_project, :condition) do
|
||||
false | false | nil | false
|
||||
false | true | nil | true
|
||||
false | true | ref(:project) | false
|
||||
false | false | ref(:project) | false
|
||||
true | false | nil | true
|
||||
true | true | nil | true
|
||||
true | false | ref(:project) | true
|
||||
true | true | ref(:project) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_issues_tab).and_return(global_search_issues_tab)
|
||||
allow(self).to receive(:project_search_tabs?).with(:issues).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:issues][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'merge requests' do
|
||||
where(:project_search_tabs, :feature_flag_tab_enabled, :global_project, :condition) do
|
||||
false | false | nil | false
|
||||
true | false | nil | true
|
||||
false | false | ref(:project) | false
|
||||
true | false | ref(:project) | true
|
||||
false | true | nil | true
|
||||
true | true | nil | true
|
||||
false | true | ref(:project) | false
|
||||
true | true | ref(:project) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_merge_requests_tab).and_return(feature_flag_tab_enabled)
|
||||
allow(self).to receive(:project_search_tabs?).with(:merge_requests).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:merge_requests][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'wiki' do
|
||||
where(:global_search_wiki_tab, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
|
||||
false | false | nil | true | true
|
||||
false | false | nil | false | false
|
||||
false | false | ref(:project) | false | false
|
||||
false | true | nil | false | false
|
||||
false | true | ref(:project) | false | false
|
||||
true | false | nil | false | false
|
||||
true | true | ref(:project) | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_wiki_tab).and_return(global_search_wiki_tab)
|
||||
allow(self).to receive(:project_search_tabs?).with(:wiki_blobs).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:wiki_blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'commits' do
|
||||
where(:global_search_commits_tab, :show_elasticsearch_tabs, :global_project, :project_search_tabs, :condition) do
|
||||
false | false | nil | true | true
|
||||
false | false | ref(:project) | true | true
|
||||
false | false | nil | false | false
|
||||
false | true | ref(:project) | false | false
|
||||
false | true | nil | false | false
|
||||
true | false | nil | false | false
|
||||
true | false | ref(:project) | false | false
|
||||
true | true | ref(:project) | false | false
|
||||
true | true | nil | false | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_commits_tab).and_return(global_search_commits_tab)
|
||||
allow(self).to receive(:project_search_tabs?).with(:commits).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:commits][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'comments' do
|
||||
where(:project_search_tabs, :show_elasticsearch_tabs, :global_project, :condition) do
|
||||
true | true | nil | true
|
||||
true | true | ref(:project) | true
|
||||
false | false | nil | false
|
||||
false | false | ref(:project) | false
|
||||
false | true | nil | true
|
||||
false | true | ref(:project) | false
|
||||
true | false | nil | true
|
||||
true | false | ref(:project) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(show_elasticsearch_tabs)
|
||||
allow(self).to receive(:project_search_tabs?).with(:notes).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:notes][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'milestones' do
|
||||
where(:global_project, :project_search_tabs, :condition) do
|
||||
ref(:project) | true | true
|
||||
nil | false | true
|
||||
ref(:project) | false | false
|
||||
nil | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
@project = global_project
|
||||
allow(self).to receive(:project_search_tabs?).with(:milestones).and_return(project_search_tabs)
|
||||
|
||||
expect(search_navigation[:milestones][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'users' do
|
||||
where(:show_user_search_tab, :condition) do
|
||||
true | true
|
||||
false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(self).to receive(:show_user_search_tab?).and_return(show_user_search_tab)
|
||||
|
||||
expect(search_navigation[:users][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'snippet_titles' do
|
||||
where(:global_project, :global_show_snippets, :global_feature_flag_enabled, :condition) do
|
||||
ref(:project) | true | false | false
|
||||
nil | false | false | false
|
||||
ref(:project) | false | false | false
|
||||
nil | true | false | false
|
||||
ref(:project) | true | true | false
|
||||
nil | false | true | false
|
||||
ref(:project) | false | true | false
|
||||
nil | true | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_service).to receive(:show_snippets?).and_return(global_show_snippets)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).with(:global_search_snippet_titles_tab)
|
||||
.and_return(global_feature_flag_enabled)
|
||||
@project = global_project
|
||||
|
||||
expect(search_navigation[:snippet_titles][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_navigation_json' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
context 'with data' do
|
||||
context 'with some tab conditions set to false' do
|
||||
example_data_1 = {
|
||||
projects: { label: _("Projects"), condition: true },
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
}
|
||||
|
||||
example_data_2 = {
|
||||
projects: { label: _("Projects"), condition: false },
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
blobs: { label: _("Code"), condition: false }
|
||||
}
|
||||
|
||||
example_data_3 = {
|
||||
projects: { label: _("Projects"), condition: true },
|
||||
blobs: { label: _("Code"), condition: true },
|
||||
epics: { label: _("Epics"), condition: true }
|
||||
blobs: { label: _("Code"), condition: true },
|
||||
epics: { label: _("Epics"), condition: true }
|
||||
}
|
||||
|
||||
where(:data, :matcher) do
|
||||
|
|
@ -1365,28 +1069,31 @@ RSpec.describe SearchHelper, feature_category: :global_search do
|
|||
|
||||
with_them do
|
||||
it 'renders data correctly' do
|
||||
allow(self).to receive(:search_navigation).with(no_args).and_return(data)
|
||||
allow(self).to receive(:current_user).and_return(build(:user))
|
||||
allow_next_instance_of(Search::Navigation) do |search_nav|
|
||||
allow(search_nav).to receive(:tabs).and_return(data)
|
||||
end
|
||||
|
||||
expect(search_navigation_json).to instance_exec(&matcher)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.search_navigation_json with .search_navigation' do
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(build(:user))
|
||||
allow(self).to receive(:can?).and_return(true)
|
||||
allow(self).to receive(:project_search_tabs?).and_return(true)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).and_return(true)
|
||||
allow(self).to receive(:feature_flag_tab_enabled?).and_return(true)
|
||||
allow(search_service).to receive(:show_elasticsearch_tabs?).and_return(true)
|
||||
allow(search_service).to receive(:show_snippets?).and_return(true)
|
||||
@project = nil
|
||||
end
|
||||
context 'when all options enabled' do
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(build(:user))
|
||||
allow(search_service).to receive(:show_snippets?).and_return(true)
|
||||
allow_next_instance_of(Search::Navigation) do |search_nav|
|
||||
allow(search_nav).to receive(:tab_enabled_for_project?).and_return(true)
|
||||
allow(search_nav).to receive(:feature_flag_tab_enabled?).and_return(true)
|
||||
end
|
||||
|
||||
it 'test search navigation item order for CE all options enabled' do
|
||||
expect(Gitlab::Json.parse(search_navigation_json).keys).to eq(%w[projects blobs issues merge_requests wiki_blobs commits notes milestones users snippet_titles])
|
||||
@project = nil
|
||||
end
|
||||
|
||||
it 'returns items in order' do
|
||||
expect(Gitlab::Json.parse(search_navigation_json).keys).to eq(%w[projects blobs issues merge_requests wiki_blobs commits notes milestones users snippet_titles])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1431,30 +1138,6 @@ RSpec.describe SearchHelper, feature_category: :global_search do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'show_elasticsearch_tabs' do
|
||||
subject { search_service.show_elasticsearch_tabs? }
|
||||
|
||||
let(:user) { build(:user) }
|
||||
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
describe 'show_epics' do
|
||||
subject { search_service.show_epics? }
|
||||
|
||||
let(:user) { build(:user) }
|
||||
|
||||
before do
|
||||
allow(self).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
describe '#wiki_blob_link' do
|
||||
let_it_be(:project) { create :project, :wiki_repo }
|
||||
let(:wiki_blob) do
|
||||
|
|
|
|||
|
|
@ -11,42 +11,6 @@ RSpec.describe Gitlab::Kas::UserAccess, feature_category: :deployment_management
|
|||
end
|
||||
|
||||
it { is_expected.to be true }
|
||||
|
||||
context 'when flag kas_user_access is disabled' do
|
||||
before do
|
||||
stub_feature_flags(kas_user_access: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.enabled_for?' do
|
||||
subject { described_class.enabled_for?(agent) }
|
||||
|
||||
let(:agent) { build(:cluster_agent) }
|
||||
|
||||
before do
|
||||
allow(::Gitlab::Kas).to receive(:enabled?).and_return true
|
||||
end
|
||||
|
||||
it { is_expected.to be true }
|
||||
|
||||
context 'when flag kas_user_access is disabled' do
|
||||
before do
|
||||
stub_feature_flags(kas_user_access: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
|
||||
context 'when flag kas_user_access_project is disabled' do
|
||||
before do
|
||||
stub_feature_flags(kas_user_access_project: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be false }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.{encrypt,decrypt}_public_session_id' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,280 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Search::Navigation, feature_category: :global_search do
|
||||
let(:user) { instance_double(User) }
|
||||
let(:project_double) { instance_double(Project) }
|
||||
let(:options) { {} }
|
||||
let(:search_navigation) { described_class.new(user: user, project: project, options: options) }
|
||||
|
||||
describe '#tab_enabled_for_project?' do
|
||||
let(:project) { project_double }
|
||||
let(:tab) { :blobs }
|
||||
|
||||
subject(:tab_enabled_for_project) { search_navigation.tab_enabled_for_project?(tab) }
|
||||
|
||||
context 'when user has ability for tab' do
|
||||
before do
|
||||
allow(search_navigation).to receive(:can?).with(user, :read_code, project_double).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
context 'when user does not have ability for tab' do
|
||||
before do
|
||||
allow(search_navigation).to receive(:can?).with(user, :read_code, project_double).and_return(false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
|
||||
context 'when an array of projects is provided' do
|
||||
let(:project) { Array.wrap(project_double) }
|
||||
|
||||
before do
|
||||
allow(search_navigation).to receive(:can?).with(user, :read_code, project_double).and_return(true)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(true) }
|
||||
end
|
||||
|
||||
context 'when project is not present' do
|
||||
let_it_be(:project) { nil }
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#tabs' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
before do
|
||||
allow(search_navigation).to receive(:can?).and_return(true)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).and_return(false)
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?).and_return(false)
|
||||
end
|
||||
|
||||
subject(:tabs) { search_navigation.tabs }
|
||||
|
||||
context 'for projects tab' do
|
||||
where(:project, :condition) do
|
||||
nil | true
|
||||
ref(:project_double) | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
expect(tabs[:projects][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for code tab' do
|
||||
where(:feature_flag_enabled, :show_elasticsearch_tabs, :project, :tab_enabled, :condition) do
|
||||
false | false | nil | false | false
|
||||
true | true | nil | true | true
|
||||
true | false | nil | false | false
|
||||
false | true | nil | false | false
|
||||
false | false | ref(:project_double) | true | true
|
||||
true | false | ref(:project_double) | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:options) { { show_elasticsearch_tabs: show_elasticsearch_tabs } }
|
||||
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_code_tab).and_return(feature_flag_enabled)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:blobs).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for issues tab' do
|
||||
where(:tab_enabled, :feature_flag_enabled, :project, :condition) do
|
||||
false | false | nil | false
|
||||
false | true | nil | true
|
||||
false | true | ref(:project_double) | false
|
||||
false | false | ref(:project_double) | false
|
||||
true | false | nil | true
|
||||
true | true | nil | true
|
||||
true | false | ref(:project_double) | true
|
||||
true | true | ref(:project_double) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_issues_tab).and_return(feature_flag_enabled)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:issues).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:issues][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for merge requests tab' do
|
||||
where(:tab_enabled, :feature_flag_enabled, :project, :condition) do
|
||||
false | false | nil | false
|
||||
true | false | nil | true
|
||||
false | false | ref(:project_double) | false
|
||||
true | false | ref(:project_double) | true
|
||||
false | true | nil | true
|
||||
true | true | nil | true
|
||||
false | true | ref(:project_double) | false
|
||||
true | true | ref(:project_double) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_merge_requests_tab).and_return(feature_flag_enabled)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:merge_requests).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:merge_requests][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for wiki tab' do
|
||||
where(:feature_flag_enabled, :show_elasticsearch_tabs, :project, :tab_enabled, :condition) do
|
||||
false | false | nil | true | true
|
||||
false | false | nil | false | false
|
||||
false | false | ref(:project_double) | false | false
|
||||
false | true | nil | false | false
|
||||
false | true | ref(:project_double) | false | false
|
||||
true | false | nil | false | false
|
||||
true | true | ref(:project_double) | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:options) { { show_elasticsearch_tabs: show_elasticsearch_tabs } }
|
||||
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_wiki_tab).and_return(feature_flag_enabled)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:wiki_blobs).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:wiki_blobs][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for commits tab' do
|
||||
where(:feature_flag_enabled, :show_elasticsearch_tabs, :project, :tab_enabled, :condition) do
|
||||
false | false | nil | true | true
|
||||
false | false | ref(:project_double) | true | true
|
||||
false | false | nil | false | false
|
||||
false | true | ref(:project_double) | false | false
|
||||
false | true | nil | false | false
|
||||
true | false | nil | false | false
|
||||
true | false | ref(:project_double) | false | false
|
||||
true | true | ref(:project_double) | false | false
|
||||
true | true | nil | false | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:options) { { show_elasticsearch_tabs: show_elasticsearch_tabs } }
|
||||
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_commits_tab).and_return(feature_flag_enabled)
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:commits).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:commits][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for comments tab' do
|
||||
where(:tab_enabled, :show_elasticsearch_tabs, :project, :condition) do
|
||||
true | true | nil | true
|
||||
true | true | ref(:project_double) | true
|
||||
false | false | nil | false
|
||||
false | false | ref(:project_double) | false
|
||||
false | true | nil | true
|
||||
false | true | ref(:project_double) | false
|
||||
true | false | nil | true
|
||||
true | false | ref(:project_double) | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:options) { { show_elasticsearch_tabs: show_elasticsearch_tabs } }
|
||||
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:notes).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:notes][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for milestones tab' do
|
||||
where(:project, :tab_enabled, :condition) do
|
||||
ref(:project_double) | true | true
|
||||
nil | false | true
|
||||
ref(:project_double) | false | false
|
||||
nil | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:milestones).and_return(tab_enabled)
|
||||
|
||||
expect(tabs[:milestones][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for users tab' do
|
||||
where(:feature_flag_enabled, :can_read_users_list, :project, :tab_enabled, :condition) do
|
||||
false | false | ref(:project_double) | true | true
|
||||
false | false | nil | false | false
|
||||
false | true | nil | false | false
|
||||
false | true | ref(:project_double) | false | false
|
||||
true | true | nil | false | true
|
||||
true | true | ref(:project_double) | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:tab_enabled_for_project?).with(:users).and_return(tab_enabled)
|
||||
allow(search_navigation).to receive(:can?)
|
||||
.with(user, :read_users_list, project_double).and_return(can_read_users_list)
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_users_tab).and_return(feature_flag_enabled)
|
||||
|
||||
expect(tabs[:users][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for snippet_titles tab' do
|
||||
where(:project, :show_snippets, :feature_flag_enabled, :condition) do
|
||||
ref(:project_double) | true | false | false
|
||||
nil | false | false | false
|
||||
ref(:project_double) | false | false | false
|
||||
nil | true | false | false
|
||||
ref(:project_double) | true | true | false
|
||||
nil | false | true | false
|
||||
ref(:project_double) | false | true | false
|
||||
nil | true | true | true
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:options) { { show_snippets: show_snippets } }
|
||||
|
||||
it 'data item condition is set correctly' do
|
||||
allow(search_navigation).to receive(:feature_flag_tab_enabled?)
|
||||
.with(:global_search_snippet_titles_tab).and_return(feature_flag_enabled)
|
||||
|
||||
expect(tabs[:snippet_titles][:condition]).to eq(condition)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -198,14 +198,6 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do
|
|||
|
||||
it { is_expected.to eq(allowed) }
|
||||
end
|
||||
|
||||
context 'when expose_authorized_cluster_agents feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(expose_authorized_cluster_agents: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with group-level authorization' do
|
||||
|
|
@ -226,14 +218,6 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do
|
|||
|
||||
it { is_expected.to eq(allowed) }
|
||||
end
|
||||
|
||||
context 'when expose_authorized_cluster_agents feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(expose_authorized_cluster_agents: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -269,14 +253,6 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do
|
|||
|
||||
it { is_expected.to eq(allowed) }
|
||||
end
|
||||
|
||||
context 'when expose_authorized_cluster_agents feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(expose_authorized_cluster_agents: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with group-level authorization' do
|
||||
|
|
@ -297,14 +273,6 @@ RSpec.describe Clusters::Agent, feature_category: :deployment_management do
|
|||
|
||||
it { is_expected.to eq(allowed) }
|
||||
end
|
||||
|
||||
context 'when expose_authorized_cluster_agents feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(expose_authorized_cluster_agents: false)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(false) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -547,18 +547,6 @@ RSpec.describe API::Internal::Kubernetes, feature_category: :deployment_manageme
|
|||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
|
||||
it 'returns 401 when global flag is disabled' do
|
||||
stub_feature_flags(kas_user_access: false)
|
||||
|
||||
deployment_project.add_member(user, :developer)
|
||||
token = new_token
|
||||
public_id = stub_user_session(user, token)
|
||||
access_key = Gitlab::Kas::UserAccess.encrypt_public_session_id(public_id)
|
||||
send_request(params: { agent_id: agent.id, access_type: 'session_cookie', access_key: access_key, csrf_token: mask_token(token) })
|
||||
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
end
|
||||
|
||||
it 'returns 401 when user id is not found in session' do
|
||||
deployment_project.add_member(user, :developer)
|
||||
token = new_token
|
||||
|
|
|
|||
|
|
@ -688,6 +688,16 @@ RSpec.describe API::Search, :clean_gitlab_redis_rate_limiting, feature_category:
|
|||
end
|
||||
end
|
||||
|
||||
context 'when user does not have permissions for scope' do
|
||||
it 'returns an empty array' do
|
||||
project.project_feature.update!(issues_access_level: Gitlab::VisibilityLevel::PRIVATE)
|
||||
|
||||
get api(endpoint, user), params: { scope: 'issues', search: 'awesome' }
|
||||
|
||||
expect(json_response).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project does not exist' do
|
||||
it 'returns 404 error' do
|
||||
get api('/projects/0/search', user), params: { scope: 'issues', search: 'awesome' }
|
||||
|
|
|
|||
|
|
@ -100,4 +100,42 @@ RSpec.describe RuboCop::Cop::Gitlab::StrongMemoizeAttr do
|
|||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strong_memoize_with() is called without parameters' do
|
||||
it 'registers an offense and autocorrects' do
|
||||
expect_offense(<<~RUBY)
|
||||
class Foo
|
||||
def memoized_method
|
||||
strong_memoize_with(:memoized_method) do
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `strong_memoize_attr`, instead of using `strong_memoize_with` without parameters.
|
||||
'This is a memoized method'
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
class Foo
|
||||
def memoized_method
|
||||
'This is a memoized method'
|
||||
end
|
||||
strong_memoize_attr :memoized_method
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strong_memoize_with() is called with parameters' do
|
||||
it 'does not register an offense' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
class Foo
|
||||
def memoized_method(param)
|
||||
strong_memoize_with(:memoized_method, param) do
|
||||
param.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1151,10 +1151,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.7.3.tgz#9ea641146436da388ffbad25d7f2abe0df52c235"
|
||||
integrity sha512-NMV++7Ew1FSBDN1xiZaauU9tfeSfgDHcOLpn+8bGpP+O5orUPm2Eu66R5eC5gkjBPaXosNAxNWtriee+aFk4+g==
|
||||
|
||||
"@gitlab/web-ide@0.0.1-dev-20230620122149":
|
||||
version "0.0.1-dev-20230620122149"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230620122149.tgz#2e5c2c41a3df91227440c1a319001bd59eac8aac"
|
||||
integrity sha512-uJFT0aUiOvPynN8/zp0VB2CVxiiKmzzTiZIJseKY8V1b2kKvW554lRVbNwES34Fo/ZXoCU65ZCmIitbbPgsZYQ==
|
||||
"@gitlab/web-ide@0.0.1-dev-20230713160749":
|
||||
version "0.0.1-dev-20230713160749"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20230713160749.tgz#427aed5e9fa6be1d1f58dde282500759df204b6c"
|
||||
integrity sha512-QySxWa5dzuZw1XGtkFDuTX9CF/kvBMGoiM9PySH2cvx6mGvC9dphis06eeliNKkNZG3arjwNSC6/8JRkg96B5A==
|
||||
|
||||
"@graphql-eslint/eslint-plugin@3.20.0":
|
||||
version "3.20.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue