Commit 866ca4e4 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 26a50872
...@@ -203,6 +203,8 @@ ...@@ -203,6 +203,8 @@
- name: postgres:9.6 - name: postgres:9.6
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg10: .use-pg10:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
...@@ -210,6 +212,8 @@ ...@@ -210,6 +212,8 @@
- name: postgres:10.9 - name: postgres:10.9
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg9-ee: .use-pg9-ee:
services: services:
...@@ -217,6 +221,8 @@ ...@@ -217,6 +221,8 @@
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
- name: elasticsearch:6.4.2 - name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.use-pg10-ee: .use-pg10-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.5-golang-1.12-git-2.24-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34"
...@@ -225,6 +231,8 @@ ...@@ -225,6 +231,8 @@
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
- name: redis:alpine - name: redis:alpine
- name: elasticsearch:6.4.2 - name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
.only-ee: .only-ee:
only: only:
......
...@@ -6,7 +6,7 @@ import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils ...@@ -6,7 +6,7 @@ import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils
import trackDashboardLoad from '../monitoring_tracking_helper'; import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql'; import getEnvironments from '../queries/getEnvironments.query.graphql';
import statusCodes from '../../lib/utils/http_status'; import statusCodes from '../../lib/utils/http_status';
import { backOff } from '../../lib/utils/common_utils'; import { backOff, convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
import { PROMETHEUS_TIMEOUT } from '../constants'; import { PROMETHEUS_TIMEOUT } from '../constants';
...@@ -52,6 +52,8 @@ export const requestMetricsDashboard = ({ commit }) => { ...@@ -52,6 +52,8 @@ export const requestMetricsDashboard = ({ commit }) => {
export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => { export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => {
commit(types.SET_ALL_DASHBOARDS, response.all_dashboards); commit(types.SET_ALL_DASHBOARDS, response.all_dashboards);
commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard); commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard);
commit(types.SET_ENDPOINTS, convertObjectPropsToCamelCase(response.metrics_data));
return dispatch('fetchPrometheusMetrics', params); return dispatch('fetchPrometheusMetrics', params);
}; };
export const receiveMetricsDashboardFailure = ({ commit }, error) => { export const receiveMetricsDashboardFailure = ({ commit }, error) => {
......
import Vue from 'vue'; import Vue from 'vue';
import pick from 'lodash/pick';
import { slugify } from '~/lib/utils/text_utility'; import { slugify } from '~/lib/utils/text_utility';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { normalizeMetric, normalizeQueryResult } from './utils'; import { normalizeMetric, normalizeQueryResult } from './utils';
...@@ -174,15 +175,19 @@ export default { ...@@ -174,15 +175,19 @@ export default {
state: emptyStateFromError(error), state: emptyStateFromError(error),
}); });
}, },
[types.SET_ENDPOINTS](state, endpoints = {}) {
[types.SET_ENDPOINTS](state, endpoints) { const endpointKeys = [
state.metricsEndpoint = endpoints.metricsEndpoint; 'metricsEndpoint',
state.deploymentsEndpoint = endpoints.deploymentsEndpoint; 'deploymentsEndpoint',
state.dashboardEndpoint = endpoints.dashboardEndpoint; 'dashboardEndpoint',
state.dashboardsEndpoint = endpoints.dashboardsEndpoint; 'dashboardsEndpoint',
state.currentDashboard = endpoints.currentDashboard; 'currentDashboard',
state.projectPath = endpoints.projectPath; 'projectPath',
state.logsPath = endpoints.logsPath || state.logsPath; 'logsPath',
];
Object.entries(pick(endpoints, endpointKeys)).forEach(([key, value]) => {
state[key] = value;
});
}, },
[types.SET_TIME_RANGE](state, timeRange) { [types.SET_TIME_RANGE](state, timeRange) {
state.timeRange = timeRange; state.timeRange = timeRange;
......
import ZenMode from '~/zen_mode'; import ZenMode from '~/zen_mode';
import initEditRelease from '~/releases/detail'; import initEditRelease from '~/releases/mount_edit';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new ZenMode(); // eslint-disable-line no-new new ZenMode(); // eslint-disable-line no-new
......
import initReleases from '~/releases/list'; import initReleases from '~/releases/mount_index';
document.addEventListener('DOMContentLoaded', initReleases); document.addEventListener('DOMContentLoaded', initReleases);
...@@ -7,7 +7,7 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue'; ...@@ -7,7 +7,7 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
export default { export default {
name: 'ReleaseDetailApp', name: 'ReleaseEditApp',
components: { components: {
GlFormInput, GlFormInput,
GlFormGroup, GlFormGroup,
...@@ -18,7 +18,7 @@ export default { ...@@ -18,7 +18,7 @@ export default {
autofocusonshow, autofocusonshow,
}, },
computed: { computed: {
...mapState([ ...mapState('detail', [
'isFetchingRelease', 'isFetchingRelease',
'fetchError', 'fetchError',
'markdownDocsPath', 'markdownDocsPath',
...@@ -42,7 +42,7 @@ export default { ...@@ -42,7 +42,7 @@ export default {
); );
}, },
tagName() { tagName() {
return this.$store.state.release.tagName; return this.$store.state.detail.release.tagName;
}, },
tagNameHintText() { tagNameHintText() {
return sprintf( return sprintf(
...@@ -60,7 +60,7 @@ export default { ...@@ -60,7 +60,7 @@ export default {
}, },
releaseTitle: { releaseTitle: {
get() { get() {
return this.$store.state.release.name; return this.$store.state.detail.release.name;
}, },
set(title) { set(title) {
this.updateReleaseTitle(title); this.updateReleaseTitle(title);
...@@ -68,7 +68,7 @@ export default { ...@@ -68,7 +68,7 @@ export default {
}, },
releaseNotes: { releaseNotes: {
get() { get() {
return this.$store.state.release.description; return this.$store.state.detail.release.description;
}, },
set(notes) { set(notes) {
this.updateReleaseNotes(notes); this.updateReleaseNotes(notes);
...@@ -79,7 +79,7 @@ export default { ...@@ -79,7 +79,7 @@ export default {
this.fetchRelease(); this.fetchRelease();
}, },
methods: { methods: {
...mapActions([ ...mapActions('detail', [
'fetchRelease', 'fetchRelease',
'updateRelease', 'updateRelease',
'updateReleaseTitle', 'updateReleaseTitle',
......
...@@ -32,7 +32,7 @@ export default { ...@@ -32,7 +32,7 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(['isLoading', 'releases', 'hasError', 'pageInfo']), ...mapState('list', ['isLoading', 'releases', 'hasError', 'pageInfo']),
shouldRenderEmptyState() { shouldRenderEmptyState() {
return !this.releases.length && !this.hasError && !this.isLoading; return !this.releases.length && !this.hasError && !this.isLoading;
}, },
...@@ -47,7 +47,7 @@ export default { ...@@ -47,7 +47,7 @@ export default {
}); });
}, },
methods: { methods: {
...mapActions(['fetchReleases']), ...mapActions('list', ['fetchReleases']),
onChangePage(page) { onChangePage(page) {
historyPushState(buildUrlWithCurrentLocation(`?page=${page}`)); historyPushState(buildUrlWithCurrentLocation(`?page=${page}`));
this.fetchReleases({ page, projectId: this.projectId }); this.fetchReleases({ page, projectId: this.projectId });
......
import Vue from 'vue';
import App from './components/app.vue';
import createStore from './store';
export default () => {
const element = document.getElementById('js-releases-page');
return new Vue({
el: element,
store: createStore(),
components: {
App,
},
render(createElement) {
return createElement('app', {
props: {
projectId: element.dataset.projectId,
documentationLink: element.dataset.documentationPath,
illustrationPath: element.dataset.illustrationPath,
},
});
},
});
};
import Vue from 'vue'; import Vue from 'vue';
import ReleaseDetailApp from './components/app.vue'; import ReleaseEditApp from './components/app_edit.vue';
import createStore from './store'; import createStore from './stores';
import detailModule from './stores/modules/detail';
export default () => { export default () => {
const el = document.getElementById('js-edit-release-page'); const el = document.getElementById('js-edit-release-page');
const store = createStore(); const store = createStore({ detail: detailModule });
store.dispatch('setInitialState', el.dataset); store.dispatch('setInitialState', el.dataset);
return new Vue({ return new Vue({
el, el,
store, store,
components: { ReleaseDetailApp }, render: h => h(ReleaseEditApp),
render(createElement) {
return createElement('release-detail-app');
},
}); });
}; };
import Vue from 'vue';
import ReleaseListApp from './components/app_index.vue';
import createStore from './stores';
import listModule from './stores/modules/list';
export default () => {
const el = document.getElementById('js-releases-page');
return new Vue({
el,
store: createStore({ list: listModule }),
render: h =>
h(ReleaseListApp, {
props: {
projectId: el.dataset.projectId,
documentationLink: el.dataset.documentationPath,
illustrationPath: el.dataset.illustrationPath,
},
}),
});
};
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default modules => new Vuex.Store({ modules });
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions'; import * as actions from './actions';
import mutations from './mutations'; import mutations from './mutations';
import state from './state'; import state from './state';
Vue.use(Vuex); export default {
namespaced: true,
export default () => actions,
new Vuex.Store({ mutations,
actions, state,
mutations, };
state,
});
import Vue from 'vue';
import Vuex from 'vuex';
import state from './state'; import state from './state';
import * as actions from './actions'; import * as actions from './actions';
import mutations from './mutations'; import mutations from './mutations';
Vue.use(Vuex); export default {
namespaced: true,
export default () => actions,
new Vuex.Store({ mutations,
actions, state,
mutations, };
state: state(),
});
...@@ -56,7 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -56,7 +56,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end end
def clear_repository_check_states def clear_repository_check_states
RepositoryCheck::ClearWorker.perform_async RepositoryCheck::ClearWorker.perform_async # rubocop:disable CodeReuse/Worker
redirect_to( redirect_to(
general_admin_application_settings_path, general_admin_application_settings_path,
...@@ -73,7 +73,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -73,7 +73,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
# Specs are in spec/requests/self_monitoring_project_spec.rb # Specs are in spec/requests/self_monitoring_project_spec.rb
def create_self_monitoring_project def create_self_monitoring_project
job_id = SelfMonitoringProjectCreateWorker.perform_async job_id = SelfMonitoringProjectCreateWorker.perform_async # rubocop:disable CodeReuse/Worker
render status: :accepted, json: { render status: :accepted, json: {
job_id: job_id, job_id: job_id,
...@@ -92,7 +92,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -92,7 +92,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
} }
end end
if SelfMonitoringProjectCreateWorker.in_progress?(job_id) if SelfMonitoringProjectCreateWorker.in_progress?(job_id) # rubocop:disable CodeReuse/Worker
::Gitlab::PollingInterval.set_header(response, interval: 3_000) ::Gitlab::PollingInterval.set_header(response, interval: 3_000)
return render status: :accepted, json: { return render status: :accepted, json: {
...@@ -112,7 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -112,7 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
# Specs are in spec/requests/self_monitoring_project_spec.rb # Specs are in spec/requests/self_monitoring_project_spec.rb
def delete_self_monitoring_project def delete_self_monitoring_project
job_id = SelfMonitoringProjectDeleteWorker.perform_async job_id = SelfMonitoringProjectDeleteWorker.perform_async # rubocop:disable CodeReuse/Worker
render status: :accepted, json: { render status: :accepted, json: {
job_id: job_id, job_id: job_id,
...@@ -131,7 +131,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -131,7 +131,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
} }
end end
if SelfMonitoringProjectDeleteWorker.in_progress?(job_id) if SelfMonitoringProjectDeleteWorker.in_progress?(job_id) # rubocop:disable CodeReuse/Worker
::Gitlab::PollingInterval.set_header(response, interval: 3_000) ::Gitlab::PollingInterval.set_header(response, interval: 3_000)
return render status: :accepted, json: { return render status: :accepted, json: {
......
...@@ -55,7 +55,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -55,7 +55,7 @@ class Admin::ProjectsController < Admin::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def repository_check def repository_check
RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) # rubocop:disable CodeReuse/Worker
redirect_to( redirect_to(
admin_project_path(@project), admin_project_path(@project),
......
...@@ -19,7 +19,7 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -19,7 +19,7 @@ class Admin::ServicesController < Admin::ApplicationController
def update def update
if service.update(service_params[:service]) if service.update(service_params[:service])
PropagateServiceTemplateWorker.perform_async(service.id) if service.active? PropagateServiceTemplateWorker.perform_async(service.id) if service.active? # rubocop:disable CodeReuse/Worker
redirect_to admin_application_settings_services_path, redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully' notice: 'Application settings saved successfully'
......
...@@ -188,7 +188,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -188,7 +188,7 @@ class Projects::IssuesController < Projects::ApplicationController
def import_csv def import_csv
if uploader = UploadService.new(project, params[:file]).execute if uploader = UploadService.new(project, params[:file]).execute
ImportIssuesCsvWorker.perform_async(current_user.id, project.id, uploader.upload.id) ImportIssuesCsvWorker.perform_async(current_user.id, project.id, uploader.upload.id) # rubocop:disable CodeReuse/Worker
flash[:notice] = _("Your issues are being imported. Once finished, you'll get a confirmation email.") flash[:notice] = _("Your issues are being imported. Once finished, you'll get a confirmation email.")
else else
......
...@@ -47,7 +47,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController ...@@ -47,7 +47,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
end end
def play def play
job_id = RunPipelineScheduleWorker.perform_async(schedule.id, current_user.id) job_id = RunPipelineScheduleWorker.perform_async(schedule.id, current_user.id) # rubocop:disable CodeReuse/Worker
if job_id if job_id
pipelines_link_start = "<a href=\"#{project_pipelines_path(@project)}\">" pipelines_link_start = "<a href=\"#{project_pipelines_path(@project)}\">"
......
...@@ -32,7 +32,7 @@ module Projects ...@@ -32,7 +32,7 @@ module Projects
end end
def destroy def destroy
DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker
track_event(:delete_repository) track_event(:delete_repository)
respond_to do |format| respond_to do |format|
......
...@@ -69,7 +69,9 @@ module Projects ...@@ -69,7 +69,9 @@ module Projects
return return
end end
# rubocop:disable CodeReuse/Worker
CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false) CreatePipelineWorker.perform_async(project.id, current_user.id, project.default_branch, :web, ignore_skip_ci: true, save_on_errors: false)
# rubocop:enable CodeReuse/Worker
pipelines_link_start = '<a href="%{url}">'.html_safe % { url: project_pipelines_path(@project) } pipelines_link_start = '<a href="%{url}">'.html_safe % { url: project_pipelines_path(@project) }
flash[:toast] = _("A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details") % { pipelines_link_start: pipelines_link_start, pipelines_link_end: "</a>".html_safe } flash[:toast] = _("A new Auto DevOps pipeline has been created, go to %{pipelines_link_start}Pipelines page%{pipelines_link_end} for details") % { pipelines_link_start: pipelines_link_start, pipelines_link_end: "</a>".html_safe }
......
...@@ -25,7 +25,7 @@ module Projects ...@@ -25,7 +25,7 @@ module Projects
result = Projects::UpdateService.new(project, current_user, cleanup_params).execute result = Projects::UpdateService.new(project, current_user, cleanup_params).execute
if result[:status] == :success if result[:status] == :success
RepositoryCleanupWorker.perform_async(project.id, current_user.id) RepositoryCleanupWorker.perform_async(project.id, current_user.id) # rubocop:disable CodeReuse/Worker
flash[:notice] = _('Repository cleanup has started. You will receive an email once the cleanup operation is complete.') flash[:notice] = _('Repository cleanup has started. You will receive an email once the cleanup operation is complete.')
else else
flash[:alert] = _('Failed to upload object map file') flash[:alert] = _('Failed to upload object map file')
......
...@@ -80,7 +80,7 @@ module Repositories ...@@ -80,7 +80,7 @@ module Repositories
return unless repo_type.project? return unless repo_type.project?
return unless project&.daily_statistics_enabled? return unless project&.daily_statistics_enabled?
ProjectDailyStatisticsWorker.perform_async(project.id) ProjectDailyStatisticsWorker.perform_async(project.id) # rubocop:disable CodeReuse/Worker
end end
def access def access
......
...@@ -31,7 +31,7 @@ module AnalyticsNavbarHelper ...@@ -31,7 +31,7 @@ module AnalyticsNavbarHelper
end end
def cycle_analytics_navbar_link(project, current_user) def cycle_analytics_navbar_link(project, current_user)
return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project) return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return unless project_nav_tab?(:cycle_analytics) return unless project_nav_tab?(:cycle_analytics)
navbar_sub_item( navbar_sub_item(
...@@ -43,7 +43,7 @@ module AnalyticsNavbarHelper ...@@ -43,7 +43,7 @@ module AnalyticsNavbarHelper
end end
def repository_analytics_navbar_link(project, current_user) def repository_analytics_navbar_link(project, current_user)
return if Feature.disabled?(:analytics_pages_under_project_analytics_sidebar, project) return if Feature.disabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return if project.empty_repo? return if project.empty_repo?
navbar_sub_item( navbar_sub_item(
...@@ -55,7 +55,7 @@ module AnalyticsNavbarHelper ...@@ -55,7 +55,7 @@ module AnalyticsNavbarHelper
end end
def ci_cd_analytics_navbar_link(project, current_user) def ci_cd_analytics_navbar_link(project, current_user)
return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project) return unless Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, project, default_enabled: true)
return unless project_nav_tab?(:pipelines) return unless project_nav_tab?(:pipelines)
return unless project.feature_available?(:builds, current_user) || !project.empty_repo? return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module Ci module Ci
class ProcessBuildService < BaseService class ProcessBuildService < BaseService
def execute(build, current_status) def execute(build, current_status)
if valid_statuses_for_when(build.when).include?(current_status) if valid_statuses_for_build(build).include?(current_status)
if build.schedulable? if build.schedulable?
build.schedule build.schedule
elsif build.action? elsif build.action?
...@@ -25,10 +25,10 @@ module Ci ...@@ -25,10 +25,10 @@ module Ci
build.enqueue build.enqueue
end end
def valid_statuses_for_when(value) def valid_statuses_for_build(build)
case value case build.when
when 'on_success' when 'on_success'
%w[success skipped] build.scheduling_type_dag? ? %w[success] : %w[success skipped]
when 'on_failure' when 'on_failure'
%w[failed] %w[failed]
when 'always' when 'always'
......
...@@ -6,10 +6,10 @@ ...@@ -6,10 +6,10 @@
= link_to navbar_links.first.link do = link_to navbar_links.first.link do
.nav-icon-container .nav-icon-container
= sprite_icon('chart') = sprite_icon('chart')
%span.nav-item-name %span.nav-item-name{ data: { qa_selector: 'analytics_link' } }
= _('Analytics') = _('Analytics')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items{ data: { qa_selector: 'analytics_sidebar_submenu' } }
- navbar_links.each do |menu_item| - navbar_links.each do |menu_item|
= nav_link(path: menu_item.path) do = nav_link(path: menu_item.path) do
= link_to(menu_item.link, menu_item.link_to_options) do = link_to(menu_item.link, menu_item.link_to_options) do
......
- should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, @group) - should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_group_analytics_sidebar, @group, default_enabled: true)
- issues_count = group_issues_count(state: 'opened') - issues_count = group_issues_count(state: 'opened')
- merge_requests_count = group_merge_requests_count(state: 'opened') - merge_requests_count = group_merge_requests_count(state: 'opened')
......
- should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, @project) - should_display_analytics_pages_in_sidebar = Feature.enabled?(:analytics_pages_under_project_analytics_sidebar, @project, default_enabled: true)
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) } .nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll .nav-sidebar-inner-scroll
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile', width: 40, height: 40) = project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile', width: 40, height: 40)
.sidebar-context-title .sidebar-context-title
= @project.name = @project.name
%ul.sidebar-top-level-items %ul.sidebar-top-level-items.qa-project-sidebar
- paths = sidebar_projects_paths - paths = sidebar_projects_paths
- paths << 'cycle_analytics#show' unless should_display_analytics_pages_in_sidebar - paths << 'cycle_analytics#show' unless should_display_analytics_pages_in_sidebar
= nav_link(path: paths, html_options: { class: 'home' }) do = nav_link(path: paths, html_options: { class: 'home' }) do
......
---
title: Prevent DAG builds to run after skipped need build
merge_request: 23405
author:
type: fixed
---
title: Backfill LfsObjectsProject records of forks
merge_request: 24767
author:
type: other
---
title: Fix Group Import existing objects lookup when description attribute is an empty
string
merge_request: 25187
author:
type: fixed
---
title: Update cluster-applications to v0.8.0
merge_request: 25138
author:
type: changed
---
title: Move analytics pages under the sidebar for projects and groups
merge_request: 24470
author:
type: changed
# frozen_string_literal: true
class ScheduleLinkLfsObjects < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'LinkLfsObjects'
BATCH_SIZE = 1_000
disable_ddl_transaction!
class Project < ActiveRecord::Base
include EachBatch
self.table_name = 'projects'
end
def up
fork_network_members =
Gitlab::BackgroundMigration::LinkLfsObjects::ForkNetworkMember
.select(1)
.with_non_existing_lfs_objects
.where('fork_network_members.project_id = projects.id')
forks = Project.where('EXISTS (?)', fork_network_members)
queue_background_migration_jobs_by_range_at_intervals(
forks,
MIGRATION,
BackgroundMigrationWorker.minimum_interval,
batch_size: BATCH_SIZE
)
end
def down
# no-op
end
end
...@@ -16,7 +16,7 @@ group: ...@@ -16,7 +16,7 @@ group:
- Merge requests - Merge requests
- Push events - Push events
To view the Contribution Analytics, go to your group's **Overview > Contribution Analytics** To view the Contribution Analytics, go to your group's **Analytics > Contribution Analytics**
page. page.
## Use cases ## Use cases
......
...@@ -14,10 +14,10 @@ requests to be merged and much more. ...@@ -14,10 +14,10 @@ requests to be merged and much more.
## View your group's Insights ## View your group's Insights
You can access your group's Insights by clicking the **Overview > Insights** You can access your group's Insights by clicking the **Analytics > Insights**
link in the left sidebar: link in the left sidebar:
![Insights sidebar link](img/insights_sidebar_link.png) ![Insights sidebar link](img/insights_sidebar_link_v12_8.png)
## Configure your Insights ## Configure your Insights
......
...@@ -10,7 +10,7 @@ Issues Analytics is a bar graph which illustrates the number of issues created e ...@@ -10,7 +10,7 @@ Issues Analytics is a bar graph which illustrates the number of issues created e
The default timespan is 13 months, which includes the current month, and the 12 months The default timespan is 13 months, which includes the current month, and the 12 months
prior. prior.
To access the chart, navigate to a group's sidebar and select **Issues > Analytics**. To access the chart, navigate to a group's sidebar and select **Analytics > Issues Analytics**.
Hover over each bar to see the total number of issues. Hover over each bar to see the total number of issues.
...@@ -28,7 +28,7 @@ You can change the total number of months displayed by setting a URL parameter. ...@@ -28,7 +28,7 @@ You can change the total number of months displayed by setting a URL parameter.
For example, `https://gitlab.com/groups/gitlab-org/-/issues_analytics?months_back=15` For example, `https://gitlab.com/groups/gitlab-org/-/issues_analytics?months_back=15`
shows a total of 15 months for the chart in the GitLab.org group. shows a total of 15 months for the chart in the GitLab.org group.
![Issues created per month](img/issues_created_per_month.png) ![Issues created per month](img/issues_created_per_month_v12_8.png)
<!-- ## Troubleshooting <!-- ## Troubleshooting
......
...@@ -13,10 +13,10 @@ This feature is [also available at the group level](../../group/insights/index.m ...@@ -13,10 +13,10 @@ This feature is [also available at the group level](../../group/insights/index.m
## View your project's Insights ## View your project's Insights
You can access your project's Insights by clicking the **Project > Insights** You can access your project's Insights by clicking the **Analytics > Insights**
link in the left sidebar: link in the left sidebar:
![Insights sidebar link](img/insights_sidebar_link.png) ![Insights sidebar link](img/insights_sidebar_link_v12_8.png)
## Configure your Insights ## Configure your Insights
......
...@@ -285,7 +285,9 @@ Note the following properties: ...@@ -285,7 +285,9 @@ Note the following properties:
| type | string | no | Type of panel to be rendered. Optional for area panel types | | type | string | no | Type of panel to be rendered. Optional for area panel types |
| query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) | | query_range | string | required | For area panel types, you must use a [range query](https://prometheus.io/docs/prometheus/latest/querying/api/#range-queries) |
![area panel type](img/prometheus_dashboard_area_panel_type.png) ![area panel chart](img/prometheus_dashboard_area_panel_type_v12_8.png)
Starting in [version 12.8](https://gitlab.com/gitlab-org/gitlab/issues/202696), the y-axis values will automatically scale according to the data. Previously, it always started from 0.
##### Anomaly chart ##### Anomaly chart
...@@ -329,7 +331,7 @@ Note the following properties: ...@@ -329,7 +331,7 @@ Note the following properties:
![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png) ![anomaly panel type](img/prometheus_dashboard_anomaly_panel_type.png)
#### Column ##### Column chart
To add a column panel type to a dashboard, look at the following sample dashboard file: To add a column panel type to a dashboard, look at the following sample dashboard file:
......
...@@ -52,7 +52,7 @@ relatively quickly to work, and they will take you to another page in the projec ...@@ -52,7 +52,7 @@ relatively quickly to work, and they will take you to another page in the projec
| <kbd>t</kbd> | Go to the project file search page. (**Repository > Files**, click **Find Files**). | | <kbd>t</kbd> | Go to the project file search page. (**Repository > Files**, click **Find Files**). |
| <kbd>g</kbd> + <kbd>c</kbd> | Go to the project commits list (**Repository > Commits**). | | <kbd>g</kbd> + <kbd>c</kbd> | Go to the project commits list (**Repository > Commits**). |
| <kbd>g</kbd> + <kbd>n</kbd> | Go to the [repository graph](#repository-graph) page (**Repository > Graph**). | | <kbd>g</kbd> + <kbd>n</kbd> | Go to the [repository graph](#repository-graph) page (**Repository > Graph**). |
| <kbd>g</kbd> + <kbd>d</kbd> | Go to repository charts (**Repository > Charts**). | | <kbd>g</kbd> + <kbd>d</kbd> | Go to repository charts (**Analytics > Repository Analytics**). |
| <kbd>g</kbd> + <kbd>i</kbd> | Go to the project issues list (**Issues > List**). | | <kbd>g</kbd> + <kbd>i</kbd> | Go to the project issues list (**Issues > List**). |
| <kbd>i</kbd> | Go to the New Issue page (**Issues**, click **New Issue** ). | | <kbd>i</kbd> | Go to the New Issue page (**Issues**, click **New Issue** ). |
| <kbd>g</kbd> + <kbd>b</kbd> | Go to the project issue boards list (**Issues > Boards**). | | <kbd>g</kbd> + <kbd>b</kbd> | Go to the project issue boards list (**Issues > Boards**). |
......
...@@ -27,7 +27,7 @@ module API ...@@ -27,7 +27,7 @@ module API
detail 'This feature was introduced in GitLab 12.5.' detail 'This feature was introduced in GitLab 12.5.'
end end
post ':id/export' do post ':id/export' do
GroupExportWorker.perform_async(current_user.id, user_group.id, params) GroupExportWorker.perform_async(current_user.id, user_group.id, params) # rubocop:disable CodeReuse/Worker
accepted! accepted!
end end
......
...@@ -76,7 +76,7 @@ module API ...@@ -76,7 +76,7 @@ module API
group = ::Groups::CreateService.new(current_user, group_params).execute group = ::Groups::CreateService.new(current_user, group_params).execute
if group.persisted? if group.persisted?
GroupImportWorker.perform_async(current_user.id, group.id) GroupImportWorker.perform_async(current_user.id, group.id) # rubocop:disable CodeReuse/Worker
accepted! accepted!
else else
......
...@@ -120,7 +120,7 @@ module API ...@@ -120,7 +120,7 @@ module API
post ':id/pipeline_schedules/:pipeline_schedule_id/play' do post ':id/pipeline_schedules/:pipeline_schedule_id/play' do
authorize! :play_pipeline_schedule, pipeline_schedule authorize! :play_pipeline_schedule, pipeline_schedule
job_id = RunPipelineScheduleWorker job_id = RunPipelineScheduleWorker # rubocop:disable CodeReuse/Worker
.perform_async(pipeline_schedule.id, current_user.id) .perform_async(pipeline_schedule.id, current_user.id)
if job_id if job_id
......
...@@ -41,7 +41,7 @@ module API ...@@ -41,7 +41,7 @@ module API
delete ':id/registry/repositories/:repository_id', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do delete ':id/registry/repositories/:repository_id', requirements: REPOSITORY_ENDPOINT_REQUIREMENTS do
authorize_admin_container_image! authorize_admin_container_image!
DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker
track_event('delete_repository') track_event('delete_repository')
status :accepted status :accepted
...@@ -79,8 +79,10 @@ module API ...@@ -79,8 +79,10 @@ module API
message = 'This request has already been made. You can run this at most once an hour for a given container repository' message = 'This request has already been made. You can run this at most once an hour for a given container repository'
render_api_error!(message, 400) unless obtain_new_cleanup_container_lease render_api_error!(message, 400) unless obtain_new_cleanup_container_lease
# rubocop:disable CodeReuse/Worker
CleanupContainerRepositoryWorker.perform_async(current_user.id, repository.id, CleanupContainerRepositoryWorker.perform_async(current_user.id, repository.id,
declared_params.except(:repository_id).merge(container_expiration_policy: false)) declared_params.except(:repository_id).merge(container_expiration_policy: false))
# rubocop:enable CodeReuse/Worker
track_event('delete_tag_bulk') track_event('delete_tag_bulk')
......
...@@ -170,9 +170,9 @@ module API ...@@ -170,9 +170,9 @@ module API
return if release.historical_release? return if release.historical_release?
if release.upcoming_release? if release.upcoming_release?
CreateEvidenceWorker.perform_at(release.released_at, release.id) CreateEvidenceWorker.perform_at(release.released_at, release.id) # rubocop:disable CodeReuse/Worker
else else
CreateEvidenceWorker.perform_async(release.id) CreateEvidenceWorker.perform_async(release.id) # rubocop:disable CodeReuse/Worker
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Create missing LfsObjectsProject records for forks
class LinkLfsObjects
# Model definition used for migration
class ForkNetworkMember < ActiveRecord::Base
self.table_name = 'fork_network_members'
def self.with_non_existing_lfs_objects
joins('JOIN lfs_objects_projects lop ON fork_network_members.forked_from_project_id = lop.project_id')
.where(
<<~SQL
NOT EXISTS (
SELECT 1
FROM lfs_objects_projects
WHERE lfs_objects_projects.project_id = fork_network_members.project_id
AND lfs_objects_projects.lfs_object_id = lop.lfs_object_id
)
SQL
)
end
end
def perform(start_id, end_id)
select_query =
ForkNetworkMember
.select('lop.lfs_object_id, fork_network_members.project_id')
.with_non_existing_lfs_objects
.where(project_id: start_id..end_id)
return if select_query.empty?
execute <<-SQL
INSERT INTO lfs_objects_projects (lfs_object_id, project_id)
#{select_query.to_sql}
SQL
end
private
def execute(sql)
::ActiveRecord::Base.connection.execute(sql)
end
end
end
end
apply: apply:
stage: deploy stage: deploy
image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.7.0" image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.8.0"
environment: environment:
name: production name: production
variables: variables:
...@@ -12,6 +12,8 @@ apply: ...@@ -12,6 +12,8 @@ apply:
GITLAB_RUNNER_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/gitlab-runner/values.yaml GITLAB_RUNNER_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/gitlab-runner/values.yaml
CILIUM_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/cilium/values.yaml CILIUM_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/cilium/values.yaml
JUPYTERHUB_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/jupyterhub/values.yaml JUPYTERHUB_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/jupyterhub/values.yaml
PROMETHEUS_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/prometheus/values.yaml
ELASTIC_STACK_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/elastic-stack/values.yaml
script: script:
- gitlab-managed-apps /usr/local/share/gitlab-managed-apps/helmfile.yaml - gitlab-managed-apps /usr/local/share/gitlab-managed-apps/helmfile.yaml
only: only:
......
...@@ -18,12 +18,21 @@ module Gitlab ...@@ -18,12 +18,21 @@ module Gitlab
super super
@group = @attributes['group'] @group = @attributes['group']
update_description
end end
private private
attr_reader :group attr_reader :group
# Convert description empty string to nil
# due to existing object being saved with description: nil
# Which makes object lookup to fail since nil != ''
def update_description
attributes['description'] = nil if attributes['description'] == ''
end
def where_clauses def where_clauses
[ [
where_clause_base, where_clause_base,
......
...@@ -159,6 +159,10 @@ module QA ...@@ -159,6 +159,10 @@ module QA
autoload :Validator, 'qa/page/validator' autoload :Validator, 'qa/page/validator'
autoload :Validatable, 'qa/page/validatable' autoload :Validatable, 'qa/page/validatable'
module SubMenus
autoload :Common, 'qa/page/sub_menus/common'
end
module Main module Main
autoload :Login, 'qa/page/main/login' autoload :Login, 'qa/page/main/login'
autoload :Menu, 'qa/page/main/menu' autoload :Menu, 'qa/page/main/menu'
......
...@@ -13,15 +13,22 @@ module QA ...@@ -13,15 +13,22 @@ module QA
element :contribution_analytics_link element :contribution_analytics_link
end end
view 'app/views/layouts/nav/sidebar/_analytics_links.html.haml' do
element :analytics_link
element :analytics_sidebar_submenu
end
def click_group_members_item def click_group_members_item
within_sidebar do within_sidebar do
click_element(:group_members_item) click_element(:group_members_item)
end end
end end
def click_group_analytics_item def click_contribution_analytics_item
within_sidebar do hover_element(:analytics_link) do
click_element(:contribution_analytics_link) within_submenu(:analytics_sidebar_submenu) do
click_element(:contribution_analytics_link)
end
end end
end end
......
...@@ -5,6 +5,8 @@ module QA ...@@ -5,6 +5,8 @@ module QA
module Group module Group
module SubMenus module SubMenus
module Common module Common
include QA::Page::SubMenus::Common
def self.included(base) def self.included(base)
base.class_eval do base.class_eval do
view 'app/views/layouts/nav/sidebar/_group.html.haml' do view 'app/views/layouts/nav/sidebar/_group.html.haml' do
...@@ -13,23 +15,10 @@ module QA ...@@ -13,23 +15,10 @@ module QA
end end
end end
def hover_element(element) private
within_sidebar do
find_element(element).hover
yield
end
end
def within_sidebar def sidebar_element
within_element(:group_sidebar) do :group_sidebar
yield
end
end
def within_submenu(element)
within_element(element) do
yield
end
end end
end end
end end
......
...@@ -5,20 +5,12 @@ module QA ...@@ -5,20 +5,12 @@ module QA
module Project module Project
module SubMenus module SubMenus
module Common module Common
def within_sidebar include QA::Page::SubMenus::Common
within('.sidebar-top-level-items') do
yield private
end
end
def within_submenu def sidebar_element
if has_css?('.fly-out-list') :project_sidebar
within('.fly-out-list') do
yield
end
else
yield
end
end end
end end
end end
......
# frozen_string_literal: true
module QA
module Page
module SubMenus
module Common
def hover_element(element)
within_sidebar do
find_element(element).hover
yield
end
end
def within_sidebar
within_element(sidebar_element) do
yield
end
end
def within_submenu(element = nil)
if element
within_element(element) do
yield
end
else
within_submenu_without_element do
yield
end
end
end
private
def within_submenu_without_element
if has_css?('.fly-out-list')
within('.fly-out-list') do
yield
end
else
yield
end
end
def sidebar_element
raise NotImplementedError
end
end
end
end
end
...@@ -55,6 +55,7 @@ require_relative 'cop/code_reuse/service_class' ...@@ -55,6 +55,7 @@ require_relative 'cop/code_reuse/service_class'
require_relative 'cop/code_reuse/presenter' require_relative 'cop/code_reuse/presenter'
require_relative 'cop/code_reuse/serializer' require_relative 'cop/code_reuse/serializer'
require_relative 'cop/code_reuse/active_record' require_relative 'cop/code_reuse/active_record'
require_relative 'cop/code_reuse/worker'
require_relative 'cop/group_public_or_visible_to_user' require_relative 'cop/group_public_or_visible_to_user'
require_relative 'cop/inject_enterprise_edition_module' require_relative 'cop/inject_enterprise_edition_module'
require_relative 'cop/graphql/authorize_types' require_relative 'cop/graphql/authorize_types'
......
...@@ -36,7 +36,7 @@ describe 'Internal Group access' do ...@@ -36,7 +36,7 @@ describe 'Internal Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/issues' do describe 'GET /groups/:path/-/issues' do
subject { issues_group_path(group) } subject { issues_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -51,7 +51,7 @@ describe 'Internal Group access' do ...@@ -51,7 +51,7 @@ describe 'Internal Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/-/merge_requests' do
let(:project) { create(:project, :internal, :repository, group: group) } let(:project) { create(:project, :internal, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
...@@ -68,7 +68,7 @@ describe 'Internal Group access' do ...@@ -68,7 +68,7 @@ describe 'Internal Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/group_members' do describe 'GET /groups/:path/-/group_members' do
subject { group_group_members_path(group) } subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -83,7 +83,7 @@ describe 'Internal Group access' do ...@@ -83,7 +83,7 @@ describe 'Internal Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/edit' do describe 'GET /groups/:path/-/edit' do
subject { edit_group_path(group) } subject { edit_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -36,7 +36,7 @@ describe 'Private Group access' do ...@@ -36,7 +36,7 @@ describe 'Private Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/issues' do describe 'GET /groups/:path/-/issues' do
subject { issues_group_path(group) } subject { issues_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -51,7 +51,7 @@ describe 'Private Group access' do ...@@ -51,7 +51,7 @@ describe 'Private Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/-/merge_requests' do
let(:project) { create(:project, :private, :repository, group: group) } let(:project) { create(:project, :private, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
...@@ -68,7 +68,7 @@ describe 'Private Group access' do ...@@ -68,7 +68,7 @@ describe 'Private Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/group_members' do describe 'GET /groups/:path/-/group_members' do
subject { group_group_members_path(group) } subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -83,7 +83,7 @@ describe 'Private Group access' do ...@@ -83,7 +83,7 @@ describe 'Private Group access' do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe 'GET /groups/:path/edit' do describe 'GET /groups/:path/-/edit' do
subject { edit_group_path(group) } subject { edit_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -36,7 +36,7 @@ describe 'Public Group access' do ...@@ -36,7 +36,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe 'GET /groups/:path/issues' do describe 'GET /groups/:path/-/issues' do
subject { issues_group_path(group) } subject { issues_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -51,7 +51,7 @@ describe 'Public Group access' do ...@@ -51,7 +51,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe 'GET /groups/:path/merge_requests' do describe 'GET /groups/:path/-/merge_requests' do
let(:project) { create(:project, :public, :repository, group: group) } let(:project) { create(:project, :public, :repository, group: group) }
subject { merge_requests_group_path(group) } subject { merge_requests_group_path(group) }
...@@ -68,7 +68,7 @@ describe 'Public Group access' do ...@@ -68,7 +68,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe 'GET /groups/:path/group_members' do describe 'GET /groups/:path/-/group_members' do
subject { group_group_members_path(group) } subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -83,7 +83,7 @@ describe 'Public Group access' do ...@@ -83,7 +83,7 @@ describe 'Public Group access' do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe 'GET /groups/:path/edit' do describe 'GET /groups/:path/-/edit' do
subject { edit_group_path(group) } subject { edit_group_path(group) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
......
...@@ -29,7 +29,7 @@ describe "Internal Project Access" do ...@@ -29,7 +29,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/tree/master" do describe "GET /:project_path/-/tree/master" do
subject { project_tree_path(project, project.repository.root_ref) } subject { project_tree_path(project, project.repository.root_ref) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -43,7 +43,7 @@ describe "Internal Project Access" do ...@@ -43,7 +43,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/commits/master" do describe "GET /:project_path/-/commits/master" do
subject { project_commits_path(project, project.repository.root_ref, limit: 1) } subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -57,7 +57,7 @@ describe "Internal Project Access" do ...@@ -57,7 +57,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/-/commit/:sha" do
subject { project_commit_path(project, project.repository.commit) } subject { project_commit_path(project, project.repository.commit) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -71,7 +71,7 @@ describe "Internal Project Access" do ...@@ -71,7 +71,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/compare" do describe "GET /:project_path/-/compare" do
subject { project_compare_index_path(project) } subject { project_compare_index_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -127,7 +127,7 @@ describe "Internal Project Access" do ...@@ -127,7 +127,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:external) } it { is_expected.to be_denied_for(:external) }
end end
describe "GET /:project_path/blob" do describe "GET /:project_path/-/blob" do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) } subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
...@@ -229,7 +229,7 @@ describe "Internal Project Access" do ...@@ -229,7 +229,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/-/merge_requests" do
subject { project_merge_requests_path(project) } subject { project_merge_requests_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -243,7 +243,7 @@ describe "Internal Project Access" do ...@@ -243,7 +243,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/merge_requests/new" do describe "GET /:project_path/-/merge_requests/new" do
subject { project_new_merge_request_path(project) } subject { project_new_merge_request_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -257,7 +257,7 @@ describe "Internal Project Access" do ...@@ -257,7 +257,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/branches" do describe "GET /:project_path/-/branches" do
subject { project_branches_path(project) } subject { project_branches_path(project) }
before do before do
...@@ -278,7 +278,7 @@ describe "Internal Project Access" do ...@@ -278,7 +278,7 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/tags" do describe "GET /:project_path/-/tags" do
subject { project_tags_path(project) } subject { project_tags_path(project) }
before do before do
......
...@@ -29,7 +29,7 @@ describe "Private Project Access" do ...@@ -29,7 +29,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/tree/master" do describe "GET /:project_path/-/tree/master" do
subject { project_tree_path(project, project.repository.root_ref) } subject { project_tree_path(project, project.repository.root_ref) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -43,7 +43,7 @@ describe "Private Project Access" do ...@@ -43,7 +43,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/commits/master" do describe "GET /:project_path/-/commits/master" do
subject { project_commits_path(project, project.repository.root_ref, limit: 1) } subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -57,7 +57,7 @@ describe "Private Project Access" do ...@@ -57,7 +57,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/-/commit/:sha" do
subject { project_commit_path(project, project.repository.commit) } subject { project_commit_path(project, project.repository.commit) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -71,7 +71,7 @@ describe "Private Project Access" do ...@@ -71,7 +71,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/compare" do describe "GET /:project_path/-/compare" do
subject { project_compare_index_path(project) } subject { project_compare_index_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -127,7 +127,7 @@ describe "Private Project Access" do ...@@ -127,7 +127,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/blob" do describe "GET /:project_path/-/blob" do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) } subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
...@@ -215,7 +215,7 @@ describe "Private Project Access" do ...@@ -215,7 +215,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/-/merge_requests" do
subject { project_merge_requests_path(project) } subject { project_merge_requests_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -229,7 +229,7 @@ describe "Private Project Access" do ...@@ -229,7 +229,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/branches" do describe "GET /:project_path/-/branches" do
subject { project_branches_path(project) } subject { project_branches_path(project) }
before do before do
...@@ -250,7 +250,7 @@ describe "Private Project Access" do ...@@ -250,7 +250,7 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/tags" do describe "GET /:project_path/-/tags" do
subject { project_tags_path(project) } subject { project_tags_path(project) }
before do before do
......
...@@ -29,7 +29,7 @@ describe "Public Project Access" do ...@@ -29,7 +29,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/tree/master" do describe "GET /:project_path/-/tree/master" do
subject { project_tree_path(project, project.repository.root_ref) } subject { project_tree_path(project, project.repository.root_ref) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -43,7 +43,7 @@ describe "Public Project Access" do ...@@ -43,7 +43,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/commits/master" do describe "GET /:project_path/-/commits/master" do
subject { project_commits_path(project, project.repository.root_ref, limit: 1) } subject { project_commits_path(project, project.repository.root_ref, limit: 1) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -57,7 +57,7 @@ describe "Public Project Access" do ...@@ -57,7 +57,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/commit/:sha" do describe "GET /:project_path/-/commit/:sha" do
subject { project_commit_path(project, project.repository.commit) } subject { project_commit_path(project, project.repository.commit) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -71,7 +71,7 @@ describe "Public Project Access" do ...@@ -71,7 +71,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/compare" do describe "GET /:project_path/-/compare" do
subject { project_compare_index_path(project) } subject { project_compare_index_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -345,7 +345,7 @@ describe "Public Project Access" do ...@@ -345,7 +345,7 @@ describe "Public Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/blob" do describe "GET /:project_path/-/blob" do
let(:commit) { project.repository.commit } let(:commit) { project.repository.commit }
subject { project_blob_path(project, File.join(commit.id, '.gitignore')) } subject { project_blob_path(project, File.join(commit.id, '.gitignore')) }
...@@ -446,7 +446,7 @@ describe "Public Project Access" do ...@@ -446,7 +446,7 @@ describe "Public Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/merge_requests" do describe "GET /:project_path/-/merge_requests" do
subject { project_merge_requests_path(project) } subject { project_merge_requests_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -460,7 +460,7 @@ describe "Public Project Access" do ...@@ -460,7 +460,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/merge_requests/new" do describe "GET /:project_path/-/merge_requests/new" do
subject { project_new_merge_request_path(project) } subject { project_new_merge_request_path(project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
...@@ -474,7 +474,7 @@ describe "Public Project Access" do ...@@ -474,7 +474,7 @@ describe "Public Project Access" do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/branches" do describe "GET /:project_path/-/branches" do
subject { project_branches_path(project) } subject { project_branches_path(project) }
before do before do
...@@ -495,7 +495,7 @@ describe "Public Project Access" do ...@@ -495,7 +495,7 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/tags" do describe "GET /:project_path/-/tags" do
subject { project_tags_path(project) } subject { project_tags_path(project) }
before do before do
......
...@@ -91,16 +91,37 @@ describe('Monitoring mutations', () => { ...@@ -91,16 +91,37 @@ describe('Monitoring mutations', () => {
expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-foss'); expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-foss');
}); });
it('should not remove default value of logsPath', () => { it('should not remove previously set properties', () => {
const defaultLogsPath = stateCopy.logsPath; const defaultLogsPath = stateCopy.logsPath;
mutations[types.SET_ENDPOINTS](stateCopy, {
logsPath: defaultLogsPath,
});
mutations[types.SET_ENDPOINTS](stateCopy, { mutations[types.SET_ENDPOINTS](stateCopy, {
dashboardEndpoint: 'dashboard.json', dashboardEndpoint: 'dashboard.json',
}); });
mutations[types.SET_ENDPOINTS](stateCopy, {
projectPath: '/gitlab-org/gitlab-foss',
});
expect(stateCopy.logsPath).toBe(defaultLogsPath); expect(stateCopy).toMatchObject({
logsPath: defaultLogsPath,
dashboardEndpoint: 'dashboard.json',
projectPath: '/gitlab-org/gitlab-foss',
});
});
it('should not update unknown properties', () => {
mutations[types.SET_ENDPOINTS](stateCopy, {
dashboardEndpoint: 'dashboard.json',
someOtherProperty: 'some invalid value', // someOtherProperty is not allowed
});
expect(stateCopy.dashboardEndpoint).toBe('dashboard.json');
expect(stateCopy.someOtherProperty).toBeUndefined();
}); });
}); });
describe('Individual panel/metric results', () => { describe('Individual panel/metric results', () => {
const metricId = '12_system_metrics_kubernetes_container_memory_total'; const metricId = '12_system_metrics_kubernetes_container_memory_total';
const result = [ const result = [
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import ReleaseDetailApp from '~/releases/detail/components/app.vue'; import ReleaseEditApp from '~/releases/components/app_edit.vue';
import { release } from '../../mock_data'; import { release } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
describe('Release detail component', () => { describe('Release edit component', () => {
let wrapper; let wrapper;
let releaseClone; let releaseClone;
let actions; let actions;
...@@ -27,9 +27,17 @@ describe('Release detail component', () => { ...@@ -27,9 +27,17 @@ describe('Release detail component', () => {
navigateToReleasesPage: jest.fn(), navigateToReleasesPage: jest.fn(),
}; };
const store = new Vuex.Store({ actions, state }); const store = new Vuex.Store({
modules: {
detail: {
namespaced: true,
actions,
state,
},
},
});
wrapper = mount(ReleaseDetailApp, { wrapper = mount(ReleaseEditApp, {
store, store,
}); });
......
...@@ -2,8 +2,8 @@ import { mount } from '@vue/test-utils'; ...@@ -2,8 +2,8 @@ import { mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility'; import { truncateSha } from '~/lib/utils/text_utility';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { release } from '../../mock_data'; import { release } from '../mock_data';
import EvidenceBlock from '~/releases/list/components/evidence_block.vue'; import EvidenceBlock from '~/releases/components/evidence_block.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('Evidence Block', () => { describe('Evidence Block', () => {
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import ReleaseBlockFooter from '~/releases/list/components/release_block_footer.vue'; import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { release } from '../../mock_data'; import { release } from '../mock_data';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
jest.mock('~/vue_shared/mixins/timeago', () => ({ jest.mock('~/vue_shared/mixins/timeago', () => ({
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { cloneDeep, merge } from 'lodash'; import { cloneDeep, merge } from 'lodash';
import { GlLink } from '@gitlab/ui'; import { GlLink } from '@gitlab/ui';
import ReleaseBlockHeader from '~/releases/list/components/release_block_header.vue'; import ReleaseBlockHeader from '~/releases/components/release_block_header.vue';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { release as originalRelease } from '../../mock_data'; import { release as originalRelease } from '../mock_data';
describe('Release block header', () => { describe('Release block header', () => {
let wrapper; let wrapper;
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlProgressBar, GlLink, GlBadge, GlButton } from '@gitlab/ui'; import { GlProgressBar, GlLink, GlBadge, GlButton } from '@gitlab/ui';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import ReleaseBlockMilestoneInfo from '~/releases/list/components/release_block_milestone_info.vue'; import ReleaseBlockMilestoneInfo from '~/releases/components/release_block_milestone_info.vue';
import { milestones } from '../../mock_data'; import { milestones } from '../mock_data';
import { MAX_MILESTONES_TO_DISPLAY } from '~/releases/list/constants'; import { MAX_MILESTONES_TO_DISPLAY } from '~/releases/constants';
describe('Release block milestone info', () => { describe('Release block milestone info', () => {
let wrapper; let wrapper;
......
import $ from 'jquery'; import $ from 'jquery';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { first } from 'underscore'; import { first } from 'underscore';
import EvidenceBlock from '~/releases/list/components/evidence_block.vue'; import EvidenceBlock from '~/releases/components/evidence_block.vue';
import ReleaseBlock from '~/releases/list/components/release_block.vue'; import ReleaseBlock from '~/releases/components/release_block.vue';
import ReleaseBlockFooter from '~/releases/list/components/release_block_footer.vue'; import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago'; import timeagoMixin from '~/vue_shared/mixins/timeago';
import { release } from '../../mock_data'; import { release } from '../mock_data';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { scrollToElement } from '~/lib/utils/common_utils'; import { scrollToElement } from '~/lib/utils/common_utils';
......
import axios from 'axios'; import axios from 'axios';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/releases/detail/store/actions'; import * as actions from '~/releases/stores/modules/detail/actions';
import * as types from '~/releases/detail/store/mutation_types'; import * as types from '~/releases/stores/modules/detail/mutation_types';
import { release } from '../../mock_data'; import { release } from '../../../mock_data';
import state from '~/releases/detail/store/state'; import state from '~/releases/stores/modules/detail/state';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility'; import { redirectTo } from '~/lib/utils/url_utility';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
* is resolved * is resolved
*/ */
import state from '~/releases/detail/store/state'; import state from '~/releases/stores/modules/detail/state';
import mutations from '~/releases/detail/store/mutations'; import mutations from '~/releases/stores/modules/detail/mutations';
import * as types from '~/releases/detail/store/mutation_types'; import * as types from '~/releases/stores/modules/detail/mutation_types';
import { release } from '../../mock_data'; import { release } from '../../../mock_data';
describe('Release detail mutations', () => { describe('Release detail mutations', () => {
let stateClone; let stateClone;
......
import _ from 'underscore'; import _ from 'underscore';
import Vue from 'vue'; import Vue from 'vue';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import app from '~/releases/list/components/app.vue'; import app from '~/releases/components/app_index.vue';
import createStore from '~/releases/list/store'; import createStore from '~/releases/stores';
import listModule from '~/releases/stores/modules/list';
import api from '~/api'; import api from '~/api';
import { resetStore } from '../store/helpers'; import { resetStore } from '../stores/modules/list/helpers';
import { import {
pageInfoHeadersWithoutPagination, pageInfoHeadersWithoutPagination,
pageInfoHeadersWithPagination, pageInfoHeadersWithPagination,
release, release,
releases, releases,
} from '../../mock_data'; } from '../mock_data';
describe('Releases App ', () => { describe('Releases App ', () => {
const Component = Vue.extend(app); const Component = Vue.extend(app);
...@@ -25,7 +26,7 @@ describe('Releases App ', () => { ...@@ -25,7 +26,7 @@ describe('Releases App ', () => {
}; };
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore({ list: listModule });
releasesPagination = _.range(21).map(index => ({ ...release, tag_name: `${index}.00` })); releasesPagination = _.range(21).map(index => ({ ...release, tag_name: `${index}.00` }));
}); });
......
...@@ -4,12 +4,12 @@ import { ...@@ -4,12 +4,12 @@ import {
fetchReleases, fetchReleases,
receiveReleasesSuccess, receiveReleasesSuccess,
receiveReleasesError, receiveReleasesError,
} from '~/releases/list/store/actions'; } from '~/releases/stores/modules/list/actions';
import state from '~/releases/list/store/state'; import state from '~/releases/stores/modules/list/state';
import * as types from '~/releases/list/store/mutation_types'; import * as types from '~/releases/stores/modules/list/mutation_types';
import api from '~/api'; import api from '~/api';
import { parseIntPagination } from '~/lib/utils/common_utils'; import { parseIntPagination } from '~/lib/utils/common_utils';
import { pageInfoHeadersWithoutPagination, releases } from '../../mock_data'; import { pageInfoHeadersWithoutPagination, releases } from '../../../mock_data';
describe('Releases State actions', () => { describe('Releases State actions', () => {
let mockedState; let mockedState;
......
import state from '~/releases/list/store/state'; import state from '~/releases/stores/modules/list/state';
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export const resetStore = store => { export const resetStore = store => {
......
import state from '~/releases/list/store/state'; import state from '~/releases/stores/modules/list/state';
import mutations from '~/releases/list/store/mutations'; import mutations from '~/releases/stores/modules/list/mutations';
import * as types from '~/releases/list/store/mutation_types'; import * as types from '~/releases/stores/modules/list/mutation_types';
import { parseIntPagination } from '~/lib/utils/common_utils'; import { parseIntPagination } from '~/lib/utils/common_utils';
import { pageInfoHeadersWithoutPagination, releases } from '../../mock_data'; import { pageInfoHeadersWithoutPagination, releases } from '../../../mock_data';
describe('Releases Store Mutations', () => { describe('Releases Store Mutations', () => {
let stateCopy; let stateCopy;
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::LinkLfsObjects, :migration, schema: 2020_02_10_062432 do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:fork_networks) { table(:fork_networks) }
let(:fork_network_members) { table(:fork_network_members) }
let(:lfs_objects) { table(:lfs_objects) }
let(:lfs_objects_projects) { table(:lfs_objects_projects) }
let(:namespace) { namespaces.create(name: 'GitLab', path: 'gitlab') }
let(:source_project) { projects.create(namespace_id: namespace.id) }
let(:another_source_project) { projects.create(namespace_id: namespace.id) }
let(:project) { projects.create(namespace_id: namespace.id) }
let(:another_project) { projects.create(namespace_id: namespace.id) }
let(:other_project) { projects.create(namespace_id: namespace.id) }
let(:linked_project) { projects.create(namespace_id: namespace.id) }
let(:fork_network) { fork_networks.create(root_project_id: source_project.id) }
let(:another_fork_network) { fork_networks.create(root_project_id: another_source_project.id) }
let(:lfs_object) { lfs_objects.create(oid: 'abc123', size: 100) }
let(:another_lfs_object) { lfs_objects.create(oid: 'def456', size: 200) }
before do
# Create links between projects
fork_network_members.create(fork_network_id: fork_network.id, project_id: source_project.id, forked_from_project_id: nil)
[project, another_project, linked_project].each do |p|
fork_network_members.create(
fork_network_id: fork_network.id,
project_id: p.id,
forked_from_project_id: fork_network.root_project_id
)
end
fork_network_members.create(fork_network_id: another_fork_network.id, project_id: another_source_project.id, forked_from_project_id: nil)
fork_network_members.create(fork_network_id: another_fork_network.id, project_id: other_project.id, forked_from_project_id: another_fork_network.root_project_id)
# Links LFS objects to some projects
[source_project, another_source_project, linked_project].each do |p|
lfs_objects_projects.create(lfs_object_id: lfs_object.id, project_id: p.id)
lfs_objects_projects.create(lfs_object_id: another_lfs_object.id, project_id: p.id)
end
end
it 'creates LfsObjectsProject records for forks within the specified range of project IDs' do
expect { subject.perform(project.id, other_project.id) }.to change { lfs_objects_projects.count }.by(6)
expect(lfs_object_ids_for(project)).to match_array(lfs_object_ids_for(source_project))
expect(lfs_object_ids_for(another_project)).to match_array(lfs_object_ids_for(source_project))
expect(lfs_object_ids_for(other_project)).to match_array(lfs_object_ids_for(another_source_project))
expect { subject.perform(project.id, other_project.id) }.not_to change { lfs_objects_projects.count }
end
context 'when it is not necessary to create LfsObjectProject records' do
it 'does not create LfsObjectProject records' do
expect { subject.perform(linked_project.id, linked_project.id) }
.not_to change { lfs_objects_projects.count }
end
end
def lfs_object_ids_for(project)
lfs_objects_projects.where(project_id: project.id).pluck(:lfs_object_id)
end
end
...@@ -26,6 +26,16 @@ describe Gitlab::ImportExport::GroupObjectBuilder do ...@@ -26,6 +26,16 @@ describe Gitlab::ImportExport::GroupObjectBuilder do
expect(label.persisted?).to be true expect(label.persisted?).to be true
end end
context 'when description is an empty string' do
let(:label_attributes) { base_attributes.merge('type' => 'GroupLabel', 'description' => '') }
it 'finds the existing group label' do
group_label = create(:group_label, label_attributes)
expect(described_class.build(Label, label_attributes)).to eq(group_label)
end
end
end end
context 'milestones' do context 'milestones' do
...@@ -41,4 +51,16 @@ describe Gitlab::ImportExport::GroupObjectBuilder do ...@@ -41,4 +51,16 @@ describe Gitlab::ImportExport::GroupObjectBuilder do
expect(milestone.persisted?).to be true expect(milestone.persisted?).to be true
end end
end end
describe '#initialize' do
context 'when attributes contain description as empty string' do
let(:attributes) { base_attributes.merge('description' => '') }
it 'converts empty string to nil' do
builder = described_class.new(Label, attributes)
expect(builder.send(:attributes)).to include({ 'description' => nil })
end
end
end
end end
This diff is collapsed.
This diff is collapsed.
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