Commit 88542a5e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b570d73e
<script> <script>
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState } from 'vuex';
import { import {
GlEmptyState, GlEmptyState,
GlButton, GlButton,
GlLink, GlLink,
GlLoadingIcon, GlLoadingIcon,
GlTable, GlTable,
GlSearchBoxByType, GlSearchBoxByClick,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -28,7 +28,7 @@ export default { ...@@ -28,7 +28,7 @@ export default {
GlLink, GlLink,
GlLoadingIcon, GlLoadingIcon,
GlTable, GlTable,
GlSearchBoxByType, GlSearchBoxByClick,
Icon, Icon,
TimeAgo, TimeAgo,
}, },
...@@ -64,10 +64,6 @@ export default { ...@@ -64,10 +64,6 @@ export default {
}, },
computed: { computed: {
...mapState('list', ['errors', 'externalUrl', 'loading']), ...mapState('list', ['errors', 'externalUrl', 'loading']),
...mapGetters('list', ['filterErrorsByTitle']),
filteredErrors() {
return this.errorSearchQuery ? this.filterErrorsByTitle(this.errorSearchQuery) : this.errors;
},
}, },
created() { created() {
if (this.errorTrackingEnabled) { if (this.errorTrackingEnabled) {
...@@ -76,6 +72,9 @@ export default { ...@@ -76,6 +72,9 @@ export default {
}, },
methods: { methods: {
...mapActions('list', ['startPolling', 'restartPolling']), ...mapActions('list', ['startPolling', 'restartPolling']),
filterErrors() {
this.startPolling(`${this.indexPath}?search_term=${this.errorSearchQuery}`);
},
trackViewInSentryOptions, trackViewInSentryOptions,
viewDetails(errorId) { viewDetails(errorId) {
visitUrl(`error_tracking/${errorId}/details`); visitUrl(`error_tracking/${errorId}/details`);
...@@ -87,17 +86,15 @@ export default { ...@@ -87,17 +86,15 @@ export default {
<template> <template>
<div> <div>
<div v-if="errorTrackingEnabled"> <div v-if="errorTrackingEnabled">
<div v-if="loading" class="py-3"> <div>
<gl-loading-icon :size="3" />
</div>
<div v-else>
<div class="d-flex flex-row justify-content-around bg-secondary border"> <div class="d-flex flex-row justify-content-around bg-secondary border">
<gl-search-box-by-type <gl-search-box-by-click
v-model="errorSearchQuery" v-model="errorSearchQuery"
class="col-lg-10 m-3 p-0" class="col-lg-10 m-3 p-0"
:placeholder="__('Search or filter results...')" :placeholder="__('Search or filter results...')"
type="search" type="search"
autofocus autofocus
@submit="filterErrors"
/> />
<gl-button <gl-button
v-track-event="trackViewInSentryOptions(externalUrl)" v-track-event="trackViewInSentryOptions(externalUrl)"
...@@ -111,9 +108,14 @@ export default { ...@@ -111,9 +108,14 @@ export default {
</gl-button> </gl-button>
</div> </div>
<div v-if="loading" class="py-3">
<gl-loading-icon size="md" />
</div>
<gl-table <gl-table
v-else
class="mt-3" class="mt-3"
:items="filteredErrors" :items="errors"
:fields="$options.fields" :fields="$options.fields"
:show-empty="true" :show-empty="true"
fixed fixed
......
...@@ -4,7 +4,6 @@ import Vuex from 'vuex'; ...@@ -4,7 +4,6 @@ import Vuex from 'vuex';
import * as listActions from './list/actions'; import * as listActions from './list/actions';
import listMutations from './list/mutations'; import listMutations from './list/mutations';
import listState from './list/state'; import listState from './list/state';
import * as listGetters from './list/getters';
import * as detailsActions from './details/actions'; import * as detailsActions from './details/actions';
import detailsMutations from './details/mutations'; import detailsMutations from './details/mutations';
...@@ -21,7 +20,6 @@ export const createStore = () => ...@@ -21,7 +20,6 @@ export const createStore = () =>
state: listState(), state: listState(),
actions: listActions, actions: listActions,
mutations: listMutations, mutations: listMutations,
getters: listGetters,
}, },
details: { details: {
namespaced: true, namespaced: true,
......
...@@ -7,6 +7,8 @@ import { __, sprintf } from '~/locale'; ...@@ -7,6 +7,8 @@ import { __, sprintf } from '~/locale';
let eTagPoll; let eTagPoll;
export function startPolling({ commit, dispatch }, endpoint) { export function startPolling({ commit, dispatch }, endpoint) {
commit(types.SET_LOADING, true);
eTagPoll = new Poll({ eTagPoll = new Poll({
resource: Service, resource: Service,
method: 'getSentryData', method: 'getSentryData',
......
export const filterErrorsByTitle = state => errorQuery =>
state.errors.filter(error => error.title.match(new RegExp(`${errorQuery}`, 'i')));
export default () => {};
...@@ -44,7 +44,11 @@ class Projects::ErrorTrackingController < Projects::ApplicationController ...@@ -44,7 +44,11 @@ class Projects::ErrorTrackingController < Projects::ApplicationController
private private
def render_index_json def render_index_json
service = ErrorTracking::ListIssuesService.new(project, current_user) service = ErrorTracking::ListIssuesService.new(
project,
current_user,
list_issues_params
)
result = service.execute result = service.execute
return if handle_errors(result) return if handle_errors(result)
...@@ -106,6 +110,10 @@ class Projects::ErrorTrackingController < Projects::ApplicationController ...@@ -106,6 +110,10 @@ class Projects::ErrorTrackingController < Projects::ApplicationController
end end
end end
def list_issues_params
params.permit(:search_term)
end
def list_projects_params def list_projects_params
params.require(:error_tracking_setting).permit([:api_host, :token]) params.require(:error_tracking_setting).permit([:api_host, :token])
end end
......
...@@ -5,6 +5,28 @@ module ErrorTracking ...@@ -5,6 +5,28 @@ module ErrorTracking
DEFAULT_ISSUE_STATUS = 'unresolved' DEFAULT_ISSUE_STATUS = 'unresolved'
DEFAULT_LIMIT = 20 DEFAULT_LIMIT = 20
def execute
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
search_term: search_term
)
# our results are not yet ready
unless result
return error('Not ready. Try again later', :no_content)
end
if result[:error].present?
return error(result[:error], http_status_for(result[:error_type]))
end
success(issues: result[:issues])
end
def external_url def external_url
project_error_tracking_setting&.sentry_external_url project_error_tracking_setting&.sentry_external_url
end end
...@@ -26,5 +48,17 @@ module ErrorTracking ...@@ -26,5 +48,17 @@ module ErrorTracking
def limit def limit
params[:limit] || DEFAULT_LIMIT params[:limit] || DEFAULT_LIMIT
end end
def search_term
params[:search_term].presence
end
def enabled?
project_error_tracking_setting&.enabled?
end
def can_read?
can?(current_user, :read_sentry_issue, project)
end
end end
end end
...@@ -62,8 +62,6 @@ module MergeRequests ...@@ -62,8 +62,6 @@ module MergeRequests
end end
def updated_check! def updated_check!
return unless Feature.enabled?(:validate_merge_sha, merge_request.target_project, default_enabled: false)
unless source_matches? unless source_matches?
raise_error('Branch has been updated since the merge was requested. '\ raise_error('Branch has been updated since the merge was requested. '\
'Please review the changes.') 'Please review the changes.')
......
---
title: Validate the merge sha before merging, confirming that the merge will only
contain what the user saw
merge_request: 20348
author:
type: fixed
---
title: Remove done callbacks from vue_shared/components/markdown
merge_request: 16842
author: Lee Tickett
type: other
---
title: Search list of Sentry errors by title in GitLab
merge_request: 19439
author:
type: added
# frozen_string_literal: true
# for secondary email confirmations - uses the same confirmation controller as :users # for secondary email confirmations - uses the same confirmation controller as :users
devise_for :emails, path: 'profile/emails', controllers: { confirmations: :confirmations } devise_for :emails, path: 'profile/emails', controllers: { confirmations: :confirmations }
...@@ -42,14 +44,6 @@ resource :profile, only: [:show, :update] do ...@@ -42,14 +44,6 @@ resource :profile, only: [:show, :update] do
end end
end end
Gitlab.ee do
resource :slack, only: [:edit] do
member do
get :slack_link
end
end
end
resources :chat_names, only: [:index, :new, :create, :destroy] do resources :chat_names, only: [:index, :new, :create, :destroy] do
collection do collection do
delete :deny delete :deny
...@@ -73,10 +67,5 @@ resource :profile, only: [:show, :update] do ...@@ -73,10 +67,5 @@ resource :profile, only: [:show, :update] do
end end
resources :u2f_registrations, only: [:destroy] resources :u2f_registrations, only: [:destroy]
Gitlab.ee do
resources :pipeline_quota, only: [:index]
resources :billings, only: [:index]
end
end end
end end
resources :projects, only: [:index, :new, :create] # frozen_string_literal: true
Gitlab.ee do resources :projects, only: [:index, :new, :create]
scope "/-/push_from_secondary/:geo_node_id" do
draw :git_http
end
end
draw :git_http draw :git_http
...@@ -87,22 +83,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -87,22 +83,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :operations, only: [:show, :update] resource :operations, only: [:show, :update]
resource :integrations, only: [:show] resource :integrations, only: [:show]
Gitlab.ee do
resource :slack, only: [:destroy, :edit, :update] do
get :slack_auth
end
end
resource :repository, only: [:show], controller: :repository do resource :repository, only: [:show], controller: :repository do
post :create_deploy_token, path: 'deploy_token/create' post :create_deploy_token, path: 'deploy_token/create'
post :cleanup post :cleanup
end end
end end
Gitlab.ee do
resources :feature_flags
end
resources :autocomplete_sources, only: [] do resources :autocomplete_sources, only: [] do
collection do collection do
get 'members' get 'members'
...@@ -268,16 +254,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -268,16 +254,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace :prometheus do namespace :prometheus do
resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do resources :metrics, constraints: { id: %r{[^\/]+} }, only: [:index, :new, :create, :edit, :update, :destroy] do
get :active_common, on: :collection get :active_common, on: :collection
Gitlab.ee do
post :validate_query, on: :collection
end
end
Gitlab.ee do
resources :alerts, constraints: { id: /\d+/ }, only: [:index, :create, :show, :update, :destroy] do
post :notify, on: :collection
end
end end
end end
...@@ -290,15 +266,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -290,15 +266,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :pipeline_status get :pipeline_status
get :ci_environments_status get :ci_environments_status
post :toggle_subscription post :toggle_subscription
Gitlab.ee do
get :approvals
post :approvals, action: :approve
delete :approvals, action: :unapprove
post :rebase
end
post :remove_wip post :remove_wip
post :assign_related_issues post :assign_related_issues
get :discussions, format: :json get :discussions, format: :json
...@@ -336,21 +303,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -336,21 +303,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post :bulk_update post :bulk_update
end end
Gitlab.ee do
resources :approvers, only: :destroy
delete 'approvers', to: 'approvers#destroy_via_user_id', as: :approver_via_user_id
resources :approver_groups, only: :destroy
scope module: :merge_requests do
resources :drafts, only: [:index, :update, :create, :destroy] do
collection do
post :publish
delete :discard
end
end
end
end
resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do resources :discussions, only: [:show], constraints: { id: /\h{40}/ } do
member do member do
post :resolve post :resolve
...@@ -381,21 +333,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -381,21 +333,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
resources :path_locks, only: [:index, :destroy] do
collection do
post :toggle
end
end
get '/service_desk' => 'service_desk#show', as: :service_desk
put '/service_desk' => 'service_desk#update', as: :service_desk_refresh
end
Gitlab.ee do
resources :push_rules, constraints: { id: /\d+/ }, only: [:update]
end
resources :pipelines, only: [:index, :new, :create, :show] do resources :pipelines, only: [:index, :new, :create, :show] do
collection do collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update] resource :pipelines_settings, path: 'settings', only: [:show, :update]
...@@ -414,11 +351,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -414,11 +351,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :failures get :failures
get :status get :status
get :test_report get :test_report
Gitlab.ee do
get :security
get :licenses
end
end end
member do member do
...@@ -447,21 +379,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -447,21 +379,12 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api get '/prometheus/api/v1/*proxy_path', to: 'environments/prometheus_api#proxy', as: :prometheus_api
Gitlab.ee do
get :logs
get '/pods/(:pod_name)/containers/(:container_name)/logs', to: 'environments#k8s_pod_logs', as: :k8s_pod_logs
end
end end
collection do collection do
get :metrics, action: :metrics_redirect get :metrics, action: :metrics_redirect
get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ } get :folder, path: 'folders/*id', constraints: { format: /(html|json)/ }
get :search get :search
Gitlab.ee do
get :logs, action: :logs_redirect
end
end end
resources :deployments, only: [:index] do resources :deployments, only: [:index] do
...@@ -472,14 +395,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -472,14 +395,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
resources :protected_environments, only: [:create, :update, :destroy], constraints: { id: /\d+/ } do
collection do
get 'search'
end
end
end
namespace :serverless do namespace :serverless do
scope :functions do scope :functions do
get '/:environment_id/:id', to: 'functions#show' get '/:environment_id/:id', to: 'functions#show'
...@@ -522,14 +437,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -522,14 +437,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
namespace :security do
resource :dashboard, only: [:show], controller: :dashboard
end
resources :vulnerability_feedback, only: [:index, :create, :update, :destroy], constraints: { id: /\d+/ }
end
get :issues, to: 'issues#calendar', constraints: lambda { |req| req.format == :ics } get :issues, to: 'issues#calendar', constraints: lambda { |req| req.format == :ics }
resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do
...@@ -543,24 +450,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -543,24 +450,11 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :realtime_changes get :realtime_changes
post :create_merge_request post :create_merge_request
get :discussions, format: :json get :discussions, format: :json
Gitlab.ee do
get 'designs(/*vueroute)', to: 'issues#designs', as: :designs, format: false
end
end end
collection do collection do
post :bulk_update post :bulk_update
post :import_csv post :import_csv
Gitlab.ee do
post :export_csv
get :service_desk
end
end
Gitlab.ee do
resources :issue_links, only: [:index, :create, :destroy], as: 'links', path: 'links'
end end
end end
...@@ -595,11 +489,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -595,11 +489,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
resources :approvers, only: :destroy
resources :approver_groups, only: :destroy
end
resources :runner_projects, only: [:create, :destroy] resources :runner_projects, only: [:create, :destroy]
resources :badges, only: [:index] do resources :badges, only: [:index] do
collection do collection do
...@@ -614,10 +503,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -614,10 +503,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
Gitlab.ee do
resources :audit_events, only: [:index]
end
resources :error_tracking, only: [:index], controller: :error_tracking do resources :error_tracking, only: [:index], controller: :error_tracking do
collection do collection do
get ':issue_id/details', get ':issue_id/details',
...@@ -639,9 +524,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -639,9 +524,15 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
draw :wiki draw :wiki
draw :repository draw :repository
Gitlab.ee do # Legacy routes.
resources :managed_licenses, only: [:index, :show, :new, :create, :edit, :update, :destroy] # Introduced in 12.0.
end # Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848.
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers)
end end
resources(:projects, resources(:projects,
...@@ -666,23 +557,4 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -666,23 +557,4 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
end end
end end
# Legacy routes.
# Introduced in 12.0.
# Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848.
scope(path: '*namespace_id',
as: :namespace,
namespace_id: Gitlab::PathRegex.full_namespace_route_regex) do
scope(path: ':project_id',
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
module: :projects,
as: :project) do
Gitlab::Routing.redirect_legacy_paths(self, :settings, :branches, :tags,
:network, :graphs, :autocomplete_sources,
:project_members, :deploy_keys, :deploy_tokens,
:labels, :milestones, :services, :boards, :releases,
:forks, :group_links, :import, :avatar, :mirror,
:cycle_analytics, :mattermost, :variables, :triggers)
end
end
end end
Gitlab.ee do # frozen_string_literal: true
get 'unsubscribes/:email', to: 'unsubscribes#show', as: :unsubscribe
post 'unsubscribes/:email', to: 'unsubscribes#create'
end
# Allows individual providers to be directed to a chosen controller # Allows individual providers to be directed to a chosen controller
# Call from inside devise_scope # Call from inside devise_scope
...@@ -30,10 +27,6 @@ devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks, ...@@ -30,10 +27,6 @@ devise_for :users, controllers: { omniauth_callbacks: :omniauth_callbacks,
devise_scope :user do devise_scope :user do
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
get '/users/almost_there' => 'confirmations#almost_there' get '/users/almost_there' => 'confirmations#almost_there'
Gitlab.ee do
get '/users/auth/kerberos_spnego/negotiate' => 'omniauth_kerberos_spnego#negotiate'
end
end end
scope '-/users', module: :users do scope '-/users', module: :users do
......
...@@ -25,10 +25,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2] ...@@ -25,10 +25,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2]
remove_concurrent_index(:redirect_routes, :permanent) remove_concurrent_index(:redirect_routes, :permanent)
end end
# If we're on MySQL then the existing index on path is ok. But on
# Postgres we need to clean things up:
break unless Gitlab::Database.postgresql?
if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : "" if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""
# Unique index on lower(path) across both types of redirect_routes: # Unique index on lower(path) across both types of redirect_routes:
...@@ -53,8 +49,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2] ...@@ -53,8 +49,6 @@ class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2]
disable_statement_timeout do disable_statement_timeout do
add_concurrent_index(:redirect_routes, :permanent) add_concurrent_index(:redirect_routes, :permanent)
break unless Gitlab::Database.postgresql?
execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);") execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));") execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")
......
...@@ -8,16 +8,10 @@ class AddSectionNameIdIndexOnCiBuildTraceSections < ActiveRecord::Migration[4.2] ...@@ -8,16 +8,10 @@ class AddSectionNameIdIndexOnCiBuildTraceSections < ActiveRecord::Migration[4.2]
disable_ddl_transaction! disable_ddl_transaction!
def up def up
# MySQL may already have this as a foreign key add_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
unless index_exists?(:ci_build_trace_sections, :section_name_id, name: INDEX_NAME)
add_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
end end
def down def down
# We cannot remove index for MySQL because it's needed for foreign key remove_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
if Gitlab::Database.postgresql?
remove_concurrent_index :ci_build_trace_sections, :section_name_id, name: INDEX_NAME
end
end end
end end
...@@ -13,28 +13,6 @@ class AddIndexesForUserActivityQueries < ActiveRecord::Migration[4.2] ...@@ -13,28 +13,6 @@ class AddIndexesForUserActivityQueries < ActiveRecord::Migration[4.2]
def down def down
remove_concurrent_index :events, [:author_id, :project_id] if index_exists?(:events, [:author_id, :project_id]) remove_concurrent_index :events, [:author_id, :project_id] if index_exists?(:events, [:author_id, :project_id])
patch_foreign_keys do remove_concurrent_index :user_interacted_projects, :user_id if index_exists?(:user_interacted_projects, :user_id)
remove_concurrent_index :user_interacted_projects, :user_id if index_exists?(:user_interacted_projects, :user_id)
end
end
private
def patch_foreign_keys
return yield if Gitlab::Database.postgresql?
# MySQL doesn't like to remove the index with a foreign key using it.
remove_foreign_key :user_interacted_projects, :users if fk_exists?(:user_interacted_projects, :user_id)
yield
# Let's re-add the foreign key using the existing index on (user_id, project_id)
add_concurrent_foreign_key :user_interacted_projects, :users, column: :user_id unless fk_exists?(:user_interacted_projects, :user_id)
end
def fk_exists?(table, column)
foreign_keys(table).any? do |key|
key.options[:column] == column.to_s
end
end end
end end
...@@ -29,14 +29,7 @@ class DropDuplicateProtectedTags < ActiveRecord::Migration[4.2] ...@@ -29,14 +29,7 @@ class DropDuplicateProtectedTags < ActiveRecord::Migration[4.2]
.where(project_id: projects) .where(project_id: projects)
.where.not(id: ids) .where.not(id: ids)
if Gitlab::Database.postgresql? tags.delete_all
tags.delete_all
else
# Workaround needed for MySQL
sql = "SELECT id FROM (#{tags.to_sql}) protected_tags"
ProtectedTag.where("id IN (#{sql})").delete_all # rubocop:disable GitlabSecurity/SqlInjection
end
end end
end end
......
...@@ -8,11 +8,10 @@ class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration[4.2] ...@@ -8,11 +8,10 @@ class AddIndexToProjectDeployTokensDeployTokenId < ActiveRecord::Migration[4.2]
disable_ddl_transaction! disable_ddl_transaction!
def up def up
# MySQL already has index inserted add_concurrent_index :project_deploy_tokens, :deploy_token_id
add_concurrent_index :project_deploy_tokens, :deploy_token_id if Gitlab::Database.postgresql?
end end
def down def down
remove_concurrent_index(:project_deploy_tokens, :deploy_token_id) if Gitlab::Database.postgresql? remove_concurrent_index(:project_deploy_tokens, :deploy_token_id)
end end
end end
...@@ -50,17 +50,9 @@ class RemoveRedundantPipelineStages < ActiveRecord::Migration[4.2] ...@@ -50,17 +50,9 @@ class RemoveRedundantPipelineStages < ActiveRecord::Migration[4.2]
UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids}) UPDATE ci_builds SET stage_id = NULL WHERE stage_id IN (#{redundant_stages_ids})
SQL SQL
if Gitlab::Database.postgresql? execute <<~SQL
execute <<~SQL DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids})
DELETE FROM ci_stages WHERE id IN (#{redundant_stages_ids}) SQL
SQL
else # We can't modify a table we are selecting from on MySQL
execute <<~SQL
DELETE a FROM ci_stages AS a, ci_stages AS b
WHERE a.pipeline_id = b.pipeline_id AND a.name = b.name
AND a.id <> b.id
SQL
end
end end
end end
end end
...@@ -12,11 +12,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2] ...@@ -12,11 +12,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2]
disable_ddl_transaction! disable_ddl_transaction!
def up def up
if Gitlab::Database.postgresql? PostgresStrategy.new.up
PostgresStrategy.new
else
MysqlStrategy.new
end.up
if index_exists_by_name?(:user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME) if index_exists_by_name?(:user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME)
remove_concurrent_index_by_name :user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME remove_concurrent_index_by_name :user_interacted_projects, CreateUserInteractedProjectsTable::INDEX_NAME
...@@ -140,30 +136,4 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2] ...@@ -140,30 +136,4 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration[4.2]
remove_concurrent_index(*args) if index_exists?(*args) remove_concurrent_index(*args) if index_exists?(*args)
end end
end end
class MysqlStrategy < ActiveRecord::Migration[4.2]
include Gitlab::Database::MigrationHelpers
def up
execute <<~SQL
INSERT INTO user_interacted_projects (user_id, project_id)
SELECT e.user_id, e.project_id
FROM (SELECT DISTINCT author_id AS user_id, project_id FROM events WHERE project_id IS NOT NULL) AS e
LEFT JOIN user_interacted_projects ucp USING (user_id, project_id)
WHERE ucp.user_id IS NULL
SQL
unless index_exists?(:user_interacted_projects, [:project_id, :user_id])
add_concurrent_index :user_interacted_projects, [:project_id, :user_id], unique: true, name: UNIQUE_INDEX_NAME
end
unless foreign_key_exists?(:user_interacted_projects, :users, column: :user_id)
add_concurrent_foreign_key :user_interacted_projects, :users, column: :user_id, on_delete: :cascade
end
unless foreign_key_exists?(:user_interacted_projects, :projects, column: :project_id)
add_concurrent_foreign_key :user_interacted_projects, :projects, column: :project_id, on_delete: :cascade
end
end
end
end end
...@@ -327,7 +327,12 @@ module Gitlab ...@@ -327,7 +327,12 @@ module Gitlab
end end
def find_or_create_object! def find_or_create_object!
return relation_class.find_or_create_by(project_id: @project.id) if UNIQUE_RELATIONS.include?(@relation_name) if UNIQUE_RELATIONS.include?(@relation_name)
unique_relation_object = relation_class.find_or_create_by(project_id: @project.id)
unique_relation_object.assign_attributes(parsed_relation_hash)
return unique_relation_object
end
# Can't use IDs as validation exists calling `group` or `project` attributes # Can't use IDs as validation exists calling `group` or `project` attributes
finder_hash = parsed_relation_hash.tap do |hash| finder_hash = parsed_relation_hash.tap do |hash|
......
...@@ -10,7 +10,7 @@ module Gitlab ...@@ -10,7 +10,7 @@ module Gitlab
RoutesNotFound = Class.new(StandardError) RoutesNotFound = Class.new(StandardError)
def draw(routes_name) def draw(routes_name)
drawn_any = draw_ce(routes_name) | draw_ee(routes_name) drawn_any = draw_ee(routes_name) | draw_ce(routes_name)
drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}")) drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}"))
end end
......
...@@ -25,8 +25,12 @@ module Sentry ...@@ -25,8 +25,12 @@ module Sentry
map_to_event(latest_event) map_to_event(latest_event)
end end
def list_issues(issue_status:, limit:) def list_issues(issue_status:, limit:, search_term: '')
issues = get_issues(issue_status: issue_status, limit: limit) issues = get_issues(
issue_status: issue_status,
limit: limit,
search_term: search_term
)
validate_size(issues) validate_size(issues)
...@@ -71,13 +75,14 @@ module Sentry ...@@ -71,13 +75,14 @@ module Sentry
response = handle_request_exceptions do response = handle_request_exceptions do
Gitlab::HTTP.get(url, **request_params.merge(params)) Gitlab::HTTP.get(url, **request_params.merge(params))
end end
handle_response(response) handle_response(response)
end end
def get_issues(issue_status:, limit:) def get_issues(issue_status:, limit:, search_term: '')
query = "is:#{issue_status} #{search_term}".strip
http_get(issues_api_url, query: { http_get(issues_api_url, query: {
query: "is:#{issue_status}", query: query,
limit: limit limit: limit
}) })
end end
......
...@@ -17422,6 +17422,9 @@ msgstr "" ...@@ -17422,6 +17422,9 @@ msgstr ""
msgid "There was an error fetching cycle analytics stages." msgid "There was an error fetching cycle analytics stages."
msgstr "" msgstr ""
msgid "There was an error fetching data for the chart"
msgstr ""
msgid "There was an error fetching data for the selected stage" msgid "There was an error fetching data for the selected stage"
msgstr "" msgstr ""
......
...@@ -16,37 +16,21 @@ retry gem install knapsack --no-document ...@@ -16,37 +16,21 @@ retry gem install knapsack --no-document
cp config/gitlab.yml.example config/gitlab.yml cp config/gitlab.yml.example config/gitlab.yml
sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml
# Determine the database by looking at the job name. cp config/database.yml.postgresql config/database.yml
# This would make the default database postgresql.
if [[ "${CI_JOB_NAME#*mysql}" != "$CI_JOB_NAME" ]]; then
export GITLAB_DATABASE='mysql'
else
export GITLAB_DATABASE='postgresql'
fi
cp config/database.yml.$GITLAB_DATABASE config/database.yml
if [ -f config/database_geo.yml.$GITLAB_DATABASE ]; then if [ -f config/database_geo.yml.postgresql ]; then
cp config/database_geo.yml.$GITLAB_DATABASE config/database_geo.yml cp config/database_geo.yml.postgresql config/database_geo.yml
fi fi
# Set user to a non-superuser to ensure we test permissions # Set user to a non-superuser to ensure we test permissions
sed -i 's/username: root/username: gitlab/g' config/database.yml sed -i 's/username: root/username: gitlab/g' config/database.yml
if [ "$GITLAB_DATABASE" = 'postgresql' ]; then sed -i 's/localhost/postgres/g' config/database.yml
sed -i 's/localhost/postgres/g' config/database.yml sed -i 's/username: git/username: postgres/g' config/database.yml
sed -i 's/username: git/username: postgres/g' config/database.yml
if [ -f config/database_geo.yml ]; then
sed -i 's/localhost/postgres/g' config/database_geo.yml
sed -i 's/username: git/username: postgres/g' config/database_geo.yml
fi
else # Assume it's mysql
sed -i 's/localhost/mysql/g' config/database.yml
if [ -f config/database_geo.yml ]; then if [ -f config/database_geo.yml ]; then
sed -i 's/localhost/mysql/g' config/database_geo.yml sed -i 's/localhost/postgres/g' config/database_geo.yml
fi sed -i 's/username: git/username: postgres/g' config/database_geo.yml
fi fi
cp config/resque.yml.example config/resque.yml cp config/resque.yml.example config/resque.yml
...@@ -63,6 +47,6 @@ sed -i 's|url:.*$|url: redis://redis:6379/12|g' config/redis.shared_state.yml ...@@ -63,6 +47,6 @@ sed -i 's|url:.*$|url: redis://redis:6379/12|g' config/redis.shared_state.yml
if [ "$SETUP_DB" != "false" ]; then if [ "$SETUP_DB" != "false" ]; then
setup_db setup_db
elif getent hosts postgres || getent hosts mysql; then elif getent hosts postgres; then
setup_db_user_only setup_db_user_only
fi fi
[[ "$TRACE" ]] && set -x [[ "$TRACE" ]] && set -x
export TILLER_NAMESPACE="$KUBE_NAMESPACE"
function deploy_exists() { function deploy_exists() {
local namespace="${1}" local namespace="${1}"
...@@ -13,18 +14,16 @@ function deploy_exists() { ...@@ -13,18 +14,16 @@ function deploy_exists() {
} }
function previous_deploy_failed() { function previous_deploy_failed() {
local namespace="${1}" local deploy="${1}"
local deploy="${2}"
echoinfo "Checking for previous deployment of ${deploy}" true echoinfo "Checking for previous deployment of ${deploy}" true
helm status --tiller-namespace "${namespace}" "${deploy}" >/dev/null 2>&1 helm status "${deploy}" >/dev/null 2>&1
local status=$? local status=$?
# if `status` is `0`, deployment exists, has a status # if `status` is `0`, deployment exists, has a status
if [ $status -eq 0 ]; then if [ $status -eq 0 ]; then
echoinfo "Previous deployment found, checking status..." echoinfo "Previous deployment found, checking status..."
deployment_status=$(helm status --tiller-namespace "${namespace}" "${deploy}" | grep ^STATUS | cut -d' ' -f2) deployment_status=$(helm status "${deploy}" | grep ^STATUS | cut -d' ' -f2)
echoinfo "Previous deployment state: ${deployment_status}" echoinfo "Previous deployment state: ${deployment_status}"
if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then if [[ "$deployment_status" == "FAILED" || "$deployment_status" == "PENDING_UPGRADE" || "$deployment_status" == "PENDING_INSTALL" ]]; then
status=0; status=0;
...@@ -38,17 +37,16 @@ function previous_deploy_failed() { ...@@ -38,17 +37,16 @@ function previous_deploy_failed() {
} }
function delete_release() { function delete_release() {
local namespace="${KUBE_NAMESPACE}" if [ -z "$CI_ENVIRONMENT_SLUG" ]; then
local deploy="${CI_ENVIRONMENT_SLUG}"
if [ -z "$deploy" ]; then
echoerr "No release given, aborting the delete!" echoerr "No release given, aborting the delete!"
return return
fi fi
echoinfo "Deleting release '$deploy'..." true local name="$CI_ENVIRONMENT_SLUG"
echoinfo "Deleting release '$name'..." true
helm delete --purge --tiller-namespace "${namespace}" "${deploy}" helm delete --purge "$name"
} }
function delete_failed_release() { function delete_failed_release() {
...@@ -61,7 +59,7 @@ function delete_failed_release() { ...@@ -61,7 +59,7 @@ function delete_failed_release() {
echoinfo "No Review App with ${CI_ENVIRONMENT_SLUG} is currently deployed." echoinfo "No Review App with ${CI_ENVIRONMENT_SLUG} is currently deployed."
else else
# Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade` # Cleanup and previous installs, as FAILED and PENDING_UPGRADE will cause errors with `upgrade`
if previous_deploy_failed "${KUBE_NAMESPACE}" "$CI_ENVIRONMENT_SLUG" ; then if previous_deploy_failed "$CI_ENVIRONMENT_SLUG" ; then
echoinfo "Review App deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG" echoinfo "Review App deployment in bad state, cleaning up $CI_ENVIRONMENT_SLUG"
delete_release delete_release
else else
...@@ -119,7 +117,6 @@ function ensure_namespace() { ...@@ -119,7 +117,6 @@ function ensure_namespace() {
} }
function install_tiller() { function install_tiller() {
local TILLER_NAMESPACE="$KUBE_NAMESPACE"
echoinfo "Checking deployment/tiller-deploy status in the ${TILLER_NAMESPACE} namespace..." true echoinfo "Checking deployment/tiller-deploy status in the ${TILLER_NAMESPACE} namespace..." true
echoinfo "Initiating the Helm client..." echoinfo "Initiating the Helm client..."
...@@ -134,12 +131,11 @@ function install_tiller() { ...@@ -134,12 +131,11 @@ function install_tiller() {
--override "spec.template.spec.tolerations[0].key"="dedicated" \ --override "spec.template.spec.tolerations[0].key"="dedicated" \
--override "spec.template.spec.tolerations[0].operator"="Equal" \ --override "spec.template.spec.tolerations[0].operator"="Equal" \
--override "spec.template.spec.tolerations[0].value"="helm" \ --override "spec.template.spec.tolerations[0].value"="helm" \
--override "spec.template.spec.tolerations[0].effect"="NoSchedule" \ --override "spec.template.spec.tolerations[0].effect"="NoSchedule"
--tiller-namespace "${TILLER_NAMESPACE}"
kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy" kubectl rollout status -n "$TILLER_NAMESPACE" -w "deployment/tiller-deploy"
if ! helm version --debug --tiller-namespace "${TILLER_NAMESPACE}"; then if ! helm version --debug; then
echo "Failed to init Tiller." echo "Failed to init Tiller."
return 1 return 1
fi fi
...@@ -151,7 +147,7 @@ function install_external_dns() { ...@@ -151,7 +147,7 @@ function install_external_dns() {
domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}') domain=$(echo "${REVIEW_APPS_DOMAIN}" | awk -F. '{printf "%s.%s", $(NF-1), $NF}')
echoinfo "Installing external DNS for domain ${domain}..." true echoinfo "Installing external DNS for domain ${domain}..." true
if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${KUBE_NAMESPACE}" "${release_name}" ; then if ! deploy_exists "${KUBE_NAMESPACE}" "${release_name}" || previous_deploy_failed "${release_name}" ; then
echoinfo "Installing external-dns Helm chart" echoinfo "Installing external-dns Helm chart"
helm repo update helm repo update
# Default requested: CPU => 0, memory => 0 # Default requested: CPU => 0, memory => 0
......
...@@ -14,11 +14,7 @@ function retry() { ...@@ -14,11 +14,7 @@ function retry() {
} }
function setup_db_user_only() { function setup_db_user_only() {
if [ "$GITLAB_DATABASE" = "postgresql" ]; then source scripts/create_postgres_user.sh
source scripts/create_postgres_user.sh
else
source scripts/create_mysql_user.sh
fi
} }
function setup_db() { function setup_db() {
...@@ -26,10 +22,6 @@ function setup_db() { ...@@ -26,10 +22,6 @@ function setup_db() {
bundle exec rake db:drop db:create db:schema:load db:migrate bundle exec rake db:drop db:create db:schema:load db:migrate
if [ "$GITLAB_DATABASE" = "mysql" ]; then
bundle exec rake add_limits_mysql
fi
bundle exec rake gitlab:db:setup_ee bundle exec rake gitlab:db:setup_ee
} }
......
...@@ -48,15 +48,22 @@ describe Projects::ErrorTrackingController do ...@@ -48,15 +48,22 @@ describe Projects::ErrorTrackingController do
describe 'format json' do describe 'format json' do
let(:list_issues_service) { spy(:list_issues_service) } let(:list_issues_service) { spy(:list_issues_service) }
let(:external_url) { 'http://example.com' } let(:external_url) { 'http://example.com' }
let(:search_term) do
before do ActionController::Parameters.new(
expect(ErrorTracking::ListIssuesService) search_term: 'something'
.to receive(:new).with(project, user) ).permit!
.and_return(list_issues_service)
end end
context 'no data' do context 'no data' do
let(:search_term) do
ActionController::Parameters.new({}).permit!
end
before do before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, search_term)
.and_return(list_issues_service)
expect(list_issues_service).to receive(:execute) expect(list_issues_service).to receive(:execute)
.and_return(status: :error, http_status: :no_content) .and_return(status: :error, http_status: :no_content)
end end
...@@ -68,59 +75,95 @@ describe Projects::ErrorTrackingController do ...@@ -68,59 +75,95 @@ describe Projects::ErrorTrackingController do
end end
end end
context 'service result is successful' do context 'with a search_term param' do
before do before do
expect(list_issues_service).to receive(:execute) expect(ErrorTracking::ListIssuesService)
.and_return(status: :success, issues: [error]) .to receive(:new).with(project, user, search_term)
expect(list_issues_service).to receive(:external_url) .and_return(list_issues_service)
.and_return(external_url)
end end
let(:error) { build(:error_tracking_error) } context 'service result is successful' do
before do
expect(list_issues_service).to receive(:execute)
.and_return(status: :success, issues: [error])
expect(list_issues_service).to receive(:external_url)
.and_return(external_url)
end
it 'returns a list of errors' do let(:error) { build(:error_tracking_error) }
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(:ok) it 'returns a list of errors' do
expect(response).to match_response_schema('error_tracking/index') get :index, params: project_params(format: :json, search_term: 'something')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json) expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('error_tracking/index')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json)
end
end end
end end
context 'service result is erroneous' do context 'without a search_term param' do
let(:error_message) { 'error message' } before do
expect(ErrorTracking::ListIssuesService)
.to receive(:new).with(project, user, {})
.and_return(list_issues_service)
end
context 'without http_status' do context 'service result is successful' do
before do before do
expect(list_issues_service).to receive(:execute) expect(list_issues_service).to receive(:execute)
.and_return(status: :error, message: error_message) .and_return(status: :success, issues: [error])
expect(list_issues_service).to receive(:external_url)
.and_return(external_url)
end end
it 'returns 400 with message' do let(:error) { build(:error_tracking_error) }
it 'returns a list of errors' do
get :index, params: project_params(format: :json) get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:ok)
expect(json_response['message']).to eq(error_message) expect(response).to match_response_schema('error_tracking/index')
expect(json_response['external_url']).to eq(external_url)
expect(json_response['errors']).to eq([error].as_json)
end end
end end
context 'with explicit http_status' do context 'service result is erroneous' do
let(:http_status) { :no_content } let(:error_message) { 'error message' }
before do context 'without http_status' do
expect(list_issues_service).to receive(:execute).and_return( before do
status: :error, expect(list_issues_service).to receive(:execute)
message: error_message, .and_return(status: :error, message: error_message)
http_status: http_status end
)
it 'returns 400 with message' do
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']).to eq(error_message)
end
end end
it 'returns http_status with message' do context 'with explicit http_status' do
get :index, params: project_params(format: :json) let(:http_status) { :no_content }
expect(response).to have_gitlab_http_status(http_status) before do
expect(json_response['message']).to eq(error_message) expect(list_issues_service).to receive(:execute).and_return(
status: :error,
message: error_message,
http_status: http_status
)
end
it 'returns http_status with message' do
get :index, params: project_params(format: :json)
expect(response).to have_gitlab_http_status(http_status)
expect(json_response['message']).to eq(error_message)
end
end end
end end
end end
......
...@@ -7209,15 +7209,15 @@ ...@@ -7209,15 +7209,15 @@
} }
], ],
"project_feature": { "project_feature": {
"builds_access_level": 0, "builds_access_level": 10,
"created_at": "2014-12-26T09:26:45.000Z", "created_at": "2014-12-26T09:26:45.000Z",
"id": 2, "id": 2,
"issues_access_level": 0, "issues_access_level": 10,
"merge_requests_access_level": 20, "merge_requests_access_level": 10,
"project_id": 4, "project_id": 4,
"snippets_access_level": 20, "snippets_access_level": 10,
"updated_at": "2016-09-23T11:58:28.000Z", "updated_at": "2016-09-23T11:58:28.000Z",
"wiki_access_level": 20 "wiki_access_level": 10
}, },
"custom_attributes": [ "custom_attributes": [
{ {
...@@ -7257,6 +7257,9 @@ ...@@ -7257,6 +7257,9 @@
"image_url": "http://www.example.com" "image_url": "http://www.example.com"
} }
], ],
"ci_cd_settings": {
"group_runners_enabled": false
},
"boards": [ "boards": [
{ {
"id": 29, "id": 29,
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue'; import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
import { GlButton, GlEmptyState, GlLoadingIcon, GlTable, GlLink } from '@gitlab/ui'; import {
GlButton,
GlEmptyState,
GlLoadingIcon,
GlTable,
GlLink,
GlSearchBoxByClick,
} from '@gitlab/ui';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -34,8 +41,8 @@ describe('ErrorTrackingList', () => { ...@@ -34,8 +41,8 @@ describe('ErrorTrackingList', () => {
beforeEach(() => { beforeEach(() => {
actions = { actions = {
getSentryData: () => {}, getErrorList: () => {},
startPolling: () => {}, startPolling: jest.fn(),
restartPolling: jest.fn().mockName('restartPolling'), restartPolling: jest.fn().mockName('restartPolling'),
}; };
...@@ -63,13 +70,13 @@ describe('ErrorTrackingList', () => { ...@@ -63,13 +70,13 @@ describe('ErrorTrackingList', () => {
describe('loading', () => { describe('loading', () => {
beforeEach(() => { beforeEach(() => {
store.state.list.loading = true;
mountComponent(); mountComponent();
}); });
it('shows spinner', () => { it('shows spinner', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy(); expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy();
expect(wrapper.find(GlTable).exists()).toBeFalsy(); expect(wrapper.find(GlTable).exists()).toBeFalsy();
expect(wrapper.find(GlButton).exists()).toBeFalsy();
}); });
}); });
...@@ -85,6 +92,20 @@ describe('ErrorTrackingList', () => { ...@@ -85,6 +92,20 @@ describe('ErrorTrackingList', () => {
expect(wrapper.find(GlTable).exists()).toBeTruthy(); expect(wrapper.find(GlTable).exists()).toBeTruthy();
expect(wrapper.find(GlButton).exists()).toBeTruthy(); expect(wrapper.find(GlButton).exists()).toBeTruthy();
}); });
describe('filtering', () => {
it('shows search box', () => {
expect(wrapper.find(GlSearchBoxByClick).exists()).toBeTruthy();
});
it('makes network request on submit', () => {
expect(actions.startPolling).toHaveBeenCalledTimes(1);
wrapper.find(GlSearchBoxByClick).vm.$emit('submit');
expect(actions.startPolling).toHaveBeenCalledTimes(2);
});
});
}); });
describe('no results', () => { describe('no results', () => {
......
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import * as actions from '~/error_tracking/store/list/actions';
import * as types from '~/error_tracking/store/list/mutation_types';
describe('error tracking actions', () => {
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
describe('startPolling', () => {
it('commits SET_LOADING', () => {
mock.onGet().reply(200);
const endpoint = '/errors';
const commit = jest.fn();
const state = {};
actions.startPolling({ commit, state }, endpoint);
expect(commit).toHaveBeenCalledWith(types.SET_LOADING, true);
});
});
});
import * as getters from '~/error_tracking/store/list/getters';
describe('Error Tracking getters', () => {
let state;
const mockErrors = [
{ title: 'ActiveModel::MissingAttributeError: missing attribute: encrypted_password' },
{ title: 'Grape::Exceptions::MethodNotAllowed: Grape::Exceptions::MethodNotAllowed' },
{ title: 'NoMethodError: undefined method `sanitize_http_headers=' },
{ title: 'NoMethodError: undefined method `pry' },
];
beforeEach(() => {
state = {
errors: mockErrors,
};
});
describe('search results', () => {
it('should return errors filtered by words in title matching the query', () => {
const filteredErrors = getters.filterErrorsByTitle(state)('NoMethod');
expect(filteredErrors).not.toContainEqual(mockErrors[0]);
expect(filteredErrors.length).toBe(2);
});
it('should not return results if there is no matching query', () => {
const filteredErrors = getters.filterErrorsByTitle(state)('GitLab');
expect(filteredErrors.length).toBe(0);
});
});
});
...@@ -5,7 +5,7 @@ import headerComponent from '~/vue_shared/components/markdown/header.vue'; ...@@ -5,7 +5,7 @@ import headerComponent from '~/vue_shared/components/markdown/header.vue';
describe('Markdown field header component', () => { describe('Markdown field header component', () => {
let vm; let vm;
beforeEach(done => { beforeEach(() => {
const Component = Vue.extend(headerComponent); const Component = Vue.extend(headerComponent);
vm = new Component({ vm = new Component({
...@@ -13,8 +13,6 @@ describe('Markdown field header component', () => { ...@@ -13,8 +13,6 @@ describe('Markdown field header component', () => {
previewMarkdown: false, previewMarkdown: false,
}, },
}).$mount(); }).$mount();
Vue.nextTick(done);
}); });
it('renders markdown header buttons', () => { it('renders markdown header buttons', () => {
...@@ -42,13 +40,11 @@ describe('Markdown field header component', () => { ...@@ -42,13 +40,11 @@ describe('Markdown field header component', () => {
expect(vm.$el.querySelector('li:nth-child(1)').classList.contains('active')).toBeTruthy(); expect(vm.$el.querySelector('li:nth-child(1)').classList.contains('active')).toBeTruthy();
}); });
it('renders `preview` link as active when previewMarkdown is true', done => { it('renders `preview` link as active when previewMarkdown is true', () => {
vm.previewMarkdown = true; vm.previewMarkdown = true;
Vue.nextTick(() => { Vue.nextTick(() => {
expect(vm.$el.querySelector('li:nth-child(2)').classList.contains('active')).toBeTruthy(); expect(vm.$el.querySelector('li:nth-child(2)').classList.contains('active')).toBeTruthy();
done();
}); });
}); });
......
...@@ -64,12 +64,10 @@ describe('Suggestion Diff component', () => { ...@@ -64,12 +64,10 @@ describe('Suggestion Diff component', () => {
}); });
describe('when apply suggestion is clicked', () => { describe('when apply suggestion is clicked', () => {
beforeEach(done => { beforeEach(() => {
createComponent(); createComponent();
findApplyButton().vm.$emit('click'); findApplyButton().vm.$emit('click');
wrapper.vm.$nextTick(done);
}); });
it('emits apply', () => { it('emits apply', () => {
...@@ -88,19 +86,15 @@ describe('Suggestion Diff component', () => { ...@@ -88,19 +86,15 @@ describe('Suggestion Diff component', () => {
expect(wrapper.text()).toContain('Applying suggestion'); expect(wrapper.text()).toContain('Applying suggestion');
}); });
it('when callback of apply is called, hides loading', done => { it('when callback of apply is called, hides loading', () => {
const [callback] = wrapper.emitted().apply[0]; const [callback] = wrapper.emitted().apply[0];
callback(); callback();
wrapper.vm return wrapper.vm.$nextTick().then(() => {
.$nextTick() expect(findApplyButton().exists()).toBe(true);
.then(() => { expect(findLoading().exists()).toBe(false);
expect(findApplyButton().exists()).toBe(true); });
expect(findLoading().exists()).toBe(false);
})
.then(done)
.catch(done.fail);
}); });
}); });
}); });
...@@ -48,11 +48,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do ...@@ -48,11 +48,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
it 'restore correct project features' do it 'restore correct project features' do
project = Project.find_by_path('project') project = Project.find_by_path('project')
expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED) expect(project.project_feature.issues_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.builds_access_level).to eq(ProjectFeature::ENABLED) expect(project.project_feature.builds_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::ENABLED) expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::ENABLED) expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::PRIVATE)
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED) expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::PRIVATE)
end end
it 'has the project description' do it 'has the project description' do
...@@ -220,6 +220,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do ...@@ -220,6 +220,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee') expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee')
end end
it 'restores `ci_cd_settings` : `group_runners_enabled` setting' do
expect(@project.ci_cd_settings.group_runners_enabled?).to eq(false)
end
it 'restores the correct service' do it 'restores the correct service' do
expect(CustomIssueTrackerService.first).not_to be_nil expect(CustomIssueTrackerService.first).not_to be_nil
end end
......
...@@ -88,12 +88,13 @@ describe Sentry::Client do ...@@ -88,12 +88,13 @@ describe Sentry::Client do
describe '#list_issues' do describe '#list_issues' do
let(:issue_status) { 'unresolved' } let(:issue_status) { 'unresolved' }
let(:limit) { 20 } let(:limit) { 20 }
let(:search_term) { '' }
let(:sentry_api_response) { issues_sample_response } let(:sentry_api_response) { issues_sample_response }
let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' } let(:sentry_request_url) { sentry_url + '/issues/?limit=20&query=is:unresolved' }
let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) } let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
subject { client.list_issues(issue_status: issue_status, limit: limit) } subject { client.list_issues(issue_status: issue_status, limit: limit, search_term: search_term) }
it_behaves_like 'calls sentry api' it_behaves_like 'calls sentry api'
...@@ -202,6 +203,16 @@ describe Sentry::Client do ...@@ -202,6 +203,16 @@ describe Sentry::Client do
end end
it_behaves_like 'maps exceptions' it_behaves_like 'maps exceptions'
context 'when search term is present' do
let(:search_term) { 'NoMethodError'}
let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
it_behaves_like 'calls sentry api'
it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Error
it_behaves_like 'has correct length', 1
end
end end
describe '#list_projects' do describe '#list_projects' do
......
...@@ -5,6 +5,14 @@ require 'spec_helper' ...@@ -5,6 +5,14 @@ require 'spec_helper'
describe ErrorTracking::ListIssuesService do describe ErrorTracking::ListIssuesService do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:project) { create(:project) } set(:project) { create(:project) }
let(:params) { { search_term: 'something' } }
let(:list_sentry_issues_args) do
{
issue_status: 'unresolved',
limit: 20,
search_term: params[:search_term]
}
end
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' } let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' } let(:token) { 'test-token' }
...@@ -14,7 +22,7 @@ describe ErrorTracking::ListIssuesService do ...@@ -14,7 +22,7 @@ describe ErrorTracking::ListIssuesService do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project) create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end end
subject { described_class.new(project, user) } subject { described_class.new(project, user, params) }
before do before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting) expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
...@@ -29,7 +37,9 @@ describe ErrorTracking::ListIssuesService do ...@@ -29,7 +37,9 @@ describe ErrorTracking::ListIssuesService do
before do before do
expect(error_tracking_setting) expect(error_tracking_setting)
.to receive(:list_sentry_issues).and_return(issues: issues) .to receive(:list_sentry_issues)
.with(list_sentry_issues_args)
.and_return(issues: issues)
end end
it 'returns the issues' do it 'returns the issues' do
......
...@@ -104,14 +104,6 @@ describe MergeRequests::MergeService do ...@@ -104,14 +104,6 @@ describe MergeRequests::MergeService do
.to change { merge_request.merge_error } .to change { merge_request.merge_error }
.from(nil).to(merge_error) .from(nil).to(merge_error)
end end
it 'merges the MR when the feature is disabled' do
stub_feature_flags(validate_merge_sha: false)
service.execute(merge_request)
expect(merge_request).to be_merged
end
end end
context 'closes related issues' do context 'closes related issues' do
......
...@@ -18,7 +18,6 @@ rspec_profiling_is_configured = ...@@ -18,7 +18,6 @@ rspec_profiling_is_configured =
ENV['RSPEC_PROFILING_POSTGRES_URL'].present? || ENV['RSPEC_PROFILING_POSTGRES_URL'].present? ||
ENV['RSPEC_PROFILING'] ENV['RSPEC_PROFILING']
branch_can_be_profiled = branch_can_be_profiled =
ENV['GITLAB_DATABASE'] == 'postgresql' &&
(ENV['CI_COMMIT_REF_NAME'] == 'master' || (ENV['CI_COMMIT_REF_NAME'] == 'master' ||
ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/) ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/)
......
# frozen_string_literal: true # frozen_string_literal: true
require 'database_cleaner/active_record/deletion'
require_relative 'db_cleaner' require_relative 'db_cleaner'
module FakeInformationSchema
# Work around a bug in DatabaseCleaner when using the deletion strategy:
# https://github.com/DatabaseCleaner/database_cleaner/issues/347
#
# On MySQL, if the information schema is said to exist, we use an inaccurate
# row count leading to some tables not being cleaned when they should
def information_schema_exists?(_connection)
false
end
end
DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
RSpec.configure do |config| RSpec.configure do |config|
include DbCleaner include DbCleaner
......
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