Commit 23d23711 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 274dff4f
...@@ -56,7 +56,6 @@ Style/FrozenStringLiteralComment: ...@@ -56,7 +56,6 @@ Style/FrozenStringLiteralComment:
- 'qa/**/*' - 'qa/**/*'
- 'rubocop/**/*' - 'rubocop/**/*'
- 'scripts/**/*' - 'scripts/**/*'
- 'spec/lib/gitlab/**/*'
RSpec/FilePath: RSpec/FilePath:
Exclude: Exclude:
......
<script> <script>
import { mapState, mapGetters, mapActions } from 'vuex'; import { mapState, mapGetters, mapActions } from 'vuex';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
import createFlash from '~/flash'; import createFlash from '~/flash';
...@@ -36,11 +37,20 @@ export default { ...@@ -36,11 +37,20 @@ export default {
GlLoadingIcon, GlLoadingIcon,
PanelResizer, PanelResizer,
}, },
mixins: [glFeatureFlagsMixin()],
props: { props: {
endpoint: { endpoint: {
type: String, type: String,
required: true, required: true,
}, },
endpointMetadata: {
type: String,
required: true,
},
endpointBatch: {
type: String,
required: true,
},
projectPath: { projectPath: {
type: String, type: String,
required: true, required: true,
...@@ -92,6 +102,7 @@ export default { ...@@ -92,6 +102,7 @@ export default {
computed: { computed: {
...mapState({ ...mapState({
isLoading: state => state.diffs.isLoading, isLoading: state => state.diffs.isLoading,
isBatchLoading: state => state.diffs.isBatchLoading,
diffFiles: state => state.diffs.diffFiles, diffFiles: state => state.diffs.diffFiles,
diffViewType: state => state.diffs.diffViewType, diffViewType: state => state.diffs.diffViewType,
mergeRequestDiffs: state => state.diffs.mergeRequestDiffs, mergeRequestDiffs: state => state.diffs.mergeRequestDiffs,
...@@ -153,6 +164,8 @@ export default { ...@@ -153,6 +164,8 @@ export default {
mounted() { mounted() {
this.setBaseConfig({ this.setBaseConfig({
endpoint: this.endpoint, endpoint: this.endpoint,
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
projectPath: this.projectPath, projectPath: this.projectPath,
dismissEndpoint: this.dismissEndpoint, dismissEndpoint: this.dismissEndpoint,
showSuggestPopover: this.showSuggestPopover, showSuggestPopover: this.showSuggestPopover,
...@@ -185,6 +198,8 @@ export default { ...@@ -185,6 +198,8 @@ export default {
...mapActions('diffs', [ ...mapActions('diffs', [
'setBaseConfig', 'setBaseConfig',
'fetchDiffFiles', 'fetchDiffFiles',
'fetchDiffFilesMeta',
'fetchDiffFilesBatch',
'startRenderDiffsQueue', 'startRenderDiffsQueue',
'assignDiscussionsToDiff', 'assignDiscussionsToDiff',
'setHighlightedRow', 'setHighlightedRow',
...@@ -196,24 +211,51 @@ export default { ...@@ -196,24 +211,51 @@ export default {
this.assignedDiscussions = false; this.assignedDiscussions = false;
this.fetchData(false); this.fetchData(false);
}, },
isLatestVersion() {
return window.location.search.indexOf('diff_id') === -1;
},
fetchData(toggleTree = true) { fetchData(toggleTree = true) {
this.fetchDiffFiles() if (this.isLatestVersion() && this.glFeatures.diffsBatchLoad) {
.then(() => { this.fetchDiffFilesMeta()
if (toggleTree) { .then(() => {
this.hideTreeListIfJustOneFile(); if (toggleTree) this.hideTreeListIfJustOneFile();
} })
.catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
});
requestIdleCallback( this.fetchDiffFilesBatch()
() => { .then(() => {
this.setDiscussions(); requestIdleCallback(
this.startRenderDiffsQueue(); () => {
}, this.setDiscussions();
{ timeout: 1000 }, this.startRenderDiffsQueue();
); },
}) { timeout: 1000 },
.catch(() => { );
createFlash(__('Something went wrong on our end. Please try again!')); })
}); .catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
});
} else {
this.fetchDiffFiles()
.then(() => {
if (toggleTree) {
this.hideTreeListIfJustOneFile();
}
requestIdleCallback(
() => {
this.setDiscussions();
this.startRenderDiffsQueue();
},
{ timeout: 1000 },
);
})
.catch(() => {
createFlash(__('Something went wrong on our end. Please try again!'));
});
}
if (!this.isNotesFetched) { if (!this.isNotesFetched) {
eventHub.$emit('fetchNotesData'); eventHub.$emit('fetchNotesData');
...@@ -324,7 +366,8 @@ export default { ...@@ -324,7 +366,8 @@ export default {
}" }"
> >
<commit-widget v-if="commit" :commit="commit" /> <commit-widget v-if="commit" :commit="commit" />
<template v-if="renderDiffFiles"> <div v-if="isBatchLoading" class="loading"><gl-loading-icon /></div>
<template v-else-if="renderDiffFiles">
<diff-file <diff-file
v-for="file in diffFiles" v-for="file in diffFiles"
:key="file.newPath" :key="file.newPath"
......
...@@ -57,3 +57,4 @@ export const MIN_RENDERING_MS = 2; ...@@ -57,3 +57,4 @@ export const MIN_RENDERING_MS = 2;
export const START_RENDERING_INDEX = 200; export const START_RENDERING_INDEX = 200;
export const INLINE_DIFF_LINES_KEY = 'highlighted_diff_lines'; export const INLINE_DIFF_LINES_KEY = 'highlighted_diff_lines';
export const PARALLEL_DIFF_LINES_KEY = 'parallel_diff_lines'; export const PARALLEL_DIFF_LINES_KEY = 'parallel_diff_lines';
export const DIFFS_PER_PAGE = 10;
...@@ -67,6 +67,8 @@ export default function initDiffsApp(store) { ...@@ -67,6 +67,8 @@ export default function initDiffsApp(store) {
return { return {
endpoint: dataset.endpoint, endpoint: dataset.endpoint,
endpointMetadata: dataset.endpointMetadata || '',
endpointBatch: dataset.endpointBatch || '',
projectPath: dataset.projectPath, projectPath: dataset.projectPath,
helpPagePath: dataset.helpPagePath, helpPagePath: dataset.helpPagePath,
currentUser: JSON.parse(dataset.currentUserData) || {}, currentUser: JSON.parse(dataset.currentUserData) || {},
...@@ -100,6 +102,8 @@ export default function initDiffsApp(store) { ...@@ -100,6 +102,8 @@ export default function initDiffsApp(store) {
return createElement('diffs-app', { return createElement('diffs-app', {
props: { props: {
endpoint: this.endpoint, endpoint: this.endpoint,
endpointMetadata: this.endpointMetadata,
endpointBatch: this.endpointBatch,
currentUser: this.currentUser, currentUser: this.currentUser,
projectPath: this.projectPath, projectPath: this.projectPath,
helpPagePath: this.helpPagePath, helpPagePath: this.helpPagePath,
......
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
convertExpandLines, convertExpandLines,
idleCallback, idleCallback,
allDiscussionWrappersExpanded, allDiscussionWrappersExpanded,
prepareDiffData,
} from './utils'; } from './utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { import {
...@@ -33,12 +34,27 @@ import { ...@@ -33,12 +34,27 @@ import {
START_RENDERING_INDEX, START_RENDERING_INDEX,
INLINE_DIFF_LINES_KEY, INLINE_DIFF_LINES_KEY,
PARALLEL_DIFF_LINES_KEY, PARALLEL_DIFF_LINES_KEY,
DIFFS_PER_PAGE,
} from '../constants'; } from '../constants';
import { diffViewerModes } from '~/ide/constants'; import { diffViewerModes } from '~/ide/constants';
export const setBaseConfig = ({ commit }, options) => { export const setBaseConfig = ({ commit }, options) => {
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options; const {
commit(types.SET_BASE_CONFIG, { endpoint, projectPath, dismissEndpoint, showSuggestPopover }); endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
} = options;
commit(types.SET_BASE_CONFIG, {
endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
});
}; };
export const fetchDiffFiles = ({ state, commit }) => { export const fetchDiffFiles = ({ state, commit }) => {
...@@ -67,6 +83,53 @@ export const fetchDiffFiles = ({ state, commit }) => { ...@@ -67,6 +83,53 @@ export const fetchDiffFiles = ({ state, commit }) => {
.catch(() => worker.terminate()); .catch(() => worker.terminate());
}; };
export const fetchDiffFilesBatch = ({ commit, state }) => {
const baseUrl = `${state.endpointBatch}?per_page=${DIFFS_PER_PAGE}`;
const url = page => (page ? `${baseUrl}&page=${page}` : baseUrl);
commit(types.SET_BATCH_LOADING, true);
const getBatch = page =>
axios
.get(url(page))
.then(({ data: { pagination, diff_files } }) => {
commit(types.SET_DIFF_DATA_BATCH, { diff_files });
commit(types.SET_BATCH_LOADING, false);
return pagination.next_page;
})
.then(nextPage => nextPage && getBatch(nextPage));
return getBatch()
.then(handleLocationHash)
.catch(() => null);
};
export const fetchDiffFilesMeta = ({ commit, state }) => {
const worker = new TreeWorker();
commit(types.SET_LOADING, true);
worker.addEventListener('message', ({ data }) => {
commit(types.SET_TREE_DATA, data);
worker.terminate();
});
return axios
.get(state.endpointMetadata)
.then(({ data }) => {
const strippedData = { ...data };
strippedData.diff_files = [];
commit(types.SET_LOADING, false);
commit(types.SET_MERGE_REQUEST_DIFFS, data.merge_request_diffs || []);
commit(types.SET_DIFF_DATA, strippedData);
prepareDiffData(data);
worker.postMessage(data.diff_files);
})
.catch(() => worker.terminate());
};
export const setHighlightedRow = ({ commit }, lineCode) => { export const setHighlightedRow = ({ commit }, lineCode) => {
const fileHash = lineCode.split('_')[0]; const fileHash = lineCode.split('_')[0];
commit(types.SET_HIGHLIGHTED_ROW, lineCode); commit(types.SET_HIGHLIGHTED_ROW, lineCode);
......
...@@ -8,6 +8,7 @@ const defaultViewType = INLINE_DIFF_VIEW_TYPE; ...@@ -8,6 +8,7 @@ const defaultViewType = INLINE_DIFF_VIEW_TYPE;
export default () => ({ export default () => ({
isLoading: true, isLoading: true,
isBatchLoading: false,
addedLines: null, addedLines: null,
removedLines: null, removedLines: null,
endpoint: '', endpoint: '',
......
export const SET_BASE_CONFIG = 'SET_BASE_CONFIG'; export const SET_BASE_CONFIG = 'SET_BASE_CONFIG';
export const SET_LOADING = 'SET_LOADING'; export const SET_LOADING = 'SET_LOADING';
export const SET_BATCH_LOADING = 'SET_BATCH_LOADING';
export const SET_DIFF_DATA = 'SET_DIFF_DATA'; export const SET_DIFF_DATA = 'SET_DIFF_DATA';
export const SET_DIFF_DATA_BATCH = 'SET_DIFF_DATA_BATCH';
export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE'; export const SET_DIFF_VIEW_TYPE = 'SET_DIFF_VIEW_TYPE';
export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS'; export const SET_MERGE_REQUEST_DIFFS = 'SET_MERGE_REQUEST_DIFFS';
export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM'; export const TOGGLE_LINE_HAS_FORM = 'TOGGLE_LINE_HAS_FORM';
......
...@@ -12,14 +12,32 @@ import * as types from './mutation_types'; ...@@ -12,14 +12,32 @@ import * as types from './mutation_types';
export default { export default {
[types.SET_BASE_CONFIG](state, options) { [types.SET_BASE_CONFIG](state, options) {
const { endpoint, projectPath, dismissEndpoint, showSuggestPopover } = options; const {
Object.assign(state, { endpoint, projectPath, dismissEndpoint, showSuggestPopover }); endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
} = options;
Object.assign(state, {
endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
});
}, },
[types.SET_LOADING](state, isLoading) { [types.SET_LOADING](state, isLoading) {
Object.assign(state, { isLoading }); Object.assign(state, { isLoading });
}, },
[types.SET_BATCH_LOADING](state, isBatchLoading) {
Object.assign(state, { isBatchLoading });
},
[types.SET_DIFF_DATA](state, data) { [types.SET_DIFF_DATA](state, data) {
prepareDiffData(data); prepareDiffData(data);
...@@ -28,6 +46,12 @@ export default { ...@@ -28,6 +46,12 @@ export default {
}); });
}, },
[types.SET_DIFF_DATA_BATCH](state, data) {
prepareDiffData(data);
state.diffFiles.push(...data.diff_files);
},
[types.RENDER_FILE](state, file) { [types.RENDER_FILE](state, file) {
Object.assign(file, { Object.assign(file, {
renderIt: true, renderIt: true,
......
...@@ -252,10 +252,11 @@ export function prepareDiffData(diffData) { ...@@ -252,10 +252,11 @@ export function prepareDiffData(diffData) {
showingLines += file.parallel_diff_lines.length; showingLines += file.parallel_diff_lines.length;
} }
const name = (file.viewer && file.viewer.name) || diffViewerModes.text;
Object.assign(file, { Object.assign(file, {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY, renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
collapsed: collapsed: name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
file.viewer.name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
isShowingFullFile: false, isShowingFullFile: false,
isLoadingFullFile: false, isLoadingFullFile: false,
discussions: [], discussions: [],
......
...@@ -6,6 +6,7 @@ import _ from 'underscore'; ...@@ -6,6 +6,7 @@ import _ from 'underscore';
import { GlTooltipDirective } from '@gitlab/ui'; import { GlTooltipDirective } from '@gitlab/ui';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import environmentItemMixin from 'ee_else_ce/environments/mixins/environment_item_mixin'; import environmentItemMixin from 'ee_else_ce/environments/mixins/environment_item_mixin';
import ActionsComponent from './environment_actions.vue'; import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue'; import ExternalUrlComponent from './environment_external_url.vue';
...@@ -26,7 +27,6 @@ const timeagoInstance = new Timeago(); ...@@ -26,7 +27,6 @@ const timeagoInstance = new Timeago();
export default { export default {
components: { components: {
UserAvatarLink,
CommitComponent, CommitComponent,
Icon, Icon,
ActionsComponent, ActionsComponent,
...@@ -35,6 +35,8 @@ export default { ...@@ -35,6 +35,8 @@ export default {
RollbackComponent, RollbackComponent,
TerminalButtonComponent, TerminalButtonComponent,
MonitoringButtonComponent, MonitoringButtonComponent,
TooltipOnTruncate,
UserAvatarLink,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -508,12 +510,16 @@ export default { ...@@ -508,12 +510,16 @@ export default {
</div> </div>
<div class="table-section section-15 d-none d-sm-none d-md-block" role="gridcell"> <div class="table-section section-15 d-none d-sm-none d-md-block" role="gridcell">
<a <a v-if="shouldRenderBuildName" :href="buildPath" class="build-link cgray">
v-if="shouldRenderBuildName" <tooltip-on-truncate
:href="buildPath" :title="buildName"
class="build-link cgray flex-truncate-parent" truncate-target="child"
> class="flex-truncate-parent"
<span class="flex-truncate-child">{{ buildName }}</span> >
<span class="flex-truncate-child">
{{ buildName }}
</span>
</tooltip-on-truncate>
</a> </a>
</div> </div>
......
...@@ -31,10 +31,6 @@ export default { ...@@ -31,10 +31,6 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
cssContainerClass: {
type: String,
required: true,
},
newEnvironmentPath: { newEnvironmentPath: {
type: String, type: String,
required: true, required: true,
...@@ -93,7 +89,7 @@ export default { ...@@ -93,7 +89,7 @@ export default {
}; };
</script> </script>
<template> <template>
<div :class="cssContainerClass"> <div>
<stop-environment-modal :environment="environmentInStopModal" /> <stop-environment-modal :environment="environmentInStopModal" />
<confirm-rollback-modal :environment="environmentInRollbackModal" /> <confirm-rollback-modal :environment="environmentInRollbackModal" />
......
...@@ -21,7 +21,6 @@ export default () => ...@@ -21,7 +21,6 @@ export default () =>
newEnvironmentPath: environmentsData.newEnvironmentPath, newEnvironmentPath: environmentsData.newEnvironmentPath,
helpPagePath: environmentsData.helpPagePath, helpPagePath: environmentsData.helpPagePath,
deployBoardsHelpPath: environmentsData.deployBoardsHelpPath, deployBoardsHelpPath: environmentsData.deployBoardsHelpPath,
cssContainerClass: environmentsData.cssClass,
canCreateEnvironment: parseBoolean(environmentsData.canCreateEnvironment), canCreateEnvironment: parseBoolean(environmentsData.canCreateEnvironment),
canReadEnvironment: parseBoolean(environmentsData.canReadEnvironment), canReadEnvironment: parseBoolean(environmentsData.canReadEnvironment),
}; };
...@@ -33,7 +32,6 @@ export default () => ...@@ -33,7 +32,6 @@ export default () =>
newEnvironmentPath: this.newEnvironmentPath, newEnvironmentPath: this.newEnvironmentPath,
helpPagePath: this.helpPagePath, helpPagePath: this.helpPagePath,
deployBoardsHelpPath: this.deployBoardsHelpPath, deployBoardsHelpPath: this.deployBoardsHelpPath,
cssContainerClass: this.cssContainerClass,
canCreateEnvironment: this.canCreateEnvironment, canCreateEnvironment: this.canCreateEnvironment,
canReadEnvironment: this.canReadEnvironment, canReadEnvironment: this.canReadEnvironment,
...this.canaryCalloutProps, ...this.canaryCalloutProps,
......
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
class Clusters::ClustersController < Clusters::BaseController class Clusters::ClustersController < Clusters::BaseController
include RoutableActions include RoutableActions
before_action :cluster, only: [:cluster_status, :show, :update, :destroy] before_action :cluster, only: [:cluster_status, :show, :update, :destroy, :clear_cache]
before_action :generate_gcp_authorize_url, only: [:new] before_action :generate_gcp_authorize_url, only: [:new]
before_action :validate_gcp_token, only: [:new] before_action :validate_gcp_token, only: [:new]
before_action :gcp_cluster, only: [:new] before_action :gcp_cluster, only: [:new]
before_action :user_cluster, only: [:new] before_action :user_cluster, only: [:new]
before_action :authorize_create_cluster!, only: [:new, :authorize_aws_role, :revoke_aws_role, :aws_proxy] before_action :authorize_create_cluster!, only: [:new, :authorize_aws_role, :revoke_aws_role, :aws_proxy]
before_action :authorize_update_cluster!, only: [:update] before_action :authorize_update_cluster!, only: [:update]
before_action :authorize_admin_cluster!, only: [:destroy] before_action :authorize_admin_cluster!, only: [:destroy, :clear_cache]
before_action :update_applications_status, only: [:cluster_status] before_action :update_applications_status, only: [:cluster_status]
before_action only: [:new, :create_gcp] do before_action only: [:new, :create_gcp] do
push_frontend_feature_flag(:create_eks_clusters) push_frontend_feature_flag(:create_eks_clusters)
...@@ -169,6 +169,12 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -169,6 +169,12 @@ class Clusters::ClustersController < Clusters::BaseController
render json: response.body, status: response.status render json: response.body, status: response.status
end end
def clear_cache
cluster.delete_cached_resources!
redirect_to cluster.show_path, notice: _('Cluster cache cleared.')
end
private private
def destroy_params def destroy_params
......
...@@ -267,6 +267,10 @@ module Clusters ...@@ -267,6 +267,10 @@ module Clusters
end end
end end
def delete_cached_resources!
kubernetes_namespaces.delete_all(:delete_all)
end
private private
def unique_management_project_environment_scope def unique_management_project_environment_scope
......
...@@ -65,6 +65,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated ...@@ -65,6 +65,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
raise NotImplementedError raise NotImplementedError
end end
def clear_cluster_cache_path(cluster)
raise NotImplementedError
end
def cluster_path(cluster, params = {}) def cluster_path(cluster, params = {})
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -19,6 +19,11 @@ class GroupClusterablePresenter < ClusterablePresenter ...@@ -19,6 +19,11 @@ class GroupClusterablePresenter < ClusterablePresenter
update_applications_group_cluster_path(clusterable, cluster, application) update_applications_group_cluster_path(clusterable, cluster, application)
end end
override :clear_cluster_cache_path
def clear_cluster_cache_path(cluster)
clear_cache_group_cluster_path(clusterable, cluster)
end
override :cluster_path override :cluster_path
def cluster_path(cluster, params = {}) def cluster_path(cluster, params = {})
group_cluster_path(clusterable, cluster, params) group_cluster_path(clusterable, cluster, params)
......
...@@ -37,6 +37,11 @@ class InstanceClusterablePresenter < ClusterablePresenter ...@@ -37,6 +37,11 @@ class InstanceClusterablePresenter < ClusterablePresenter
update_applications_admin_cluster_path(cluster, application) update_applications_admin_cluster_path(cluster, application)
end end
override :clear_cluster_cache_path
def clear_cluster_cache_path(cluster)
clear_cache_admin_cluster_path(cluster)
end
override :cluster_path override :cluster_path
def cluster_path(cluster, params = {}) def cluster_path(cluster, params = {})
admin_cluster_path(cluster, params) admin_cluster_path(cluster, params)
......
...@@ -19,6 +19,11 @@ class ProjectClusterablePresenter < ClusterablePresenter ...@@ -19,6 +19,11 @@ class ProjectClusterablePresenter < ClusterablePresenter
update_applications_project_cluster_path(clusterable, cluster, application) update_applications_project_cluster_path(clusterable, cluster, application)
end end
override :clear_cluster_cache_path
def clear_cluster_cache_path(cluster)
clear_cache_project_cluster_path(clusterable, cluster)
end
override :cluster_path override :cluster_path
def cluster_path(cluster, params = {}) def cluster_path(cluster, params = {})
project_cluster_path(clusterable, cluster, params) project_cluster_path(clusterable, cluster, params)
......
...@@ -7,4 +7,7 @@ class DiffFileMetadataEntity < Grape::Entity ...@@ -7,4 +7,7 @@ class DiffFileMetadataEntity < Grape::Entity
expose :old_path expose :old_path
expose :new_file?, as: :new_file expose :new_file?, as: :new_file
expose :deleted_file?, as: :deleted_file expose :deleted_file?, as: :deleted_file
expose :file_hash do |diff_file|
Digest::SHA1.hexdigest(diff_file.file_path)
end
end end
...@@ -28,6 +28,14 @@ ...@@ -28,6 +28,14 @@
.form-group .form-group
= field.submit _('Save changes'), class: 'btn btn-success qa-save-domain' = field.submit _('Save changes'), class: 'btn btn-success qa-save-domain'
- if @cluster.managed?
.sub-section.form-group
%h4
= s_('ClusterIntegration|Clear cluster cache')
%p
= s_("ClusterIntegration|Clear the local cache of namespace and service accounts. This is necessary if your integration has become out of sync. The cache is repopulated during the next CI job that requires namespace and service accounts.")
= link_to(s_('ClusterIntegration|Clear cluster cache'), clusterable.clear_cluster_cache_path(@cluster), method: :delete, class: 'btn btn-primary')
.sub-section.form-group .sub-section.form-group
%h4.text-danger %h4.text-danger
= s_('ClusterIntegration|Remove Kubernetes cluster integration') = s_('ClusterIntegration|Remove Kubernetes cluster integration')
......
...@@ -78,6 +78,8 @@ ...@@ -78,6 +78,8 @@
= render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request) = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request)
#js-diffs-app.diffs.tab-pane{ data: { "is-locked" => @merge_request.discussion_locked?, #js-diffs-app.diffs.tab-pane{ data: { "is-locked" => @merge_request.discussion_locked?,
endpoint: diffs_project_merge_request_path(@project, @merge_request, 'json', request.query_parameters), endpoint: diffs_project_merge_request_path(@project, @merge_request, 'json', request.query_parameters),
endpoint_metadata: diffs_metadata_project_json_merge_request_path(@project, @merge_request, 'json', request.query_parameters),
endpoint_batch: diffs_batch_project_json_merge_request_path(@project, @merge_request, 'json', request.query_parameters),
help_page_path: suggest_changes_help_path, help_page_path: suggest_changes_help_path,
current_user_data: @current_user_data, current_user_data: @current_user_data,
project_path: project_path(@merge_request.project), project_path: project_path(@merge_request.project),
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.issues-filters{ class: ("w-100" if type == :boards_modal) } .issues-filters{ class: ("w-100" if type == :boards_modal) }
.issues-details-filters.filtered-search-block.d-flex.flex-column.flex-lg-row{ class: block_css_class, "v-pre" => type == :boards_modal } .issues-details-filters.filtered-search-block.d-flex.flex-column.flex-lg-row{ class: block_css_class, "v-pre" => type == :boards_modal }
.d-flex.flex-column.flex-md-row.flex-grow-1.mb-lg-0.mb-md-2.mb-sm-0 .d-flex.flex-column.flex-md-row.flex-grow-1.mb-lg-0.mb-md-2.mb-sm-0.w-100
- if type == :boards - if type == :boards
= render "shared/boards/switcher", board: board = render "shared/boards/switcher", board: board
= form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form w-100' do = form_tag page_filter_path, method: :get, class: 'filter-form js-filter-form w-100' do
...@@ -162,8 +162,8 @@ ...@@ -162,8 +162,8 @@
%button.clear-search.hidden{ type: 'button' } %button.clear-search.hidden{ type: 'button' }
= icon('times') = icon('times')
.filter-dropdown-container.d-flex.flex-column.flex-md-row .filter-dropdown-container.d-flex.flex-column.flex-md-row
#js-board-labels-toggle
- if type == :boards - if type == :boards
#js-board-labels-toggle
.js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } } .js-board-config{ data: { can_admin_list: user_can_admin_list, has_scope: board.scoped? } }
- if user_can_admin_list - if user_can_admin_list
= render 'shared/issuable/board_create_list_dropdown', board: board = render 'shared/issuable/board_create_list_dropdown', board: board
......
---
title: Add option to delete cached Kubernetes namespaces
merge_request: 20411
author:
type: added
---
title: Convert flash epic error to form validation error
merge_request: 20130
author:
type: changed
---
title: Fix tooltip hovers in environments table
merge_request: 20737
author:
type: fixed
...@@ -166,6 +166,7 @@ Rails.application.routes.draw do ...@@ -166,6 +166,7 @@ Rails.application.routes.draw do
end end
get :cluster_status, format: :json get :cluster_status, format: :json
delete :clear_cache
end end
end end
end end
......
...@@ -184,6 +184,17 @@ that the YAML parser knows to interpret the whole thing as a string rather than ...@@ -184,6 +184,17 @@ that the YAML parser knows to interpret the whole thing as a string rather than
a "key: value" pair. Be careful when using special characters: a "key: value" pair. Be careful when using special characters:
`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``. `:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``.
If any of the script commands return an exit code different from zero, the job
will fail and further commands will not be executed. This behavior can be avoided by
storing the exit code in a variable:
```yaml
job:
script:
- false && true; exit_code=$?
- if [ $exit_code -ne 0 ]; then echo "Previous command failed"; fi;
```
#### YAML anchors for `script` #### YAML anchors for `script`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/23005) in GitLab 12.5. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/23005) in GitLab 12.5.
......
...@@ -75,6 +75,21 @@ NOTE: **Note:** ...@@ -75,6 +75,21 @@ NOTE: **Note:**
If you [install applications](#installing-applications) on your cluster, GitLab will create If you [install applications](#installing-applications) on your cluster, GitLab will create
the resources required to run these even if you have chosen to manage your own cluster. the resources required to run these even if you have chosen to manage your own cluster.
### Clearing the cluster cache
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/31759) in GitLab 12.6.
If you choose to allow GitLab to manage your cluster for you, GitLab stores a cached
version of the namespaces and service accounts it creates for your projects. If you
modify these resources in your cluster manually, this cache can fall out of sync with
your cluster, which can cause deployment jobs to fail.
To clear the cache:
1. Navigate to your group’s **Kubernetes** page, and select your cluster.
1. Expand the **Advanced settings** section.
1. Click **Clear cluster cache**.
## Base domain ## Base domain
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24580) in GitLab 11.8. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24580) in GitLab 11.8.
......
...@@ -132,6 +132,21 @@ NOTE: **Note:** ...@@ -132,6 +132,21 @@ NOTE: **Note:**
If you [install applications](#installing-applications) on your cluster, GitLab will create If you [install applications](#installing-applications) on your cluster, GitLab will create
the resources required to run these even if you have chosen to manage your own cluster. the resources required to run these even if you have chosen to manage your own cluster.
#### Clearing the cluster cache
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/31759) in GitLab 12.6.
If you choose to allow GitLab to manage your cluster for you, GitLab stores a cached
version of the namespaces and service accounts it creates for your projects. If you
modify these resources in your cluster manually, this cache can fall out of sync with
your cluster, which can cause deployment jobs to fail.
To clear the cache:
1. Navigate to your project’s **Operations > Kubernetes** page, and select your cluster.
1. Expand the **Advanced settings** section.
1. Click **Clear cluster cache**.
### Base domain ### Base domain
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24580) in GitLab 11.8. > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24580) in GitLab 11.8.
......
...@@ -3469,6 +3469,9 @@ msgstr "" ...@@ -3469,6 +3469,9 @@ msgstr ""
msgid "Cluster Health" msgid "Cluster Health"
msgstr "" msgstr ""
msgid "Cluster cache cleared."
msgstr ""
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}." msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
msgstr "" msgstr ""
...@@ -3601,6 +3604,12 @@ msgstr "" ...@@ -3601,6 +3604,12 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster." msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "" msgstr ""
msgid "ClusterIntegration|Clear cluster cache"
msgstr ""
msgid "ClusterIntegration|Clear the local cache of namespace and service accounts. This is necessary if your integration has become out of sync. The cache is repopulated during the next CI job that requires namespace and service accounts."
msgstr ""
msgid "ClusterIntegration|Cloud Run" msgid "ClusterIntegration|Cloud Run"
msgstr "" msgstr ""
...@@ -6644,6 +6653,9 @@ msgstr "" ...@@ -6644,6 +6653,9 @@ msgstr ""
msgid "Epic" msgid "Epic"
msgstr "" msgstr ""
msgid "Epic cannot be found."
msgstr ""
msgid "Epic events" msgid "Epic events"
msgstr "" msgstr ""
...@@ -9611,6 +9623,9 @@ msgstr "" ...@@ -9611,6 +9623,9 @@ msgstr ""
msgid "Issue board focus mode" msgid "Issue board focus mode"
msgstr "" msgstr ""
msgid "Issue cannot be found."
msgstr ""
msgid "Issue events" msgid "Issue events"
msgstr "" msgstr ""
...@@ -17678,6 +17693,9 @@ msgstr "" ...@@ -17678,6 +17693,9 @@ msgstr ""
msgid "This environment has no deployments yet." msgid "This environment has no deployments yet."
msgstr "" msgstr ""
msgid "This epic already has the maximum number of child epics."
msgstr ""
msgid "This epic does not exist or you don't have sufficient permission." msgid "This epic does not exist or you don't have sufficient permission."
msgstr "" msgstr ""
...@@ -19487,12 +19505,6 @@ msgstr "" ...@@ -19487,12 +19505,6 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access." msgid "Want to see the data? Please ask an administrator for access."
msgstr "" msgstr ""
msgid "We can't find an epic that matches what you are looking for."
msgstr ""
msgid "We can't find an issue that matches what you are looking for."
msgstr ""
msgid "We could not determine the path to remove the epic" msgid "We could not determine the path to remove the epic"
msgstr "" msgstr ""
......
...@@ -448,6 +448,33 @@ describe Admin::ClustersController do ...@@ -448,6 +448,33 @@ describe Admin::ClustersController do
end end
end end
describe 'DELETE clear cluster cache' do
let(:cluster) { create(:cluster, :instance) }
let!(:kubernetes_namespace) do
create(:cluster_kubernetes_namespace,
cluster: cluster,
project: create(:project)
)
end
def go
delete :clear_cache, params: { id: cluster }
end
it 'deletes the namespaces associated with the cluster' do
expect { go }.to change { Clusters::KubernetesNamespace.count }
expect(response).to redirect_to(admin_cluster_path(cluster))
expect(cluster.kubernetes_namespaces).to be_empty
end
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
describe 'GET #cluster_status' do describe 'GET #cluster_status' do
let(:cluster) { create(:cluster, :providing_by_gcp, :instance) } let(:cluster) { create(:cluster, :providing_by_gcp, :instance) }
......
...@@ -516,6 +516,42 @@ describe Groups::ClustersController do ...@@ -516,6 +516,42 @@ describe Groups::ClustersController do
end end
end end
describe 'DELETE clear cluster cache' do
let(:cluster) { create(:cluster, :group, groups: [group]) }
let!(:kubernetes_namespace) do
create(:cluster_kubernetes_namespace,
cluster: cluster,
project: create(:project)
)
end
def go
delete :clear_cache,
params: {
group_id: group,
id: cluster
}
end
it 'deletes the namespaces associated with the cluster' do
expect { go }.to change { Clusters::KubernetesNamespace.count }
expect(response).to redirect_to(group_cluster_path(group, cluster))
expect(cluster.kubernetes_namespaces).to be_empty
end
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(group) }
it { expect { go }.to be_allowed_for(:maintainer).of(group) }
it { expect { go }.to be_denied_for(:developer).of(group) }
it { expect { go }.to be_denied_for(:reporter).of(group) }
it { expect { go }.to be_denied_for(:guest).of(group) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
describe 'GET cluster_status' do describe 'GET cluster_status' do
let(:cluster) { create(:cluster, :providing_by_gcp, cluster_type: :group_type, groups: [group]) } let(:cluster) { create(:cluster, :providing_by_gcp, cluster_type: :group_type, groups: [group]) }
......
...@@ -517,6 +517,38 @@ describe Projects::ClustersController do ...@@ -517,6 +517,38 @@ describe Projects::ClustersController do
end end
end end
describe 'DELETE clear cluster cache' do
let(:cluster) { create(:cluster, :project, projects: [project]) }
let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster) }
def go
delete :clear_cache,
params: {
namespace_id: project.namespace,
project_id: project,
id: cluster
}
end
it 'deletes the namespaces associated with the cluster' do
expect { go }.to change { Clusters::KubernetesNamespace.count }
expect(response).to redirect_to(project_cluster_path(project, cluster))
expect(cluster.kubernetes_namespaces).to be_empty
end
describe 'security' do
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:maintainer).of(project) }
it { expect { go }.to be_denied_for(:developer).of(project) }
it { expect { go }.to be_denied_for(:reporter).of(project) }
it { expect { go }.to be_denied_for(:guest).of(project) }
it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) }
end
end
describe 'GET cluster_status' do describe 'GET cluster_status' do
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
......
...@@ -8,6 +8,7 @@ describe 'User expands diff', :js do ...@@ -8,6 +8,7 @@ describe 'User expands diff', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes) allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes) allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
...@@ -20,7 +21,7 @@ describe 'User expands diff', :js do ...@@ -20,7 +21,7 @@ describe 'User expands diff', :js do
it_behaves_like 'rendering a single diff version' it_behaves_like 'rendering a single diff version'
it 'allows user to expand diff' do it 'allows user to expand diff' do
page.within find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd"]') do page.within find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9"]') do
click_link 'Click to expand it.' click_link 'Click to expand it.'
wait_for_requests wait_for_requests
......
...@@ -21,6 +21,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do ...@@ -21,6 +21,7 @@ describe 'Merge request > User resolves diff notes and threads', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
end end
it_behaves_like 'rendering a single diff version' it_behaves_like 'rendering a single diff version'
......
...@@ -22,6 +22,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do ...@@ -22,6 +22,7 @@ describe 'Merge request > User sees avatars on diff notes', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in user sign_in user
......
...@@ -11,6 +11,7 @@ describe 'Merge request > User sees diff', :js do ...@@ -11,6 +11,7 @@ describe 'Merge request > User sees diff', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
end end
it_behaves_like 'rendering a single diff version' it_behaves_like 'rendering a single diff version'
......
...@@ -17,6 +17,7 @@ describe 'Merge request > User sees versions', :js do ...@@ -17,6 +17,7 @@ describe 'Merge request > User sees versions', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
......
...@@ -10,6 +10,7 @@ describe 'User views diffs', :js do ...@@ -10,6 +10,7 @@ describe 'User views diffs', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
visit(diffs_project_merge_request_path(project, merge_request)) visit(diffs_project_merge_request_path(project, merge_request))
wait_for_requests wait_for_requests
......
...@@ -10,6 +10,7 @@ describe 'View on environment', :js do ...@@ -10,6 +10,7 @@ describe 'View on environment', :js do
before do before do
stub_feature_flags(single_mr_diff_view: false) stub_feature_flags(single_mr_diff_view: false)
stub_feature_flags(diffs_batch_load: false)
project.add_maintainer(user) project.add_maintainer(user)
end end
......
...@@ -34,6 +34,8 @@ describe('diffs/components/app', () => { ...@@ -34,6 +34,8 @@ describe('diffs/components/app', () => {
localVue, localVue,
propsData: { propsData: {
endpoint: `${TEST_HOST}/diff/endpoint`, endpoint: `${TEST_HOST}/diff/endpoint`,
endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
endpointBatch: `${TEST_HOST}/diff/endpointBatch`,
projectPath: 'namespace/project', projectPath: 'namespace/project',
currentUser: {}, currentUser: {},
changesEmptyStateIllustration: '', changesEmptyStateIllustration: '',
...@@ -42,6 +44,11 @@ describe('diffs/components/app', () => { ...@@ -42,6 +44,11 @@ describe('diffs/components/app', () => {
...props, ...props,
}, },
store, store,
methods: {
isLatestVersion() {
return true;
},
},
}); });
} }
...@@ -59,6 +66,58 @@ describe('diffs/components/app', () => { ...@@ -59,6 +66,58 @@ describe('diffs/components/app', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('fetch diff methods', () => {
beforeEach(() => {
spyOn(window, 'requestIdleCallback').and.callFake(fn => fn());
createComponent();
spyOn(wrapper.vm, 'fetchDiffFiles').and.callFake(() => Promise.resolve());
spyOn(wrapper.vm, 'fetchDiffFilesMeta').and.callFake(() => Promise.resolve());
spyOn(wrapper.vm, 'fetchDiffFilesBatch').and.callFake(() => Promise.resolve());
spyOn(wrapper.vm, 'setDiscussions');
spyOn(wrapper.vm, 'startRenderDiffsQueue');
});
it('calls fetchDiffFiles if diffsBatchLoad is not enabled', () => {
wrapper.vm.glFeatures.diffsBatchLoad = false;
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchDiffFiles).toHaveBeenCalled();
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.setDiscussions).toHaveBeenCalled();
expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesMeta).not.toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
});
});
it('calls fetchDiffFiles if diffsBatchLoad is enabled, and not latest version', () => {
wrapper.vm.glFeatures.diffsBatchLoad = true;
wrapper.vm.isLatestVersion = () => false;
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchDiffFiles).toHaveBeenCalled();
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.setDiscussions).toHaveBeenCalled();
expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesMeta).not.toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesBatch).not.toHaveBeenCalled();
});
});
it('calls batch methods if diffsBatchLoad is enabled, and latest version', () => {
wrapper.vm.glFeatures.diffsBatchLoad = true;
wrapper.vm.fetchData(false);
expect(wrapper.vm.fetchDiffFiles).not.toHaveBeenCalled();
wrapper.vm.$nextTick(() => {
expect(wrapper.vm.setDiscussions).toHaveBeenCalled();
expect(wrapper.vm.startRenderDiffsQueue).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesMeta).toHaveBeenCalled();
expect(wrapper.vm.fetchDiffFilesBatch).toHaveBeenCalled();
});
});
});
it('adds container-limiting classes when showFileTree is false with inline diffs', () => { it('adds container-limiting classes when showFileTree is false with inline diffs', () => {
createComponent({}, ({ state }) => { createComponent({}, ({ state }) => {
state.diffs.showTreeList = false; state.diffs.showTreeList = false;
...@@ -93,6 +152,14 @@ describe('diffs/components/app', () => { ...@@ -93,6 +152,14 @@ describe('diffs/components/app', () => {
expect(wrapper.contains(GlLoadingIcon)).toBe(true); expect(wrapper.contains(GlLoadingIcon)).toBe(true);
}); });
it('displays loading icon on batch loading', () => {
createComponent({}, ({ state }) => {
state.diffs.isBatchLoading = true;
});
expect(wrapper.contains(GlLoadingIcon)).toBe(true);
});
it('displays diffs container when not loading', () => { it('displays diffs container when not loading', () => {
createComponent(); createComponent();
......
...@@ -8,6 +8,8 @@ import { ...@@ -8,6 +8,8 @@ import {
import actions, { import actions, {
setBaseConfig, setBaseConfig,
fetchDiffFiles, fetchDiffFiles,
fetchDiffFilesBatch,
fetchDiffFilesMeta,
assignDiscussionsToDiff, assignDiscussionsToDiff,
removeDiscussionsFromDiff, removeDiscussionsFromDiff,
startRenderDiffsQueue, startRenderDiffsQueue,
...@@ -68,18 +70,41 @@ describe('DiffsStoreActions', () => { ...@@ -68,18 +70,41 @@ describe('DiffsStoreActions', () => {
describe('setBaseConfig', () => { describe('setBaseConfig', () => {
it('should set given endpoint and project path', done => { it('should set given endpoint and project path', done => {
const endpoint = '/diffs/set/endpoint'; const endpoint = '/diffs/set/endpoint';
const endpointMetadata = '/diffs/set/endpoint/metadata';
const endpointBatch = '/diffs/set/endpoint/batch';
const projectPath = '/root/project'; const projectPath = '/root/project';
const dismissEndpoint = '/-/user_callouts'; const dismissEndpoint = '/-/user_callouts';
const showSuggestPopover = false; const showSuggestPopover = false;
testAction( testAction(
setBaseConfig, setBaseConfig,
{ endpoint, projectPath, dismissEndpoint, showSuggestPopover }, {
{ endpoint: '', projectPath: '', dismissEndpoint: '', showSuggestPopover: true }, endpoint,
endpointBatch,
endpointMetadata,
projectPath,
dismissEndpoint,
showSuggestPopover,
},
{
endpoint: '',
endpointBatch: '',
endpointMetadata: '',
projectPath: '',
dismissEndpoint: '',
showSuggestPopover: true,
},
[ [
{ {
type: types.SET_BASE_CONFIG, type: types.SET_BASE_CONFIG,
payload: { endpoint, projectPath, dismissEndpoint, showSuggestPopover }, payload: {
endpoint,
endpointMetadata,
endpointBatch,
projectPath,
dismissEndpoint,
showSuggestPopover,
},
}, },
], ],
[], [],
...@@ -114,6 +139,64 @@ describe('DiffsStoreActions', () => { ...@@ -114,6 +139,64 @@ describe('DiffsStoreActions', () => {
}); });
}); });
describe('fetchDiffFilesBatch', () => {
it('should fetch batch diff files', done => {
const endpointBatch = '/fetch/diffs_batch';
const batch1 = `${endpointBatch}?per_page=10`;
const batch2 = `${endpointBatch}?per_page=10&page=2`;
const mock = new MockAdapter(axios);
const res1 = { diff_files: [], pagination: { next_page: 2 } };
const res2 = { diff_files: [], pagination: {} };
mock.onGet(batch1).reply(200, res1);
mock.onGet(batch2).reply(200, res2);
testAction(
fetchDiffFilesBatch,
{},
{ endpointBatch },
[
{ type: types.SET_BATCH_LOADING, payload: true },
{ type: types.SET_DIFF_DATA_BATCH, payload: { diff_files: res1.diff_files } },
{ type: types.SET_BATCH_LOADING, payload: false },
{ type: types.SET_DIFF_DATA_BATCH, payload: { diff_files: [] } },
{ type: types.SET_BATCH_LOADING, payload: false },
],
[],
() => {
mock.restore();
done();
},
);
});
});
describe('fetchDiffFilesMeta', () => {
it('should fetch diff meta information', done => {
const endpointMetadata = '/fetch/diffs_meta';
const mock = new MockAdapter(axios);
const data = { diff_files: [] };
const res = { data };
mock.onGet(endpointMetadata).reply(200, res);
testAction(
fetchDiffFilesMeta,
{},
{ endpointMetadata },
[
{ type: types.SET_LOADING, payload: true },
{ type: types.SET_LOADING, payload: false },
{ type: types.SET_MERGE_REQUEST_DIFFS, payload: [] },
{ type: types.SET_DIFF_DATA, payload: { data, diff_files: [] } },
],
[],
() => {
mock.restore();
done();
},
);
});
});
describe('setHighlightedRow', () => { describe('setHighlightedRow', () => {
it('should mark currently selected diff and set lineHash and fileHash of highlightedRow', () => { it('should mark currently selected diff and set lineHash and fileHash of highlightedRow', () => {
testAction(setHighlightedRow, 'ABC_123', {}, [ testAction(setHighlightedRow, 'ABC_123', {}, [
......
...@@ -28,6 +28,16 @@ describe('DiffsStoreMutations', () => { ...@@ -28,6 +28,16 @@ describe('DiffsStoreMutations', () => {
}); });
}); });
describe('SET_BATCH_LOADING', () => {
it('should set loading state', () => {
const state = {};
mutations[types.SET_BATCH_LOADING](state, false);
expect(state.isBatchLoading).toEqual(false);
});
});
describe('SET_DIFF_DATA', () => { describe('SET_DIFF_DATA', () => {
it('should set diff data type properly', () => { it('should set diff data type properly', () => {
const state = {}; const state = {};
...@@ -45,6 +55,23 @@ describe('DiffsStoreMutations', () => { ...@@ -45,6 +55,23 @@ describe('DiffsStoreMutations', () => {
}); });
}); });
describe('SET_DIFFSET_DIFF_DATA_BATCH_DATA', () => {
it('should set diff data batch type properly', () => {
const state = { diffFiles: [] };
const diffMock = {
diff_files: [diffFileMockData],
};
mutations[types.SET_DIFF_DATA_BATCH](state, diffMock);
const firstLine = state.diffFiles[0].parallel_diff_lines[0];
expect(firstLine.right.text).toBeUndefined();
expect(state.diffFiles[0].renderIt).toEqual(true);
expect(state.diffFiles[0].collapsed).toEqual(false);
});
});
describe('SET_DIFF_VIEW_TYPE', () => { describe('SET_DIFF_VIEW_TYPE', () => {
it('should set diff view type properly', () => { it('should set diff view type properly', () => {
const state = {}; const state = {};
......
...@@ -10,7 +10,6 @@ describe('Environment', () => { ...@@ -10,7 +10,6 @@ describe('Environment', () => {
endpoint: 'environments.json', endpoint: 'environments.json',
canCreateEnvironment: true, canCreateEnvironment: true,
canReadEnvironment: true, canReadEnvironment: true,
cssContainerClass: 'container',
newEnvironmentPath: 'environments/new', newEnvironmentPath: 'environments/new',
helpPagePath: 'help', helpPagePath: 'help',
canaryDeploymentFeatureId: 'canary_deployment', canaryDeploymentFeatureId: 'canary_deployment',
......
# frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Build::Context::Build do describe Gitlab::Ci::Build::Context::Build do
......
# frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Build::Context::Global do describe Gitlab::Ci::Build::Context::Global do
......
...@@ -960,4 +960,20 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do ...@@ -960,4 +960,20 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end end
end end
end end
describe '#delete_cached_resources!' do
let!(:cluster) { create(:cluster, :project) }
let!(:staging_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, namespace: 'staging') }
let!(:production_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, namespace: 'production') }
subject { cluster.delete_cached_resources! }
it 'deletes associated namespace records' do
expect(cluster.kubernetes_namespaces).to match_array([staging_namespace, production_namespace])
subject
expect(cluster.kubernetes_namespaces).to be_empty
end
end
end end
...@@ -83,6 +83,12 @@ describe GroupClusterablePresenter do ...@@ -83,6 +83,12 @@ describe GroupClusterablePresenter do
it { is_expected.to eq(update_applications_group_cluster_path(group, cluster, application)) } it { is_expected.to eq(update_applications_group_cluster_path(group, cluster, application)) }
end end
describe '#clear_cluster_cache_path' do
subject { presenter.clear_cluster_cache_path(cluster) }
it { is_expected.to eq(clear_cache_group_cluster_path(group, cluster)) }
end
describe '#cluster_path' do describe '#cluster_path' do
subject { presenter.cluster_path(cluster) } subject { presenter.cluster_path(cluster) }
......
...@@ -34,4 +34,10 @@ describe InstanceClusterablePresenter do ...@@ -34,4 +34,10 @@ describe InstanceClusterablePresenter do
it { is_expected.to eq(aws_proxy_admin_clusters_path(resource: resource)) } it { is_expected.to eq(aws_proxy_admin_clusters_path(resource: resource)) }
end end
describe '#clear_cluster_cache_path' do
subject { presenter.clear_cluster_cache_path(cluster) }
it { is_expected.to eq(clear_cache_admin_cluster_path(cluster)) }
end
end end
...@@ -83,6 +83,12 @@ describe ProjectClusterablePresenter do ...@@ -83,6 +83,12 @@ describe ProjectClusterablePresenter do
it { is_expected.to eq(update_applications_project_cluster_path(project, cluster, application)) } it { is_expected.to eq(update_applications_project_cluster_path(project, cluster, application)) }
end end
describe '#clear_cluster_cache_path' do
subject { presenter.clear_cluster_cache_path(cluster) }
it { is_expected.to eq(clear_cache_project_cluster_path(project, cluster)) }
end
describe '#cluster_path' do describe '#cluster_path' do
subject { presenter.cluster_path(cluster) } subject { presenter.cluster_path(cluster) }
......
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
# This pending test can be removed when `single_mr_diff_view` is enabled by default # This pending test can be removed when `single_mr_diff_view` is enabled by default
# disabling the feature flag above is then not needed anymore. # disabling the feature flag above is then not needed anymore.
RSpec.shared_examples 'rendering a single diff version' do |attribute| RSpec.shared_examples 'rendering a single diff version' do |attribute|
before do
stub_feature_flags(diffs_batch_load: false)
end
pending 'allows editing diff settings single_mr_diff_view is enabled' do pending 'allows editing diff settings single_mr_diff_view is enabled' do
project = create(:project, :repository) project = create(:project, :repository)
user = project.creator user = project.creator
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment